Подтвердить что ты не робот

Как показать waitcursor, когда приложение WPF занято привязкой данных

У меня есть приложение WPF, использующее шаблон MVVM, который иногда должен показывать waitcursor, когда он занят тем, что пользователь должен ждать. Благодаря сочетанию ответов на этой странице: отображает Песочные часы, когда приложение занято, у меня есть решение, которое почти работает (хотя это не действительно MVVM по духу). Всякий раз, когда я делаю что-то много времени в своих моделях просмотра, я делаю это:

using (UiServices.ShowWaitCursor())
{
.. do time-consuming logic
this.SomeData = somedata;
}

(ShowWaitCursor() возвращает IDisposable, который показывает waitcursor, пока он не будет удален) Последняя строка в моем примере - это то, где я устанавливаю некоторое свойство. Это свойство связано в моем XAML, например. например:

<ItemsControl ItemsSource="{Binding SomeData}" /> 

Однако, поскольку это может быть длинный список объектов, а иногда и сложных datatemplates и т.д., фактическая привязка и рендеринг когда-то занимают значительное количество времени. Поскольку это привязка занимает место вне моего оператора using, ожидающий курсор исчезнет до того, как фактическое ожидание закончится для пользователя.

Итак, мой вопрос заключается в том, как сделать waitcursor в приложении MVF MVF, которое учитывает привязку данных?

4b9b3361

Ответ 1

Ответ Isak не помог мне, потому что он не решил проблему того, как действовать, когда фактическое ожидание для пользователя. Я закончил это: каждый раз, когда я начинаю делать что-то временное, я называю вспомогательный метод. Этот вспомогательный метод изменяет курсор, а затем создает DispatcherTimer, который будет вызываться, когда приложение неактивно. Когда он называется, он возвращает mousecursor:

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

Ответ 2

Поэтому мне не нравилось использование OverrideCursor, потому что у меня было несколько окон, и мне нужны те, которые в настоящее время не выполняли что-то, чтобы иметь обычный курсор стрелки.

Вот мое решение:

<Window.Style>
    <Style TargetType="Window">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsBusy}" Value="True">
                <Setter Property="Cursor" Value="Wait" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Style>
<Grid>
    <Grid.Style>
        <Style TargetType="Grid">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsBusy}" Value="True">
                    <Setter Property="IsHitTestVisible" Value="False" /> <!-- Ensures wait cursor is active everywhere in the window -->
                    <Setter Property="IsEnabled" Value="False" /> <!-- Makes everything appear disabled -->
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>
    <!-- Window controls go here -->
</Grid>

Ответ 3

То, что я делал в прошлом, - это определение логических свойств в модели viewmodel, которая указывает, что выполняется длительный расчет. Например, IsBusy, который установлен в true при работе и false при простоя.

Затем в представлении я привязываюсь к этому и показываю индикатор выполнения или счетчик или аналогичный, пока это свойство истинно. Я лично никогда не устанавливал курсор с использованием этого подхода, но я не понимаю, почему это было бы невозможно.

Если вам нужен еще больший контроль, а простого логического значения недостаточно, вы можете использовать VisualStateManager, который вы выберете из своей модели просмотра, При таком подходе вы можете подробно указать, как должен выглядеть пользовательский интерфейс в зависимости от состояния модели.

Ответ 4

В дополнение к вкладу Исака Саво, вы можете посмотреть блог Брайана Китинга для рабочего образца.