display Hourglass when application is busy

前端 未结 9 1669
说谎
说谎 2020-11-28 03:01

For a view constructed using WPF, I want to change the mouse cursor to a hourglass when the application is busy and nonresponsive.

One solution is to add



        
9条回答
  •  日久生厌
    2020-11-28 03:38

    I personnaly prefer to not see to mouse pointer switching many times from hourglass to arrow. To help prevent that behavior while calling embedded functions that take a while and each try to control the mouse pointer, I use a stack (counter) that I call a LifeTrackerStack. And only when the stack is empty (counter to 0) that I set back the hour glass to an arrow.

    I also use MVVM. I also prefer thread safe code.

    In my root class of model I declare my LifeTrackerStack that I either populate in childs model classes or use directly from child model classes when I have access to it from them.

    My life tracker have 2 states/actions:

    • Alive (counter > 0) => turn Model.IsBusy to true;
    • Done (counter == 0) => turn Model.IsBusy to false;

    Then in my view, I manually bind to my Model.IsBusy and do:

    void ModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "IsBusy")
        {
            if (this._modelViewAnalysis.IsBusy)
            {
                if (Application.Current.Dispatcher.CheckAccess())
                {
                    this.Cursor = Cursors.Wait;
                }
                else
                {
                    Application.Current.Dispatcher.Invoke(new Action(() => this.Cursor = Cursors.Wait));
                }
            }
            else
            {
                Application.Current.Dispatcher.Invoke(new Action(() => this.Cursor = null));
            }
        }
    

    This is my class LifeTrackerStack:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace HQ.Util.General
    {
        /// 
        /// Usage is to have only one event for a recursive call on many objects
        /// 
        public class LifeTrackerStack
        {
            // ******************************************************************
            protected readonly Action _stackCreationAction;
            protected readonly Action _stackDisposeAction;
            private int _refCount = 0;
            private object _objLock = new object();
            // ******************************************************************
            public LifeTrackerStack(Action stackCreationAction = null, Action stackDisposeAction = null)
            {
                _stackCreationAction = stackCreationAction;
                _stackDisposeAction = stackDisposeAction;
            }
    
            // ******************************************************************
            /// 
            /// Return a new LifeTracker to be used in a 'using' block in order to ensure reliability
            /// 
            /// 
            public LifeTracker GetNewLifeTracker()
            {
                LifeTracker lifeTracker = new LifeTracker(AddRef, RemoveRef);
    
                return lifeTracker;
            }
    
            // ******************************************************************
            public int Count
            {
                get { return _refCount; }
            }
    
            // ******************************************************************
            public void Reset()
            {
                lock (_objLock)
                {
                    _refCount = 0;
                    if (_stackDisposeAction != null)
                    {
                        _stackDisposeAction();
                    }
                }
            }
    
            // ******************************************************************
            private void AddRef()
            {
                lock (_objLock)
                {
                    if (_refCount == 0)
                    {
                        if (_stackCreationAction != null)
                        {
                            _stackCreationAction();
                        }
                    }
                    _refCount++;
                }
            }
    
            // ******************************************************************
            private void RemoveRef()
            {
                bool shouldDispose = false;
                lock (_objLock)
                {
                    if (_refCount > 0)
                    {
                        _refCount--;
                    }
    
                    if (_refCount == 0)
                    {
                        if (_stackDisposeAction != null)
                        {
                            _stackDisposeAction();
                        }
                    }
                }
            }
    
            // ******************************************************************
        }
    }
    
    
    using System;
    
    namespace HQ.Util.General
    {
        public delegate void ActionDelegate();
    
        public class LifeTracker : IDisposable
        {
            private readonly ActionDelegate _actionDispose;
            public LifeTracker(ActionDelegate actionCreation, ActionDelegate actionDispose)
            {
                _actionDispose = actionDispose;
    
                if (actionCreation != null)
                    actionCreation();
            }
    
            private bool _disposed = false;
            public void Dispose()
            {
                Dispose(true);
                // This object will be cleaned up by the Dispose method.
                // Therefore, you should call GC.SupressFinalize to
                // take this object off the finalization queue
                // and prevent finalization code for this object
                // from executing a second time.
                GC.SuppressFinalize(this);
            }
    
            protected virtual void Dispose(bool disposing)
            {
                // Check to see if Dispose has already been called.
                if (!this._disposed)
                {
                    // If disposing equals true, dispose all managed
                    // and unmanaged resources.
                    if (disposing)
                    {
                        _actionDispose();
                    }
    
                    // Note disposing has been done.
                    _disposed = true;
                }
            }
        }
    }
    

    And the usage of it:

        _busyStackLifeTracker = new LifeTrackerStack
            (
                () =>
                {
                    this.IsBusy = true;
                },
                () =>
                {
                    this.IsBusy = false;
                }
            );
    

    Everywhere I have lengthy jog, I do:

            using (this.BusyStackLifeTracker.GetNewLifeTracker())
            {
                // long job
            }
    

    It works for me. Hope it could help any! Eric

提交回复
热议问题