wpf MVVM 等待光标如何在调用命令期间设置 .wait 光标?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10118788/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:22:24  来源:igfitidea点击:

MVVM Wait Cursor how to set the.wait cursor during invocation of a command?

wpfmvvmcursorwait

提问by user1328350

Scenario: User clicks a button on the View This invokes a command on the ViewModel, DoProcessing How, and where does the Wait cursor get set, considering the responsibilitues of View and ViewModel?

场景:用户单击视图上的按钮,考虑到视图和视图模型的职责,这会调用视图模型上的命令、DoProcessing 如何以及等待光标在哪里设置?

Just to be clear, I am just looking to change the DEFAULT cursor to an hourglass while the command is running. When the command completes, the cursor mut change back to an arrow. (It is a synchronous operation I am looking for, and I want the UI to block).

为了清楚起见,我只是想在命令运行时将 DEFAULT 光标更改为沙漏。命令完成后,光标 mut 变回箭头。(这是我正在寻找的同步操作,我希望 UI 被阻止)。

I have created an IsBusy property on the ViewModel. How do I ensure that the Application's mouse pointer changes?

我在 ViewModel 上创建了一个 IsBusy 属性。如何确保Application的鼠标指针发生变化?

回答by Shakti Prakash Singh

I am using it successfully in my application:

我在我的应用程序中成功使用它:

/// <summary>
///   Contains helper methods for UI, so far just one for showing a waitcursor
/// </summary>
public static class UIServices
{
    /// <summary>
    ///   A value indicating whether the UI is currently busy
    /// </summary>
    private static bool IsBusy;

    /// <summary>
    /// Sets the busystate as busy.
    /// </summary>
    public static void SetBusyState()
    {
        SetBusyState(true);
    }

    /// <summary>
    /// Sets the busystate to busy or not busy.
    /// </summary>
    /// <param name="busy">if set to <c>true</c> the application is now busy.</param>
    private static void SetBusyState(bool busy)
    {
        if (busy != IsBusy)
        {
            IsBusy = busy;
            Mouse.OverrideCursor = busy ? Cursors.Wait : null;

            if (IsBusy)
            {
                new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, System.Windows.Application.Current.Dispatcher);
            }
        }
    }

    /// <summary>
    /// Handles the Tick event of the dispatcherTimer control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    private static void dispatcherTimer_Tick(object sender, EventArgs e)
    {
        var dispatcherTimer = sender as DispatcherTimer;
        if (dispatcherTimer != null)
        {
            SetBusyState(false);
            dispatcherTimer.Stop();
        }
    }
}

This has been taken from here. Courtsey huttelihut.

这是从这里取的。Courtsey httelihut

You need to call the SetBusyStatemethod every time you think you are going to perform any time consuming operation. e.g.

SetBusyState每次您认为要执行任何耗时的操作时,都需要调用该方法。例如

...
UIServices.SetBusyState();
DoProcessing();
...

This will automatically change your cursor to wait cursor when the application is busy and back to normal when idle.

这将在应用程序繁忙时自动将光标更改为等待光标,并在空闲时恢复正常。

回答by bradcarman

A very simple method is to simply bind to the 'Cursor' property of the window (or any other control). For example:

一个非常简单的方法是简单地绑定到窗口(或任何其他控件)的“光标”属性。例如:

XAML:

XAML:

<Window
    x:Class="Example.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     Cursor="{Binding Cursor}" />

ViewModel Cursor Property (Using Apex.MVVM):

ViewModel 光标属性(使用 Apex.MVVM):

    private NotifyingProperty cursor = new NotifyingProperty("Cursor", typeof(System.Windows.Input.Cursor), System.Windows.Input.Cursors.Arrow);
    public System.Windows.Input.Cursor Cursor
    {
        get { return (System.Windows.Input.Cursor)GetValue(cursor); }
        set { SetValue(cursor, value); }
    }

Then simply change the cursor in your view when needed...

然后只需在需要时更改视图中的光标...

    public void DoSomethingLongCommand()
    {
        Cursor = System.Windows.Input.Cursors.Wait;

        ... some long process ...

        Cursor = System.Windows.Input.Cursors.Arrow;
    }

回答by v00d00

Command is handled on the view model, so the reasonable decission would be to do folowing:

命令在视图模型上处理,因此合理的决定是执行以下操作:

1) Create a busy indicator service and inject it into the view model (this will allow you to replace the cursor logic with some nasty animation easily)

1) 创建一个繁忙的指标服务并将其注入视图模型(这将允许您轻松地用一些讨厌的动画替换光标逻辑)

2) In the command handler call the busy indicator service to notify the user

2) 在命令处理程序中调用忙指示服务通知用户

I might be wrong, but it looks like you are trying to do some heavy calculations or I/O on UI thread. I highly recommend you to perform work on thread pool in this case. You can use Task and TaskFactory to easily wrap work with ThreadPool

