MVVM Подождите, как установить курсор .wait при вызове команды?
Сценарий:
Пользователь нажимает кнопку в представлении
Это вызывает команду в ViewModel, DoProcessing
Как и где установлен курсор Wait, учитывая ответственность View и ViewModel?
Чтобы быть ясным, я просто хочу изменить курсор DEFAULT на песочные часы во время выполнения команды. Когда команда завершена, курсор mut изменится на стрелку. (Это синхронная операция, которую я ищу, и я хочу, чтобы пользовательский интерфейс блокировался).
Я создал свойство IsBusy в ViewModel. Как я могу гарантировать, что указатель мыши Application изменится?
Ответы
Ответ 1
Я успешно использую его в своем приложении:
/// <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();
}
}
}
Это было взято из здесь. Courtsey huttelihut.
Вам нужно вызывать метод SetBusyState
каждый раз, когда вы думаете, что собираетесь выполнять какое-либо трудоемкое время. например.
...
UIServices.SetBusyState();
DoProcessing();
...
Это автоматически изменит ваш курсор, чтобы ждать курсора, когда приложение занято и возвращается в нормальное состояние на холостом ходу.
Ответ 2
Очень простой метод - просто привязать свойство "Курсор" окна (или любого другого элемента управления). Например:
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;
}
Ответ 3
Команда обрабатывается на модели представления, поэтому разумное разрешение будет заключаться в следующем:
1) Создайте услугу индикатора занятости и введите его в модель представления (это позволит легко заменить логику курсора некоторой неприятной анимацией)
2) В обработчике команд вызовите службу индикатора занятости, чтобы уведомить пользователя
Возможно, я ошибаюсь, но похоже, что вы пытаетесь выполнить тяжелые вычисления или ввод-вывод в потоке пользовательского интерфейса. В этом случае я настоятельно рекомендую вам выполнить работу с пулом потоков. Вы можете использовать Task и TaskFactory для удобства работы с ThreadPool
Ответ 4
Лоран Бюньон (Создатель MVVM Light) проводит отличную сессию (в 50:58). Также доступна сессия DeepDive (альтернативно здесь (в 24:47)).
По крайней мере, в одном из них он в реальном времени кодирует индикатор занятости с помощью BusyProperty.
Ответ 5
IMHO, что отлично подходит для логики курсора ожидания рядом с командой в модели viewmodel.
Что касается наилучшего способа изменить курсор, создайте обертку 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();
}
}
Применение:
using (new StackedCursorOverride(Cursors.Wait))
{
// ...
}
Вышеописанная версия решения, которое я отправил на этот вопрос .
Ответ 6
private static void LoadWindow<T>(Window owner) where T : Window, new()
{
owner.Cursor = Cursors.Wait;
new T { Owner = owner }.Show();
owner.Cursor = Cursors.Arrow;
}
Ответ 7
ViewModel должен только решить, занят ли он, и решение о том, какой курсор использовать, или использовать какой-либо другой метод, такой как индикатор выполнения, следует оставить на усмотрение View.
А с другой стороны, обработка этого кода с помощью кода в представлении также не очень желательна, потому что в идеале представления не должны иметь код.
Поэтому я решил создать класс, который можно использовать в представлении XAML, чтобы указать, что курсор должен быть изменен на Ожидание, когда ViewModel занята. Используя 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>