【问题标题】:How to implement busy indicator in application shell如何在应用程序外壳中实现忙碌指示器
【发布时间】:2014-08-08 01:04:39
【问题描述】:

我正在尝试在我的应用程序的“外壳”中实现 WPF 扩展工具包中的繁忙指示器。目标是在一个地方实现指标,然后能够从任何地方设置 IsBusy 属性,以便可以对其进行初始化。这是我的外壳:

<Window x:Class="Foundation.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="clr-namespace:Library.Controls.Views;assembly=Library"
    xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
    WindowStyle="None"
    AllowsTransparency="False"
    Name="ShellView"
    FontFamily="Yu Gothic Light"
    Background="{StaticResource AiWhiteBrush}">

<!--Region Outer Most Grid-->
<xctk:BusyIndicator IsBusy="{Binding IsBusy}">
    <Grid x:Name="OuterGrid">

<!-- CONTENT HERE -->

    </Grid>
</xctk:BusyIndicator>
<!--End Region-->

然后,我的 Shell 的 ViewModel 如下所示:

using CashDrawer.Views;
using Library.BaseClasses;
using Library.StaticClasses;
using Microsoft.Practices.Prism.Commands;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using WpfPageTransitions;

namespace Foundation
{
    public class ShellViewModel : ViewModelBase
    {
        #region constructor(s)

        public ShellViewModel()
        {
            StateManager.IsBusyChange += new StateManager.IsBusyHandler(IsBusyEventAction);
        }

        #endregion constructor(s)

        #region properties

        private bool _IsBusy;
        public bool IsBusy
        {
            get
            {
                return _IsBusy;
            }
            set
            {
                if (_IsBusy != value)
                {
                    _IsBusy = value;
                    OnPropertyChanged("IsBusy");
                }
            }
        }

        #endregion properties

        #region actions, functions, and methods

        private void IsBusyEventAction(object sender, EventArgs e)
        {
            if (StateManager.IsBusy)
            {
                this.IsBusy = true;
            }
            else
            {
                this.IsBusy = false;
            }
        }

        #endregion actions, functions, and methods
    }
}

最后,我创建了一个静态 StateManager 类:

using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using WpfPageTransitions;

namespace Library.StaticClasses
{
    public static class StateManager
    {

        private static bool _IsBusy;
        public static bool IsBusy
        {
            get
            {
                return _IsBusy;
            }
            set
            {
                if (_IsBusy != value)
                {
                    _IsBusy = value;
                    IsBusyChange(null, null);
                }
            }
        }

        public delegate void IsBusyHandler(object sender, EventArgs e);

        public static event IsBusyHandler IsBusyChange;
    }
}

这个想法是,当 StateManager 的 IsBusy 属性发生更改时,它将触发一个事件,该事件将相应地更改 ShellViewModel 中的 IsBusy 属性。逻辑工作正常。但是,忙碌指示器未按预期工作。这是另一个视图模型中的代码 sn-p 切换 IsBusy 属性:

private void SaveCommand_Action()
{
     StateManager.IsBusy = true;

     this.Validate();

     if (!HasValidationErrors)
     {
        if (this.CustomerControlVM.SaveCustomer() != 0)
        {
            VehicleControlVM.VehicleModel.CustomerID = this.CustomerControlVM.CustomerModel.CustomerID;
            this.VehicleControlVM.SaveVehicle();

            ComplaintsView complaintsControl = new ComplaintsView();

            (complaintsControl.DataContext as ComplaintsViewModel).CurrentVehicle = this.VehicleControlVM.VehicleModel;
            (complaintsControl.DataContext as ComplaintsViewModel).CurrentCustomer = this.CustomerControlVM.CustomerModel;
            StateManager.LoadView(complaintsControl, PageTransitionType.SlideLeft);
        }
    }

    StateManager.IsBusy = false;
}

我看到代码中有一些滞后,但我从未看到忙碌指示符出现。我可以删除StateManager.IsBusy = false;,然后忙碌指示符会出现(当然会无限期地显示)。我尝试在 IsBusy 状态更改之间创建更长的延迟,但指示符仍然没有出现。我已经阅读了多篇文章和文章,试图了解可能出了什么问题,但我没有看到任何有用的东西。我知道 IsBusy 指示器正在 UI 线程上发生,但我正在更改 ViewModel 中不应出现在 UI 线程上的 IsBusy 状态。有什么想法或建议吗?

