【问题标题】:MVVM Notifying Property Changed BackMVVM 通知属性改回
【发布时间】:2015-06-25 21:16:17
【问题描述】:

我有一个基于 MVVM 的应用程序(不使用任何特定的 mvvm 库或框架),有几个按钮我想根据方法是否正在运行来启用和禁用。我在 ViewModel 上设置了一个 bool 属性“ProcessRunning”:

bool _ProcessRunning = false;
/// <summary>
/// Whether A Process Is Currently Taking Place
/// </summary>
public bool ProcessRunning
{
    get
    {
        return _ProcessRunning;
    }
    set
    {
        if (_ProcessRunning != value)
        {
            _ProcessRunning = value;
            RaisePropertyChanged("ProcessRunning");
        }
    }
}

然后我有一系列按钮,其样式将它们绑定到此属性,如下所示:

按钮:

<Button Name="btn_AddMore" Content="More" Style="{StaticResource ExecuteButtons}"
        ToolTip="Enumerate Customer Folders To Get More Customer And Job Options"
        Command="{Binding AddFolderOptions}"/>

风格:

<Style TargetType="Button" x:Key="ExecuteButtons">
    <Setter Property="Width" Value="80"/>
    <Setter Property="Height" Value="40"/>
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="0,0,0,0"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="FontWeight" Value="700"/>
    <Setter Property="BorderBrush" Value="#FFA0A0A0"/>
    <Setter Property="Margin" Value="12,0,0,0"/>
    <Setter Property="Background" Value="#FFEEEEEE"/>
    <Setter Property="IsEnabled" Value="{Binding ProcessRunning, Converter={StaticResource Inverse}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Cursor" Value="Hand"/>
        </Trigger>
    </Style.Triggers>
</Style>

除了我在执行其他操作的方法期间更改它之外,绑定按预期工作:

private void AddOptionsFromFolders(object Parameter)
{
    try
    {
        ProcessRunning = true;
        Transfer_Workers.Program.Processes.AddFolderChoices(ref _LocalSettings);

在这种情况下,视图直到 AddOptionsFromFolders 退出后才会更新。

我通过其他几个问题发现 UI 线程在此方法调用期间被阻塞,并且在方法完成之前不会被释放更新,所以我尝试了这个:

private void AddOptionsFromFolders(object Parameter)
{
    try
    {
        ProcessRunning = true;

        //Transfer_Workers.Program.Processes.AddFolderChoices(ref _LocalSettings);

        Thread test = new Thread(() => { Thread.Sleep(2000); ProcessRunning = false; });
        test.Start();

在这种情况下,当 ProcessRunning 设置为 true 时 UI 会更新,但当“测试”线程完成并应将 ProcessRunning 设置为 false 时,UI 不会更新。

我的 ViewModel 继承自主视图模型,以及从我声明了 RaisePropertyChanged 的​​基本视图模型继承:

internal void RaisePropertyChanged(string prop)
{
    PropertyChanged(this, new PropertyChangedEventArgs(prop));
}

我似乎无法弄清楚为什么当 ProcessUpdated 设置回 false 时 UI 不更新。没有抛出异常,并且输出窗口中没有警告或错误 (Visual Studio 2013 Pro)。

我的预期结果是,当方法运行时,按钮将被禁用,而不是按钮被启用。如果有人可以在这里阐明手头的问题,或者甚至提出一种不同的方式来通知 View 以更异步的方式更改的属性,我将不胜感激。

另外,如果有什么我不清楚或遗漏的信息,请直接询问,我会尽快给您。

【问题讨论】:

  • 顺便说一句,Visual Studio 2013 Pro 是一个编辑器,不管你信不信,它对程序的输出没有直接影响;它只规定了可以使用的 .Net 版本。任何关于 SO 的帖子都更适合将正在编译的实际 .Net 版本放在标签列表中。

标签: c# wpf multithreading mvvm


【解决方案1】:

这是因为您正在从后台线程更新属性。 UI 无法从 UI 线程以外的线程更新,因此您的属性更改将被忽略。

您可以使用 async/await 来做到这一点:

ProcessRunning = true;
await Task.Run(() => 
{ 
    // do work here 
});
ProcessRunning = false;

在等待 Task 时捕获同步上下文,因此它会在 UI 线程上恢复执行。

但是,对于禁用按钮,您可能应该确保命令上的 CanExecute 返回 false,直到命令完成执行。

【讨论】:

  • 我想过这个,但是我不能使用匿名方法来运行任务,因为我需要在发生的操作中通过引用传递参数。此外,在任务确实完成之前,我不希望 ProcessRunning 设置为 false。
  • 如果我设置一个事件,UI 线程有一个处理程序,当进程完成并将属性重置为 false 时,该处理程序会被引发,这是否可行?
  • 然后将参数捕获为闭包?例如Task.Run(() =&gt; AddFolderChoices(ref _LocalSettings))。在任务完成之前它不会设置ProgressRunning=false。事件对您没有帮助,因为它将在同一个线程上引发和处理。
  • 好的,我明白你在说什么。创建了一个名为primed command的新命令对象,我将两个动作传递给它,一个是初始化值(将进程设置为true),另一个是使用Task.Run调用主动作并传递参数,它在大多数情况下都有效.这样做有一个小错误,由于某种原因,并非所有绑定都同时刷新,但非常感谢这对我有用。 :) 我将不得不回顾一下线程和任务之间的区别。
【解决方案2】:

应该避免直接读写可能与GUI线程相关的变量。

对于这种情况,在和你的线程结束时,使用调度器通知gui线程的变化,如

Application.Current.Dispatcher.BeginInvoke(
     DispatcherPriority.Background,
      new Action(() => ProcessRunning = false));

当我尝试从后台线程读取时,我遇到了类似的情况。在这里阅读我的经验C# WPF: Linq Fails in BackgroundWorker DoWork Event


次要想法

另外请记住,使用多线程时,您可能会遇到争用情况。

真的可以消除另一个线程也可能更改标志吗?或者后台线程实际上比预期更快地完成并在最初设置之前先设置值?

竞争条件可能很隐蔽,并且确实会发生。

【讨论】:

  • 除非我在链接中弄错了,否则您可能在后面的代码上将方法作为事件处理程序执行?并直接写入 UI,我的问题中描述的 ProcessRunning 是我的视图模型上的一个属性,UI 绑定到该属性,windows datacontext 设置为视图模型。如果我有误解,请尽管告诉我。我也尝试实现执行任务的应用程序调度,但无济于事。
  • 正如我在 Charles Mager 的回答中询问的那样,如果我设置一个事件,UI 线程有一个处理程序,当进程完成并且属性重置为 false 时,该处理程序会被引发,这应该工作吗?
猜你喜欢
  • 2015-10-29
  • 1970-01-01
  • 2011-06-30
  • 2013-02-03
  • 1970-01-01
  • 2012-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多