c# - 使用BackgroundWorker交换绑定(bind)属性的对象引用

标签 c# wpf xaml binding

一点背景

我正在构建一个使用插件体系结构访问特定设备的应用程序。这些设备具有我称为属性的字段,可以读取,写入或同时读取和写入。这是通过在自己的线程中运行的WPF前端和后端的MVVM类似结构完成的,该结构包装了特定的插件以通用方式公开其属性。

有关线程结构的一些细节

我有两个“主机”对象,一个启动插件结构,最终公开了两个视图:一个可用插件的视图和(如果选择了一个插件)一个插件已知的所有属性的视图。另一个最终启动STA线程并运行WPF应用程序中的主窗口。第二个主机使用BackgroundWorkers进行选择,初始化,更新和提交任务。 DoWork事件/代理(我相信它们称为委托)在MVVM结构中的ViewController类中定义,并提供用于更新等的功能接口,并实现INotifyPropertyChanged接口。

更多细节

创建主窗口后,将其上下文设置为视图控制器对象。然后,GUI将两个列表框绑定到两个视图。

问题

当我从UI线程中调用selectPlugin方法时,它将冻结,直到完成连接操作并将每个单个属性加载到ViewModel包含的列表中为止。但是,它确实可以工作,之后,ListBox ItemsSource绑定将更新,并显示所有属性。

但是,我不希望UI冻结所有操作,因此我实现了背景工作人员。一切正常,更新对象,并用新的View实例替换绑定源。但是绑定本身不会更新。

我尝试使用UI线程中的Dispatcher.Invoke或DoWorkComplete事件分配不同的解决方案。我发现使用以下属性设置器,PropertyChanged事件本身保持为空:

    public IAttributesViewModel AttributesView
    {
        get 
        {
                StaticLogger.WriteDebugLog(log,
                                           String.Format("Executing {0}",
                                                         System.Reflection.MethodBase.GetCurrentMethod()));
                return _currentAttributes;
        }
        set 
        {
            _currentAttributes = value;
            OnPropertyChanged("AttributesView");
        }
    }


INotifyPropertyChanged的实现如下所示:

    #region INotifyPropertyChange implementation
    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    /// <summary>
    /// Called when [property changed].
    /// </summary>
    /// <param name="name">The name.</param>
    protected void OnPropertyChanged(string name)
    {
        StaticLogger.WriteDebugLog(log, String.Format("Executing {0}", System.Reflection.MethodBase.GetCurrentMethod()));
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion


上面声明的PropertyChanged事件始终为null。这可能或应该与XAML中的实际绑定有关,但是我确实在UI线程中设置了主窗口的de datacontext,如下所示:

    private static void Host(object viewControler)
    {
        var controler = viewControler as IViewController;
        _instance._main = new MainWindow(controler) { DataContext = controler };
        var gui = new App();
        if (_instance._main == null) throw new ArgumentNullException("viewControler");
        gui.Run(_instance._main);
    }


我会在UI主机对象上使用Singleton实例,以防万一。

在XAML中,我对包含AttributesListbox和一些样式代码的UserControll使用自定义依赖项属性。实际的ListBox为其自身的ItemSource属性绑定到此属性。不要以为这与直接使用列表框应该没有什么不同,但以防万一这导致PropertyChanged事件为null的问题,并且可以通过以下方式实现:

C#
    ///
    /// AttributesListBox.xaml的交互逻辑
    ///
    公共局部类AttributesListBox:UserControl
    {
        私有静态只读UIPropertyMetadata _sourceMetadata =新的UIPropertyMetadata(String.Empty,
            SourceChangedCallback);

    public AttributesListBox()
    {
        InitializeComponent();
    }

    /// <summary>
    /// The dependency property that gets or sets the source of the ListBox to render.
    /// </summary>
    public static DependencyProperty sourceProperty = DependencyProperty.Register(
        "Source", typeof(IEnumerable), typeof(AttributesListBox),_sourceMetadata);
    /// <summary>
    /// Gets or sets the nsource of the image to render.
    /// </summary>
    public IEnumerable Source
    {
        get { return (IEnumerable)GetValue(sourceProperty); }
        set { SetValue(sourceProperty, value); }
    }

    private static void SourceChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var box = (AttributesListBox) d;
        box.List.ItemsSource = e.NewValue as IEnumerable;
    }
}


XAML

<ListBox Name="List" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Source}" BorderBrush="{x:Null}" />


第一个问题

我将ViewController对象与UI结合使用的方式有什么问题?因此,为什么PropertyChanged事件始终为null?这应该意味着我在某个地方搞砸了绑定过程,不是吗?

第二个问题

如何获取OnPropertyChanged事件以通知UI线程,具体说明UserControll(AttributesListbox)中的绑定?

活动遵循什么路线?

例如:
我使用DoWork委托,并且此方法直接从实例中更改属性,而该实例不在UI线程中。这是否导致事件永远不会到达UI线程,因为它是在另一个工作线程中引发的?

我无法理解事件冒泡/监视,是仅限于创建实例的线程还是调用特定方法(使用分派器或其他方法)的线程的事件?

假设

我怀疑我的情况有两个问题:
1. PropertyChanged处理程序保持为空,因为该对象未绑定或未在正确的线程中创建。
2.如果该事件在实际位置触发,则该事件永远不会到达正确的线程,因为它被卡在BackgroundWorker线程或后端“主机”线程中。

这是我在这里问的第一个问题,因此,如果您在拼图中遗漏了一些碎片,请告知我。

感谢您抽出宝贵的时间来阅读我的小问题情况,希望我们能够找到解决方案。

最佳答案

我认为您可能搞砸了列表框的绑定,即(为简化起见,简称为:)

<ListBox ItemsSource="{Binding Source}" />


此绑定的意思是“在我当前的Source中查找名为DataContext的属性”,我怀疑这不是您想要的。如果要绑定到Source的属性AttributesListBox(我想它是上面的列表框的宿主),则应采用以下方式:

<ListBox ItemsSource="{Binding Source, RelativeSource={RelativeSource AncestorType={x:Type AttributesListBox}}}" />


这意味着-“在属于AttributesListBox类型的树的第一个对象中查找名为Source的属性”。当然,您仍然必须将该Source属性绑定到控制器的正确内容,但是我想您已经做到了。

处理属性更改事件的方法是必须从UI(Dispatcher)线程引发该事件,如果从后台线程引发该事件,则wpf不会自动将其编组到UI线程。因此,请确保在完成后台工作后,设置需要在UI线程上更新的属性。

关于c# - 使用BackgroundWorker交换绑定(bind)属性的对象引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4508338/

相关文章:

wpf - ListBox中的滚动效果

c# - 将方法作为构造函数的参数传递

c# - 正则表达式匹配.Net

c# - 我如何使用 System.Data.SQLite merge 制作单一可执行文件?

c# - Windows 10 专业版 1803 蓝牙配置文件访问

wpf - 在 F# 中访问 XAML (WPF) 元素

C# - 使用 HTML Agility Pack 获取标签内的文本

c# - 如何从MySql数据库获取当前用户名信息,C#

c# - C++ 后端与 C# 前端?

c# - 创建自定义 WPF 控件并将 DataTemplate 设置为依赖属性