【问题讨论】:

  • 很可能它是您正在调用的操作的优先级,尝试使用调度程序调用状态更改,其优先级如发送,例如Dispatcher.Invoke(() =&gt; StateManager.IsBusy = true, System.Windows.Threading.DispatcherPriority.Send),这将给它时间在执行逻辑之前渲染指标
  • 嗨 sa_ddam213。感谢您的反馈。我试了一下,仍然没有发生。如果我删除 StateManager.IsBusy = false,则逻辑似乎在繁忙指示器出现之前执行。我想你可能正在做某事。还有其他想法吗?
  • 也许尝试不同的DispatcherPriority,甚至可能将逻辑而不是 StateManager.IsBusy 包装在具有背景优先级的 DispatcherInvoke 中,这样指示符将呈现,一旦完成,将调用保存逻辑( make suer IsBusy = false 也在与其他逻辑的调用内部
  • 嗯,看起来相反的工作。我没有提高 StateManager.IsBusy = true 的优先级,而是将逻辑包装在一个方法中并使用相同的语法,但优先级为 ,它的工作方式如下:private void SaveCommand_Action() { StateManager.IsBusy = true; Application.Current.Dispatcher.Invoke(() =&gt; Save(), System.Windows.Threading.DispatcherPriority.Background); StateManager.IsBusy = false; }
  • 哈!我们有同样的想法。我非常感谢您的帮助 sa_ddam213。很好的电话。现在,我只需要干净的方式来实现它,所以我不需要每次调用指标时都这样做。但这是我的问题。非常感谢

标签: c# wpf mvvm wpftoolkit


【解决方案1】:

关于我最后的评论。

您可以更改状态管理器以代替执行操作,因此您传入方法,状态管理器可以设置调用等

public static class StateManager 
{

public static void Process(Action action) {
    IsBusy = true;
    Application.Current.Dispatcher.Invoke(action, System.Windows.Threading.DispatcherPriority.Background);
    IsBusy = false;
}

private static bool _IsBusy;
public static bool IsBusy {
    get {
        return _IsBusy;
    }
    set {
        if (_IsBusy != value) {
            _IsBusy = value;
            IsBusyChange(null, null);
        }
    }
}

public delegate void IsBusyHandler(object sender, EventArgs e);

public static event IsBusyHandler IsBusyChange;

}

那么你可以这样做:

private void SaveCommand_Action()
{
     StateManager.Process(() =>
     {

     this.Validate();

     if (!HasValidationErrors)
     {
        if (this.CustomerControlVM.SaveCustomer() != 0)
        {
            VehicleControlVM.VehicleModel.CustomerID = this.CustomerControlVM.CustomerModel.CustomerID;
            this.VehicleControlVM.SaveVehicle();

            ComplaintsView complaintsControl = new ComplaintsView();

            (complaintsControl.DataContext as ComplaintsViewModel).CurrentVehicle = this.VehicleControlVM.VehicleModel;
            (complaintsControl.DataContext as ComplaintsViewModel).CurrentCustomer = this.CustomerControlVM.CustomerModel;
            StateManager.LoadView(complaintsControl, PageTransitionType.SlideLeft);
        }
    }

    StateManager.IsBusy = false;
});
}

【讨论】:

  • 除此方法外,我还建议将指标直接绑定到StateManagerIsBusy 属性,而不是通过事件连接到另一个VM 属性,除非必要。例如IsBusy="{Binding IsBusy, Source={x:Static l:StateManager.Instance}}",其中l: 指的是命名空间Library.StaticClasses。您可能需要在 StateManager 类上实现单例模式和 INotifyPropertyChanged
【解决方案2】:

感谢 sa_ddam213,我解决了这个问题。问题是当务之急。这段代码负责处理它:

    private void SaveCommand_Action()
    {
        StateManager.IsBusy = true;
        Application.Current.Dispatcher.Invoke(() => Save(), System.Windows.Threading.DispatcherPriority.Background);
    }

    private void Save()
    {
        this.Validate();

        if (!HasValidationErrors)
        {
            if (this.CustomerControlVM.SaveCustomer() != 0)
            {
                VehicleControlVM.VehicleModel.CustomerID = this.CustomerControlVM.CustomerModel.CustomerID;
                this.VehicleControlVM.SaveVehicle();

                ComplaintsView complaintsControl = new ComplaintsView();

                (complaintsControl.DataContext as ComplaintsViewModel).CurrentVehicle = this.VehicleControlVM.VehicleModel;
                (complaintsControl.DataContext as ComplaintsViewModel).CurrentCustomer = this.CustomerControlVM.CustomerModel;
                StateManager.LoadView(complaintsControl, PageTransitionType.SlideLeft);
                StateManager.IsBusy = false;
            }
        }
    }

我还有一些工作要做,所以我不需要在每次 IsBusy 状态更改时都这样做,但是根据我所学到的,我可以弄清楚。非常感谢 sa_ddam213。

【讨论】:

    猜你喜欢
    • 2014-11-18
    • 2015-11-28
    • 2017-05-25
    • 2015-02-24
    • 2011-05-20
    • 2018-11-04
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多