XAML序列化 - 需要指定属性
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XAML序列化 - 需要指定属性相关的知识,希望对你有一定的参考价值。
我正在尝试使用XAML序列化/反序列化一些自定义(非WPF / UI)信息,并希望强制要求某些属性。
默认情况下,XAML反序列化只是使用默认构造函数创建每个对象,然后在元素的属性或属性元素语法中设置它找到的任何属性。未被序列化的XAML中指定的基础对象的任何属性都保持不变,即它们在构造之后获得的任何值。
我想知道指定XAML中必须存在某个属性的最佳方法 - 如果没有,则反序列化失败。
我期待某种属性,但我找不到任何东西。
WPF中的某些类型确实表现出这种行为,但可能WPF使用自己的自定义方式来强制执行此操作。例如,如果你有..
<Setter Property="Height" ></Setter>
..设计师会抱怨'财产“价值”缺失'。
我可以想到一些非常复杂的方法:
- 让每个属性setter以某种方式记录它被调用,然后在反序列化后运行自定义代码,检查所有“必需”属性是否实际设置。
- 在任何地方使用可空属性,然后在反序列化后检查是否有任何“必需”属性仍然为空。 (当然,如果将null设置为有效的话,这将不起作用!)
- 也许有一种编写自定义XamlObjectWriter的方法可以检查对象属性的某些[Required]属性,如果XamlReader找不到这些属性,则会失败。
这些听起来比我希望的要多得多 - 而且我确信有更明显的方法。有没有人有这个问题的任何其他想法或经验(也许是解决方案)?
我最近遇到了类似的问题。在找不到任何简单的方法后,我决定加入XamlObjectWriter
的活动,为此添加自定义支持。这基本上是你在第3点建议的,除非结果并不那么复杂。
基本上它的工作方式如下:字典保存在每个反序列化对象映射到一组剩余必需属性的位置。 BeforePropertiesHandler
使用RequiredAttribute
填充当前对象的所有属性。 XamlSetValueHandler
从集合中删除当前属性。最后,AfterPropertiesHandler
确保没有在当前对象上设置的必需属性,否则抛出异常。
class RequiredAttribute : Attribute
{
}
public T Deserialize<T>(Stream stream)
{
var requiredProperties = new Dictionary<object, HashSet<MemberInfo>>();
var writerSettings = new XamlObjectWriterSettings
{
BeforePropertiesHandler = (sender, args) =>
{
var thisInstanceRequiredProperties = new HashSet<MemberInfo>();
foreach(var propertyInfo in args.Instance.GetType().GetProperties())
{
if(propertyInfo.GetCustomAttribute<RequiredAttribute>() != null)
{
thisInstanceRequiredProperties.Add(propertyInfo);
}
}
requiredProperties[args.Instance] = thisInstanceRequiredProperties;
},
XamlSetValueHandler = (sender, args) =>
{
if(!requiredProperties.ContainsKey(sender))
{
return;
}
requiredProperties[sender].Remove(args.Member.UnderlyingMember);
},
AfterPropertiesHandler = (sender, args) =>
{
if(!requiredProperties.ContainsKey(args.Instance))
{
return;
}
var propertiesNotSet = requiredProperties[args.Instance];
if(propertiesNotSet.Any())
{
throw new Exception("Required property "" + propertiesNotSet.First().Name + "" not set.");
}
requiredProperties.Remove(args.Instance);
}
};
var readerSettings = new XamlXmlReaderSettings
{
LocalAssembly = Assembly.GetExecutingAssembly(),
ProvideLineInfo = true
};
using(var reader = new XamlXmlReader(stream, readerSettings))
using(var writer = new XamlObjectWriter(reader.SchemaContext, writerSettings))
{
XamlServices.Transform(reader, writer);
return (T)writer.Result;
}
}
我知道这已经过时了,但是在我弄清楚之前不久我一直在寻找一种方法。也许它会帮助别人。幸运的是,这很简单:实施ISupportInitialize
。我正在使用WPF,但它应该可以在任何地方使用。
public class Hero : ISupportInitialize
{
public string Who
{ get; set; } = string.Empty;
public string What
{ get; set; } = string.Empty;
public string Where
{ get; set; } = string.Empty;
public void BeginInit()
{
// set a flag here if your property setters
// have dependencies on other properties
}
public void EndInit()
{
if (string.IsNullOrWhiteSpace(Who))
throw new Exception($"The property "Who" is missing");
if (string.IsNullOrWhiteSpace(What))
throw new Exception($"The property "What" is missing");
// Where is optional...
}
}
Who
和What
是必需的,但第二个条目缺少What
:
<Window.Resources>
<local:Hero x:Key="Badguy" Who="Vader" What="Sith" Where="Death Star"/>
<local:Hero x:Key="Goodguy" Who="HanSolo" Where="Millenium Falcon"/>
</Window.Resources>
在VS2017 XAML标记编辑器中:
以上是关于XAML序列化 - 需要指定属性的主要内容,如果未能解决你的问题,请参考以下文章
自定义控件中的 Xamarin BindableProperty
MVVM Light:在 XAML 中添加 EventToCommand 而不使用 Blend、更简单的方法或片段?