我可能是错的,但看起来您正在尝试在 UI 线程上进行一些繁重的计算或 I/O。在这种情况下,我强烈建议您在线程池上执行工作。您可以使用 Task 和 TaskFactory 轻松地使用 ThreadPool 包装工作

回答by Boas Enkler

There is a great Session(at 50:58) by Laurent Bugnion online (Creator of MVVM Light). There's also an deepDive sessionavailable (alternatively here(at 24:47)).

Laurent Bugnion 在线(MVVM Light 的创建者)有一个很棒的Session(50:58 )。还有一个deepDive 会话可用(或者在这里(24:47))。

In at least one of them he live codes a busy Indicator using a is BusyProperty.

至少在其中一个中,他使用 is BusyProperty 对繁忙的指标进行现场编码。

回答by Bruce

The ViewModel should only decide whether it is busy, and the decision about what cursor to use, or whether to use some other technique such as a progress bar should be left up to the View.

ViewModel 应该只决定它是否忙,而决定使用什么光标,或者是否使用其他一些技术(例如进度条)应该留给 View。

And on the other hand, handling it with code-behind in the View is not so desirable either, because the ideal is that Views should not have code-behind.

另一方面,在视图中使用代码隐藏处理它也不是那么可取,因为理想情况是视图不应该有代码隐藏。

Therefore I chose to make a class that can be used in the View XAML to specify that the cursor should be change to Wait when the ViewModel is busy. Using UWP + Prism the class definition is:

因此我选择创建一个可以在View XAML 中使用的类来指定当ViewModel 繁忙时光标应该更改为Wait。使用 UWP + Prism 的类定义是:

public class CursorBusy : FrameworkElement
{
    private static CoreCursor _arrow = new CoreCursor(CoreCursorType.Arrow, 0);
    private static CoreCursor _wait = new CoreCursor(CoreCursorType.Wait, 0);

    public static readonly DependencyProperty IsWaitCursorProperty =
        DependencyProperty.Register(
            "IsWaitCursor",
            typeof(bool),
            typeof(CursorBusy),
            new PropertyMetadata(false, OnIsWaitCursorChanged)
    );

    public bool IsWaitCursor
    {
        get { return (bool)GetValue(IsWaitCursorProperty); } 
        set { SetValue(IsWaitCursorProperty, value); }
    }

    private static void OnIsWaitCursorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        CursorBusy cb = (CursorBusy)d;
        Window.Current.CoreWindow.PointerCursor = (bool)e.NewValue ? _wait : _arrow;
    }
}

And the way to use it is:

使用方法是:

<mvvm:SessionStateAwarePage
    x:Class="Orsa.Views.ImportPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mvvm="using:Prism.Windows.Mvvm"
    xmlns:local="using:Orsa"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mvvm:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid>
    <Grid.RowDefinitions>
    .
    .
    </Grid.RowDefinitions>
    <local:CursorBusy IsWaitCursor="{Binding IsBusy}"/>
    (other UI Elements)
    .
    .
    </Grid>
</mvvm:SessionStateAwarePage>

回答by LawMan

private static void LoadWindow<T>(Window owner) where T : Window, new()
{
    owner.Cursor = Cursors.Wait;
    new T { Owner = owner }.Show();
    owner.Cursor = Cursors.Arrow;
}

回答by Dennis

IMHO that it is perfectly fine for the wait cursor logic to be next to the command in the viewmodel.

恕我直言,等待光标逻辑位于视图模型中的命令旁边是完全没问题的。

As to the best way to do change the cursor, create a IDisposablewrapper that changes the Mouse.OverrideCursorproperty.

至于更改光标的最佳方法,请创建一个IDisposable更改Mouse.OverrideCursor属性的包装器。

public class StackedCursorOverride : IDisposable
{
    private readonly static Stack<Cursor> CursorStack;

    static StackedCursorOverride()
    {
        CursorStack = new Stack<Cursor>();
    }

    public StackedCursorOverride(Cursor cursor)
    {            
        CursorStack.Push(cursor);
        Mouse.OverrideCursor = cursor;            
    }

    public void Dispose()
    {
        var previousCursor = CursorStack.Pop();
        if (CursorStack.Count == 0)
        {
            Mouse.OverrideCursor = null;
            return;
        }

        // if next cursor is the same as the one we just popped, don't change the override
        if ((CursorStack.Count > 0) && (CursorStack.Peek() != previousCursor))
            Mouse.OverrideCursor = CursorStack.Peek();             
    }
}

Usage:

用法:

using (new StackedCursorOverride(Cursors.Wait))
{
     // ...
}

The above is a revised version of the solution that I posted to this question.

以上是我发布到这个问题的解决方案的修订版。