【问题标题】:Multiple views sharing same data with two-way data binding between multiple threads多个视图通过多线程之间的双向数据绑定共享相同数据
【发布时间】:2019-02-15 05:41:38
【问题描述】:

UWP 应用(mvvm 架构)我有一个 MainView,它的 ViewModel 中有一个集合,用于绑定到 MainView 上的 GridView每个项目都有一个 TextBox,具有 2 路数据绑定,具有 Note 类的 Description 属性。

每个gridviewitem的TextBox的Xaml。

<TextBlock Text="{x:Bind Description,Mode=TwoWay}"

用于绑定到gridview的ItemSource的集合属性。

public ObservableCollection<Note> Notes { get; }

这是类注意

public class Note : Observable
{
    private string _description;
    public string Description
    {
        get => _description;
        set => Set(ref _description, value, nameof(Description));
    }        
}

Observable 类用于两种数据绑定帮助。

public class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return;
        }

        storage = value;
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

现在一切正常,当我更改文本框中的文本时,它也会更改描述的值。

第二次查看

现在我有一个功能,其中每个 GridViewItem 都有一个 按钮,它可以在 新窗口 中打开注释。而这个新窗口只有 1 个 TextBox,所以现在辅助视图和打开该视图的 GridViewItem 使用的是 Note 的相同对象。

辅助视图中的此文本框还具有与 Note 的 描述 的 2 路数据绑定。

问题

我想要的是,无论gridview中的文本框还是辅助视图上的文本框被编辑,描述的值必须在这两个文本框之间保持同步,这就是为什么我试图将它们与同一个对象绑定2种注意因此,相同的 Description 对象绑定到它们两者。

这里的错误对我来说是编组线程错误,所以每当我尝试更改任何文本框的值时,它都会尝试更新其他视图(这是另一个线程)上的 UI,这当然是不允许。

我知道 CoreDisptcher

我已经知道 UWP 用于安全跨线程通信的 Dispatcher 功能,我已经完成了所有设置,如果我通过常规方法使用它,我可以轻松地将它用于跨线程 UI 更新,它完全可以工作。但我的问题是以下行:

protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\

当它试图调用 PropertyChanged 我试图在我的 Dispatcher 中包装以下行时发生异常:

OnPropertyChanged(propertyName);

但是 INotify 接口不允许我有一个返回 Task 的 Set 方法,它只需要返回一个对象,这就是我卡住的地方,我不知道怎么做要在这种情况下使用 Dispatcher,请让我知道是否有更好的方法可以做到这一点,看来这种方式可能效率不高。 谢谢。

【问题讨论】:

    标签: c# multithreading uwp dispatcher multiviews


    【解决方案1】:

    在这种情况下,最好的解决方案是为每个窗口设置一组单独的 INotifyPropertyChanged 实例,并使用某种消息传递解决方案,例如 MvvmLight 中的 EventHub,它会发布底层模型已更改以及所有相关方的消息应该更新他们的实例。

    另一种选择是创建一个基本模型类,它为每个 UI 线程维护一个包含 INotifyPropertyChanged 实例的字典(因此它将是一个 Dictionary&lt;Dispatcher, YourModelClass&gt;。现在父级将订阅每个子级的 PropertyChanged 事件实例,一旦它执行,就会使用适当的Dispatcher 将事件传播给其他孩子。

    还有一个非常有趣的实用程序类 ViewSpecificBindableClass 由 Marian Dolinský on his GitHub 编写,它可能是一个解决方案,可以让您在多个视图中拥有“单个”类,同时了解多个调度程序。我还没有尝试过,但看起来很有希望。

    【讨论】:

    • 我可以获得 ViewSpecificBindableClass 的链接吗?
    • 我创建了一个 github 存储库,其中包含此问题的更简单版本来重现它,请查看任何帮助将不胜感激,github.com/touseefbsb/MultiWindowBindingSync
    • 我明天会检查它,只是去睡觉:-)。如果我忘记了,请明天提醒我:-)。编码愉快!
    • 明天我会在推特上联系你 :)
    • 你好马丁,只是想提醒你一下:)
    【解决方案2】:

    所以我最终不得不采取一种完全不同的方法来集中 MainView 文本框的 TextChanged 事件 和辅助视图上的事件。

    我基本上将主页上的文本框传递到辅助页面(辅助视图),然后订阅其 TextChanged 事件。我还在辅助视图上订阅了文本框的 TextChanged 事件,然后在反向 dispatchers 的帮助下,我能够毫无问题地在 2 个窗口之间同步文本。

    注意:务必确保在辅助窗口关闭时取消订阅事件以防止内存泄漏。

    private async void PipBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        string text = PipBox.Text;
        await CoreApplication.MainView.Dispatcher.AwaitableRunAsync(() =>
        {
            if (parentBox.Text != text)
                parentBox.Text = text;
        });
    }
    private async void ParentBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        string text = parentBox.Text;
        // the awaitablerunasync extension method comes from "Windows Community Toolkit".
        await _viewLifetimeControl.Dispatcher.AwaitableRunAsync(() =>
        {
            if (ViewModel.MyNote.Description != text)
                ViewModel.MyNote.Description = text;
        });
    }
    

    请注意,我在两个文本框上仍然有 2 路数据绑定,它不会导致任何异常,因为我对两个视图都使用了 2 个不同的 Note 实例。

    <TextBox Text="{x:Bind ViewModel.MyNote.Description, Mode=TwoWay}"
                     x:Name="PipBox"/>
    

    但因为我在两个文本框上都有双向数据绑定,所以我可以轻松地让两个 Note 实例在不同的线程上保持同步。

    我会保留 github repo 以防它可以帮助其他人:https://github.com/touseefbsb/MultiWindowBindingSync

    P.S :特别感谢 Martin Zikmund,他在解决这个问题上帮助了我很多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-10
      • 2014-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      相关资源
      最近更新 更多