c# - WPF MVVM 在 UI/后台线程之间共享属性

标签 c# wpf multithreading mvvm

我无法在网上找到一个我能理解的简单示例来说明如何实现这一目标。我也不想使用 MvvmLight。

我的 View 包含一个填充颜色的矩形,该矩形绑定(bind)到我的 View 模型中的 SolidColorBrush 属性。

我的模型还有一个 SolidColorBrush 属性和一个 UpdateBrushColor 方法。

我希望我的 View 模型运行一个后台线程,该线程可以在模型中调用 UpdateBrushColor,然后更新其自己的 SolidColorBrush 以匹配模型画笔颜色,然后这将通过数据绑定(bind)更新 UI。

我在后台使用 Dispatcher.Invoke 来更新 UI,但仍然收到异常:“必须在与 DependencyObject 相同的线程上创建 DependencySource”。

查看(仅控件)

<StackPanel VerticalAlignment="Center">
  <Rectangle Height="50"
              Width="50"
              Fill="{Binding Brush}">
  </Rectangle>
  <CheckBox></CheckBox>
</StackPanel>

型号

class Model
{
    public SolidColorBrush Brush { get; set; } = Brushes.Black;

    public void UpdateBrushColor()
    {
        Thread.Sleep(100);
        Random rnd = new Random();
        byte r = (byte)rnd.Next(0, 255);
        byte g = (byte)rnd.Next(0, 255);
        byte b = (byte)rnd.Next(0, 255);
        Brush = new SolidColorBrush(Color.FromArgb(255, r, g, b));
    }
}

查看模型

class Vm : INotifyPropertyChanged
{
    public Model Model { get; set; }

    private SolidColorBrush brush;
    public SolidColorBrush Brush
    {
        get { return brush; }
        set { SetProperty(ref brush, value); }
    }

    public Vm()
    {
        Model = new Model();

        Task.Factory.StartNew(() =>
        {
            while (true)
            {
                Model.UpdateBrushColor();

                Application.Current.Dispatcher.Invoke(() =>
                {                        
                    Brush = Model.Brush;    // why can't access model from here?
                });
            }
        });
    }

    // Data Binding Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Object.Equals(storage, value))
            return false;

        storage = value;
        NotifyPropertyChanged(propertyName);
        return true;
    }
}

最佳答案

不要在模型中使用 Brush 类,因为它派生自 DispatcherObject,因此具有线程亲和性。在任务线程中创建的实例不能在 UI 线程中使用。

您可以使用Color,它是一种没有线程关联的结构类型。

除此之外,您还可以使用简单的 DispatcherTimer 进行循环更新:

public class Model
{
    private readonly Random random = new Random();

    public Color Color { get; private set; }

    public void UpdateColor()
    {
        Color = Color.FromRgb(
            (byte)random.Next(256), (byte)random.Next(256), (byte)random.Next(256));
    }
}

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Model Model { get; } = new Model();

    private Brush brush;
    public Brush Brush
    {
        get { return brush; }
        set
        {
            brush = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Brush)));
        }
    }

    public ViewModel()
    {
        var timer = new DispatcherTimer
        {
            Interval = TimeSpan.FromSeconds(0.1)
        };

        timer.Tick += (s, e) =>
        {
            Model.UpdateColor();
            Brush = new SolidColorBrush(Model.Color);
        };

        timer.Start();
    }
}

关于c# - WPF MVVM 在 UI/后台线程之间共享属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46631399/

相关文章:

c# - 日期格式化 WPF 数据网格

c# - 单个事务中的多线程

c# - 选择 child 后触发 WPF TreeviewItem 父选择事件?

c# - 在代码隐藏的 WPF 中创建闪烁动画

c# - 获取绑定(bind)的父级

java - 如何限制spring中的对象数量

c++ - 如何访问线程外的线程数据

c# - 解析 DateTime C# 不适用于我的格式

c# - Xamarin 选项卡导致 System.NullReferenceException

c# - P/Invoke & InterOP 中是否有用于编码的匹配类型表?