我对此非常困惑,它开始让我质疑我对WPF资源系统的全面理解
我有一个多窗口应用程序,其中每个Window派生对象在一个单独的线程上运行,并带有单独的调度程序.
Thread t = new Thread(() => {
Window1 win = new Window1();
win.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
Run Code Online (Sandbox Code Playgroud)
我有一个Dictionary1.xaml资源字典,里面有一个名为Style的对象(它只是将Background属性设置为Red,并以TextBox为目标).在我的App.xaml中,我通过ResourceDictionary.MergedDictionaries集合引用了Dictionary1.xaml.在我的其他窗口的XAML中,我有一个StaticResource到文本框控件中的样式键,它可以工作.
我可以打开多个窗口,但不应该遇到跨线程错误?在其中一个窗口类的构造函数中,我这样做了:
Style s = (Style)TryFindResource("TestKey");
Console.WriteLine(((Setter)s.Setters[0]).Property.Name); // no problem
s.Dispatcher == this.Dispatcher // false
Run Code Online (Sandbox Code Playgroud)
由于Style对象是从DispatcherObject派生的,这是否意味着它只能由拥有它的线程访问?如果在ResourceDictionary中定义了一个对象,这是不是意味着默认情况下它是一个静态实例?这怎么可以工作?为什么我没有遇到跨线程错误?
(我错误地报告了一个问题,因为我删除了一个由其他东西引起的交叉线程错误)
我对此感到非常困惑 - 我认为只有冻结的Freezable对象才能跨线程共享.为什么我可以在其他线程上访问DispatcherObject?
所以我终于有了答案 - 我深入研究了很多 .NET 框架代码并得出以下结论:
当资源字典是应用程序级字典、主题字典或只读时,存储在资源字典中的所有项目都将被“密封”
if (this.IsThemeDictionary || this._ownerApps != null || this.IsReadOnly)
{
StyleHelper.SealIfSealable(value);
}
...
internal static void SealIfSealable(object value)
{
ISealable sealable = value as ISealable;
if (sealable != null && !sealable.IsSealed && sealable.CanSeal)
{
sealable.Seal();
}
}
Run Code Online (Sandbox Code Playgroud)
“密封”一个对象本质上使其不可变,并通过 ISealable 字典实现 - 事实上 Freezable 通过调用 Freeze() 实现其密封行为!样式也实现了它,并且它的实现可以防止 Setters 或 Triggers 集合被修改。ISealable 由许多类实现!
public abstract class Freezable : DependencyObject, ISealable
public class Style : DispatcherObject, INameScope, IAddChild, ISealable, IHaveResources, IQueryAmbient
Run Code Online (Sandbox Code Playgroud)
更重要的是,我所见过的(到目前为止)每个 WPF 类中 Seal() 的每个实现都会调用 DispatcherObject.DetachFromDispatcher() ,这会将调度程序设置为 null!(Freeze() 内部也调用这个)
“密封”似乎实现了经常被宣传为 Freezable 独有的不变性行为,但实现 ISealable 的对象将表现出相同的行为并且是线程安全的 - Freezable 具有区分它的附加行为(例如,子属性通知更改)
总而言之,“密封”对象实际上使其能够跨线程共享,因为如果每个对象存在于应用程序级或主题资源字典或只读资源中,则每个对象都会自动与其调度程序分离字典
要验证此结论,请反思 ResourceDictionary 并查看设置 ResourceDictionary 的逻辑父级(例如 FrameworkElement、FrameworkContentElement 或 Application)的 AddOwner() 方法
这就是为什么画笔和 Style 对象可以从其他线程访问,因为资源字典被合并到 Application.Resources 中,因此全部被自动密封 - 毫不奇怪,将这些资源放在窗口级别将导致常见的跨线程异常。
我想您可以归纳出 ISealable 使 WPF 对象能够跨线程共享并且是只读的,尽管从技术上讲,与 Dispatcher 分离并不是协议的要求(就像 DispatcherObjects 不需要在每个线程中进行 verifyAccess() 调用一样)属性),因为从技术上讲,每个对象都需要实现自己的 Seal() 行为,并且 ISealable 和 Dispatcher 之间没有直接关联
| 归档时间: |
|
| 查看次数: |
899 次 |
| 最近记录: |