web-dev-qa-db-ja.com

MVVM待機カーソルコマンドの呼び出し中に.waitカーソルを設定する方法

シナリオ:ユーザーがビューのボタンをクリックすると、ViewおよびViewModelの役割を考慮して、ViewModel、DoProcessing How、および待機カーソルがどこに設定されるコマンドが呼び出されますか?

明確にするために、コマンドの実行中にDEFAULTカーソルを砂時計に変更しようとしています。コマンドが完了すると、カーソルミュートが矢印に戻ります。 (これは私が探している同期操作であり、UIをブロックして欲しい)。

ViewModelにIsBusyプロパティを作成しました。 Applicationのマウスポインターが確実に変更されるようにするにはどうすればよいですか?

24
user1328350

私のアプリケーションで問題なく使用しています:

/// <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();
        }
    }
}

これは here から取得されました。コートジー huttelihut

時間のかかる操作を実行するたびに、SetBusyStateメソッドを呼び出す必要があります。例えば.

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

これにより、カーソルが自動的に変更され、アプリケーションがビジーの場合は待機カーソルになり、アイドルの場合は通常に戻ります。

30

非常に簡単な方法は、ウィンドウ(またはその他のコントロール)の 'Cursor'プロパティに単にバインドすることです。例えば:

XAML:

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

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); }
    }

次に、必要に応じてビューのカーソルを変更します...

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

        ... some long process ...

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

コマンドはビューモデルで処理されるため、次のことを行うのが妥当です。

1)ビジーインジケーターサービスを作成し、ビューモデルに挿入します(これにより、カーソルロジックを厄介なアニメーションに簡単に置き換えることができます)。

2)コマンドハンドラーでビジーインジケーターサービスを呼び出してユーザーに通知します

私は間違っているかもしれませんが、UIスレッドで重い計算またはI/Oを実行しようとしているようです。この場合、スレッドプールで作業を実行することを強くお勧めします。 TaskとTaskFactoryを使用して、作業をThreadPoolで簡単にラップできます。

2
v00d00

Laurent Bugnionオンライン( MVVM Light の作成者)によるすばらしい Session (at 50:58)があります。 deepDiveセッション も利用できます(または here (24:47))。

それらの少なくとも1つで、彼はis BusyPropertyを使用してビジーインジケーターをライブコーディングします。

1
Boas Enkler

ViewModelは、それがビジーであるかどうか、およびどのカーソルを使用するか、または進行状況バーなどの他の手法を使用するかどうかの決定のみをビューに任せる必要があります。

また、その一方で、ビューでコードビハインドを使用して処理することも、ビューがコードビハインドを持たないことが理想であるため、あまり望ましくありません。

したがって、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;
    }
}

そしてそれを使う方法は:

<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>
1
Bruce
private static void LoadWindow<T>(Window owner) where T : Window, new()
{
    owner.Cursor = Cursors.Wait;
    new T { Owner = owner }.Show();
    owner.Cursor = Cursors.Arrow;
}
0
LawMan

待機カーソルロジックがviewmodelのコマンドの隣にあることはまったく問題ありません。

カーソルを変更する最良の方法については、Mouse.OverrideCursorプロパティを変更するIDisposableラッパーを作成します。

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();             
    }
}

用途:

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

上記は私がこれに投稿したソリューションの改訂版です question

0
Dennis