Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 总线指示器允许触发继电器命令两次_C#_Wpf_Mvvm_Task Parallel Library_Busyindicator - Fatal编程技术网

C# 总线指示器允许触发继电器命令两次

C# 总线指示器允许触发继电器命令两次,c#,wpf,mvvm,task-parallel-library,busyindicator,C#,Wpf,Mvvm,Task Parallel Library,Busyindicator,我有一个WPF应用程序,它遵循MVVM 为了使UI具有响应性,我使用TPL来执行长时间运行的命令,并向用户显示,该应用程序现在很忙 在其中一个视图模型中,我有以下命令: public ICommand RefreshOrdersCommand { get; private set; } public OrdersEditorVM() { this.Orders = new ObservableCollection<OrderVM>(); this.RefreshOr

我有一个WPF应用程序,它遵循MVVM

为了使UI具有响应性,我使用TPL来执行长时间运行的命令,并向用户显示,该应用程序现在很忙

在其中一个视图模型中,我有以下命令:

public ICommand RefreshOrdersCommand { get; private set; }

public OrdersEditorVM()
{
    this.Orders = new ObservableCollection<OrderVM>();
    this.RefreshOrdersCommand = new RelayCommand(HandleRefreshOrders);

    HandleRefreshOrders();
}

private void HandleRefreshOrders()
{
    var task = Task.Factory.StartNew(() => RefreshOrders());
    task.ContinueWith(t => RefreshOrdersCompleted(t.Result), 
        CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
    task.ContinueWith(t => this.LogAggregateException(t.Exception, Resources.OrdersEditorVM_OrdersLoading, Resources.OrdersEditorVM_OrdersLoadingFaulted),
        CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}

private Order[] RefreshOrders()
{
    IsBusy = true;
    System.Diagnostics.Debug.WriteLine("Start refresh.");
    try
    {
        var orders = // building a query with Entity Framework DB Context API

        return orders
            .ToArray();
    }
    finally
    {
        IsBusy = false;
        System.Diagnostics.Debug.WriteLine("Stop refresh.");
    }
}

private void RefreshOrdersCompleted(Order[] orders)
{
    Orders.RelpaceContent(orders.Select(o => new OrderVM(this, o)));

    if (Orders.Count > 0)
    {
        Orders[0].IsSelected = true;
    }
}
…并禁用内容:

                    <VisualState x:Name="Visible">
                       <Storyboard>
                          <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="busycontent" Storyboard.TargetProperty="(UIElement.Visibility)">
                             <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                <DiscreteObjectKeyFrame.Value>
                                   <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                             </DiscreteObjectKeyFrame>
                          </ObjectAnimationUsingKeyFrames>
                          <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="overlay" Storyboard.TargetProperty="(UIElement.Visibility)">
                             <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                <DiscreteObjectKeyFrame.Value>
                                   <Visibility>Visible</Visibility>
                                </DiscreteObjectKeyFrame.Value>
                             </DiscreteObjectKeyFrame>
                          </ObjectAnimationUsingKeyFrames>
                       </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Busy">
                       <Storyboard>
                          <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="content" Storyboard.TargetProperty="(Control.IsEnabled)">
                             <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                <DiscreteObjectKeyFrame.Value>
                                   <sys:Boolean>False</sys:Boolean>
                                </DiscreteObjectKeyFrame.Value>
                             </DiscreteObjectKeyFrame>
                          </ObjectAnimationUsingKeyFrames>
                       </Storyboard>
                    </VisualState>

假的
有什么想法吗

更新。 问题不在于如何防止命令重新进入。我想知道一种可以按两次按钮的机械装置

以下是它的工作原理(从我的观点):

  • ViewModel.IsBusy
    BusyIndicator.IsBusy
    绑定。绑定是同步的
  • BusyIndicator.IsBusy的Setter调用
    VisualStateManager.GoToState
    两次。其中一个调用使一个矩形可见,该矩形与
    BusyIndicator
    的内容重叠(在我的例子中是按钮)
  • 因此,据我所知,在设置了
    ViewModel.IsBusy
    之后,我无法实际实现该按钮,因为所有这些事情都发生在同一(UI)线程上

但是按钮是如何被按下两次的呢?

我不会试图依靠忙指示器来控制有效的程序流。命令执行设置为
IsBusy
,如果
IsBusy
已为
True
,则它本身不应运行

private void HandleRefreshOrders()
{
    if (IsBusy)
        return;
...
您还可以将按钮的
Enabled
状态绑定到
IsBusy
,但我认为核心解决方案是防止您的命令意外再次进入。解雇员工也会增加一些复杂性。我会将状态设置移动到
HandleRefreshOrders
中,然后在
继续执行中处理状态重置。(可能需要一些额外的建议/阅读,以确保其线程安全。)

*编辑:要明确移动
IsBusy
以避免重复执行:

private void HandleRefreshOrders()
{
    if (IsBusy)
        return;

    IsBusy = true;

    var task = Task.Factory.StartNew(() => RefreshOrders());
    task.ContinueWith(t => 
            {
                RefreshOrdersCompleted(t.Result);
                IsBusy = false;
            }, 
        CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion,  TaskScheduler.FromCurrentSynchronizationContext());
    task.ContinueWith(t => 
            {
                this.LogAggregateException(t.Exception, Resources.OrdersEditorVM_OrdersLoading, Resources.OrdersEditorVM_OrdersLoadingFaulted);
                IsBusy = false;
            },
    CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
}

然后从
RefreshOrders
方法中删除
IsBusy
引用。单击按钮一次后,
IsBusy
将被设置。如果用户快速单击,则触发命令的第二次尝试将跳过。任务将在后台线程上执行,完成后,
IsBusy
标志将重置,命令将再次响应。这当前假定对
RefreshOrdersCompleted
LogAggregateException
的调用不冒泡异常,否则如果抛出异常,则不会重置该标志。可以在这些调用之前移动标志重置,或者在annon中使用try/finally。方法声明。

>“我不会试图依靠忙指示器来控制有效的程序流”。为什么?你能争辩一下吗?如果(IsBusy)返回
,我看不出与
有任何关键区别。此外,问题不在于“如何防止双重命令执行”。我不明白,我怎么能实现双击按钮,它必须在第一次点击后隐藏。也许解释你的问题更清楚,因为你自相矛盾。“看起来BusyIndicator没有及时隐藏工具栏,两个后台线程试图执行RefreshOrders方法,…开始刷新。开始刷新。我做错了什么?”这看起来像是在问为什么要执行双重命令。或者,您是否希望双击按钮?在这种情况下,常规选项是在命令上启动计时器,如果再次及时单击,则执行双击行为;如果超时,则执行单次单击行为。好的,根据您的描述,忙碌指示器绑定到
IsBusy
属性。但是,您是从任务内部设置此属性的。(异步)处理单击事件的代码可以很好地完成,并在任务真正开始之前再次触发。如果在事件处理程序中而不是在任务中设置
IsBusy
,这可能会被阻止,但我怀疑是否有任何保证。AFAIK繁忙指示器通过控制其他指示器来捕捉事件,从而欺骗用户界面。在按钮捕获下一次单击之前,它可能不会就位。当从UI-thread设置
IsBusy
时,同样的效果(双重触发)也会发生。好的,我根据您提供的内容构建了一个快速示例,并确认了该行为。这个问题是我早些时候提出的。忙碌指示器拦截用户事件,但只有在它有机会生效之后。事实上,在busyindicator出现之前,您可以点击按钮的消息泵两下。您可以选择如上所述检测并忽略重新输入,或者将按钮的
IsEnabled
绑定到
IsBusy
属性。(使用反向转换器)这会转储消息,不会导致重复调用。