如何创建自定义*只写*依赖属性?
-
19-09-2019 - |
题
我需要知道创建只写依赖属性的过程是什么。我可以看到 DependencyProperty 类没有用于只写属性的特殊“Register”方法,但我不知道 RegisterAttached 方法是否适用于我想要执行的操作。
此属性需要是依赖属性,而不是简单的 CLR 属性。在内部,我的类需要对此属性进行 PropertyChangedCallback 以保持稳定。
我知道可以创建只写依赖属性,因为它在以下内容中表述得很清楚:
Pro C# 2008 和 .NET 3.5 平台,第 1061 页.
然而,这是我可以在同一页面上找到“依赖属性”和“只写”的唯一地方。这位作者显然认为没有必要向读者展示除基本读写依赖属性之外的任何内容的过程。当然,这本书 可以 是一堆废话——但这本书看起来很标准,所以我认为作者是正确的。我认为互联网上信息的缺乏源于这样一个事实:通常没有人需要建造这样的房产。
我知道想要创建自己的只写依赖属性听起来很可疑。我向你保证这在我想要的地方是有意义的。我的类有一个属性,其值仅对设置它的对象有用。如果另一个对象稍后请求该属性的值,则在不知道 setter 的原始上下文的情况下,它将无法从该值中获得任何理性意义。
该属性不用于提供信息。让外部对象尝试以这种方式使用属性值是有问题的、危险的和安全风险。所以我认为最好的设计是禁止对该属性的读取操作。任何使用我的课程的人都会发现他们被迫按照预期的方式使用该课程,这最终会变得更好、更干净。
解决方案
你不能,这似乎是设计使然。虽然我可以理解您对上述书籍的处理方式,并且绝不质疑其质量,但我仍然认为这是某种复制粘贴或类似的问题。这是我的推理:
WPF属性系统代码
- 正如您已经提到的公共 API 依赖属性类 仅有的功能 注册只读() 和 注册附加只读().
- 通过以下方式深入了解类内部 反射器 只揭示处理这些的专用代码 只读依赖属性, ,看不到任何关于只写功能的信息。
- 另一个选择可能是元数据,但两者都不是 依赖属性元数据 也不特别 框架属性元数据 借助于 FrameworkPropertyMetadataOptions 枚举 正在提供任何类似于只写的东西。
WPF属性系统设计
- 更重要, '其 XAML 处理器的当前 WPF 实现本质上是依赖属性感知的。加载二进制 XAML 并处理依赖属性的属性时,WPF XAML 处理器使用依赖属性的属性系统方法。这有效地绕过了属性包装器。, , 看 XAML 加载和依赖属性.
- 最重要的, '依赖属性通常应被视为公共属性。Windows Presentation Foundation (WPF) 属性系统的本质阻止了对依赖属性值进行安全保证的能力。, , 看 依赖财产安全.
特别是后两点概述了设计约束,依赖属性值始终可以通过 获取值()/设定值(), ,无论它们的 CLR 包装器是访问受限还是可用,唯一的例外是专门考虑的 只读依赖属性.
因此,作为 杰夫斯 答案已经暗示,仅删除 getter 例如并不能真正阻止任何人通过以下方式访问该属性 获取值(), ,尽管这至少可以 “减少自定义类立即暴露的命名空间”. 。任何此类语义解决方法的用处是使属性值的可见性/可访问性稍差,并且检索到的值对客户端来说本质上无用,如 杰夫 当然取决于您的具体情况。
其他提示
有趣的是,这绝对是一个罕见的情况下,我很想听到更多的是什么使。
你会考虑提供一个无效值(例如零),用于通过结合或读取的GetValue的想法,而只是不具有CLR吸气?
要么使用一个专用的DependencyProperty来存储您所关心的“真实”价值的或的只是一个私有成员变量。
在属性更改回调,随时恢复值返回到原来的值,同时存储程所设定的新的值。
我花了我大部分时间在做Silverlight控件的开发,所以这个属性在WPF和Silverlight的土地工作,并且不使用coercian或任何乐趣这样。也许它可以让你要在正确的轨道上,虽然。
/// <summary>
/// Sets the write-only dependency property.
/// </summary>
public string MyWriteOnlyDependencyProperty
{
set { SetValue(MyWriteOnlyDependencyPropertyProperty, value); }
}
private string _theRealSetValue;
private bool _ignorePropertyChange;
/// <summary>
/// Identifies the MyWriteOnlyDependencyProperty dependency property.
/// </summary>
public static readonly DependencyProperty MyWriteOnlyDependencyPropertyProperty =
DependencyProperty.Register(
"MyWriteOnlyDependencyProperty",
typeof(string),
typeof(TemplatedControl1),
new PropertyMetadata(null, OnMyWriteOnlyDependencyPropertyPropertyChanged));
/// <summary>
/// MyWriteOnlyDependencyPropertyProperty property changed handler.
/// </summary>
/// <param name="d">TemplatedControl1 that changed its MyWriteOnlyDependencyProperty.</param>
/// <param name="e">Event arguments.</param>
private static void OnMyWriteOnlyDependencyPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TemplatedControl1 source = d as TemplatedControl1;
if (source._ignorePropertyChange)
{
source._ignorePropertyChange = false;
return;
}
string value = e.NewValue as string;
source._theRealSetValue = value;
// Revert, since this should never be accessible through a read
source._ignorePropertyChange = true;
source.SetValue(e.Property, e.OldValue);
}
它看起来像可以使用通过在依赖属性定义施加的CoerceValueCallback
的属性相关联的FrameworkPropertyMetadata
。只要安装回调,是以第二个参数,新的值,把它传递给经由自己的只写机构的对象,然后返回null
(或值类型,default(T)
)。
这是真的,“.NET记得之前强迫原始值”,但它不会通过数据绑定传播。调用GetValue
将返回裹挟值,其不泄漏任何东西。
我使用这个实现用于我的主要属性,这是一个字节序列的值单向方便setter方法。用户可以结合的字符串,例如,以设置主媒体资源到已编码的字节(ASCII或UTF-8,这取决于设置什么属性)。但是,并非所有的字节序列是有效的UTF-8,所以这是不可能扭转转换并通过简便属性读取的字符串返回。
public string AsciiData
{
set { BinaryArray = Encoding.ASCII.GetBytes(value); }
}
public static readonly DependencyProperty AsciiDataProperty =
DependencyProperty.Register("AsciiData",
typeof(string),
typeof(HexView),
new FrameworkPropertyMetadata(null, CoerceAsciiData));
private static object CoerceAsciiData(DependencyObject target, object value)
{
(target as HexView).AsciiData = value as string;
return null;
}
在胁迫处理程序可以通过元数据替换被移除,因此这是不提供的安全,但它会防止开发人员偶然创建在错误的方法耦合。
我很困惑,为什么你不能只是有“得”返回任何有用吗?
但此外,也许你只是不落实“OnMyWriteOnlyDependencyPropertyPropertyChanged”,在杰夫的例子。
没有真正的理由有情况下,如果没有人能读它,对吗?