【发布时间】:2010-12-22 10:42:58
【问题描述】:
一点背景
我正在构建一个使用插件架构来访问特定设备的应用程序。这些设备具有我命名为属性的字段,可以读取、写入或两者兼而有之。这是使用在其自己的线程中运行的 WPF 前端和后端中的类似 MVVM 的结构来完成的,该结构包装特定插件以以通用方式公开其属性。
关于线程结构的一些细节
我有两个“宿主”对象,一个启动插件结构最终公开两个视图:可用插件的视图和(如果选择了插件)单个插件已知的所有属性的视图在。另一个最终启动 STA 线程并在 WPF 应用程序中运行主窗口。第二台主机使用 BackgroundWorkers 来执行选择、初始化、更新和提交任务。 DoWork 事件/委托(我相信它们被称为委托)在 ViewController 类中定义,该类位于 MVVM 结构中,并提供用于更新等的功能接口,并实现 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 事件始终为空。这可能或应该与 XAML 中的实际绑定有关,但是我确实在 UI 线程中设置了主窗口的数据上下文,如下所示:
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 = new 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 事件总是为空?这应该意味着我在某处搞砸了绑定过程不是吗?
第二个问题
如何获得 OnPropertyChanged 事件以通知 UI 线程,具体是 UserControll(AttributesListbox) 中的绑定?
事件遵循什么路线?
例如: 我使用 DoWork 委托,此方法直接从实例中更改属性,而不是在 UI 线程中。这是否会导致事件永远无法到达 UI 线程,因为它是在其他工作线程中引发的?
我无法理解事件冒泡/隧道,事件是否仅限于创建实例的线程或调用特定方法的线程(使用调度程序或其他)?
假设
我怀疑我的情况有两个问题: 1. PropertyChanged 处理程序保持为空,因为对象未绑定或未在正确的线程中创建。 2. 事件,如果它实际被触发,永远不会到达正确的线程,因为它卡在 BackgroundWorker 线程或后端“主机”线程中。
这是我在这里提出的第一个问题,所以如果你遗漏了一些拼图,请告诉我。
感谢您花时间阅读我的小问题情况,希望我们能找到解决方案。
【问题讨论】: