一点背景
我正在构建一个使用插件体系结构访问特定设备的应用程序。这些设备具有我称为属性的字段,可以读取,写入或同时读取和写入。这是通过在自己的线程中运行的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/