Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.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# 工作完成前WPF MVVM灯光显示通知_C#_Wpf_Mvvm_Mvvm Light - Fatal编程技术网

C# 工作完成前WPF MVVM灯光显示通知

C# 工作完成前WPF MVVM灯光显示通知,c#,wpf,mvvm,mvvm-light,C#,Wpf,Mvvm,Mvvm Light,在我的MVVM Light应用程序中,我想在做一些需要两三秒钟的同步工作之前显示一个通知。我不希望用户在工作完成时做任何事情,因此不需要异步、任务和IProgress或backgroundworkers等 在ViewModel中,我有以下代码。(请注意,它不是位于XAML文件的代码隐藏中,而是位于数据绑定的ViewModel中) NavigationService将通知文本添加到客户端窗口顶部的数据绑定列表视图中 我的问题是,“Doin'it”在加载项目后显示,无论它花费多长时间 一个例子是当我

在我的MVVM Light应用程序中,我想在做一些需要两三秒钟的同步工作之前显示一个通知。我不希望用户在工作完成时做任何事情,因此不需要异步、任务和IProgress或backgroundworkers等

在ViewModel中,我有以下代码。(请注意,它不是位于XAML文件的代码隐藏中,而是位于数据绑定的ViewModel中)

NavigationService将通知文本添加到客户端窗口顶部的数据绑定列表视图中

我的问题是,“Doin'it”在加载项目后显示,无论它花费多长时间

一个例子是当我想加载并显示一个项目时。如果加载部分需要几秒钟的时间,界面将冻结,用户不知道发生了什么

编辑

由于请求,我添加了更多代码。这段代码运行良好

显示通知的视图具有以下XAML

<ListView ItemsSource="{Binding Notifications}">
   <ListView.ItemTemplate>
      <DataTemplate>
         <Label Foreground="LightGray" Content="{Binding Message}" />
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

您没有收到通知的原因是您在调度程序线程(又名:UI线程)上运行,即应用程序的主线程,该线程也用于重新绘制屏幕。因此,当您等待
GetTheJobDone(project)
方法完成时,不会进行屏幕绘制。因此,您需要两个线程来完成这项工作,一个线程来完成这项工作,另一个线程来完成通知

例如:

让我们在自己的线程上移动工作,但是,我们需要禁用屏幕(根据您的设计)。这可以通过在窗口上设置
ishitestvisible
来实现(有关更好的方法,请参见文章末尾)

广泛的错误处理是必要的,因为我们阻止了与UI的交互——这不是一件好事(见下文)——并且必须确保在发生错误时它再次可用

使用.NET 4.5时,您可以将代码归结为:

try
{
    textBox1.Text = "Started";
    this.IsHitTestVisible = false; // this being the window
    await Task.Factory.StartNew(() => {
        this.GetTheJobDone();
    });
    textBox1.Text = "Done";
}
catch (Exception ex)
{
    this.textBox1.Text = "Error: " + ex.Message;
}
finally
{
    this.IsHitTestVisible = true;
}
这里不需要
Dispatcher.Invoke
,因为等待封送返回调用线程。此外,错误处理变得更加清晰,因为现在常见的错误处理涵盖了多个执行路径


然而,与其只是阻塞UI,不如显示一个忙碌的指示器(一个带有进度指示器的覆盖图),这样她就知道发生了什么。在这种情况下,不需要设置
ishitestvisible

以下是我自己基于的解决方案。正如我所建议的,我必须使它异步工作。现在我不锁定GUI,它工作正常

在所有ViewModels的基类中,我添加了一个名为AddTask的方法

public Task AddTask(Action work, 
   string notification, 
   string workDoneNotification,       
   Action<Task> continueWith)
{
   int notificationKey = NavigationService.AddNotification(notification,
                             autoRemove:false);

   Task task = Task.Factory.StartNew(() =>
   {
       work.Invoke();
   });

   task.ContinueWith(t =>
   {
       NavigationService.RemoveNotification(notificationKey);
       NavigationService.AddNotification(workDoneNotification);
       if (continueWith != null)
       {
           continueWith.Invoke(t);
       }
   }, TaskScheduler.FromCurrentSynchronizationContext());

   return task;
}

您可能仍然需要一个线程来完成这项工作,还需要一个线程来显示对话框。否则,UI线程将忙于执行工作,并且在工作完成之前无法显示消息。您需要将代码发布到方法,可能还有一些XAML,以便我们查看您是否正在更新绑定到UI的属性。您也可以自己确定,并告诉我们您是否是。谢谢,但是您是否有建议如何根据MVVM模式实现它。为了方便起见,我在示例中使用了
文本框。通常,您只需调用
NavigationService.AddNotification
方法,就像您在回答中所做的那样。如果一个答案是有帮助的,那么获得一个向上投票的奖励将是很好的;-)我会的,我只是以前没有这个名声:)
textBox1.Text = "Started";
this.IsHitTestVisible = false; // this being the window

try
{
    Task.Factory.StartNew(() => {
        // handle errors so that IsHitTestVisible will be enabled after error is handled
        try
        {
            this.GetTheJobDone();
            Dispatcher.Invoke(() => {
                // invoke required as we are on a different thead
                textBox1.Text = "Done";
            });
        }
        catch (Exception ex)
        {
            Dispatcher.Invoke(() => {
                this.textBox1.Text = "Error: " + ex.Message;
            });
        }
        finally
        {
            Dispatcher.Invoke(() => {
                this.IsHitTestVisible = true;
            });
        }
    });
}
catch (Exception ex)
{
    this.textBox1.Text = "Error: " + ex.Message;
    this.IsHitTestVisible = true;
}
try
{
    textBox1.Text = "Started";
    this.IsHitTestVisible = false; // this being the window
    await Task.Factory.StartNew(() => {
        this.GetTheJobDone();
    });
    textBox1.Text = "Done";
}
catch (Exception ex)
{
    this.textBox1.Text = "Error: " + ex.Message;
}
finally
{
    this.IsHitTestVisible = true;
}
public Task AddTask(Action work, 
   string notification, 
   string workDoneNotification,       
   Action<Task> continueWith)
{
   int notificationKey = NavigationService.AddNotification(notification,
                             autoRemove:false);

   Task task = Task.Factory.StartNew(() =>
   {
       work.Invoke();
   });

   task.ContinueWith(t =>
   {
       NavigationService.RemoveNotification(notificationKey);
       NavigationService.AddNotification(workDoneNotification);
       if (continueWith != null)
       {
           continueWith.Invoke(t);
       }
   }, TaskScheduler.FromCurrentSynchronizationContext());

   return task;
}
AddTask(() => GetTheJobDone(), 
   "Doin' it",
   "It's done",
   t => LoadProjects());