如何解决带有转换的 WPF MarkupExtension,其中参数是一个类
我有一个应用程序,它对绑定到自定义 ConcurrentDictionary
的数据使用稍微修改过的 ObservableConcurrentDictionary
(暴露的 Count 和为 FrameworkElement
比较器添加的构造函数)。存储在字典中的数据有很多需要显示或影响渲染的属性。
public class DictionaryData : ObservableConcurrentDictionary<string,ItemValueData>
{ public DictionaryData() : base(StringComparer.InvariantCultureIgnoreCase) { } }
public class ItemValueData
{
// properties
public string Source { get; set; }
public string Name { get; set; }
public int Quality { get; set; }
public double Value { get; set; }
// ... many other properties
// omitted members / constructors / private variable etc.
}
ObservableConcurrentDictionary
数据被实例化为 DD a DependencyProperty
的 Window/Canvas/Page/Container...
public DictionaryData DD {
get => (DictionaryData)GetValue(DDProperty);
set { SetValue(DDProperty,value); OnPropertyChanged("DDProperty"); }
}
public readonly DependencyProperty DDProperty =
DependencyProperty.Register("DD",typeof(DictionaryData),typeof(MyWindowApp),new FrameworkPropertyMetadata(null,FrameworkPropertyMetadataOptions.AffectsRender));
在可用的 XAML 中,我目前为 ItemValueData
类中的每个唯一属性使用不同的绑定转换器。
<ElementA Value="{av:Binding DD Converter={Converters:ItemConverterName},ConverterParameter='Item001Name'}" .../>
<ElementB Value="{av:Binding DD Converter=Converters:ItemConverterQuality},ConverterParameter='Item001Name'}" .../>
<ElementC Value="{av:Binding DD Converter=Converters:ItemConverterValue},ConverterParameter='Item001Name'}" .../>
<ElementD Value="{av:Binding DD Converter=Converters:ItemConverterSource},ConverterParameter='Item001Name'}" .../>
<!-- several hundred FrameWorkElements -->
<ElementA Value="{av:Binding DD Converter={Converters:ItemConverterValue},ConverterParameter='Item400Name'}" .../>
其中每个转换器处理 ItemValueData 类的单个属性。 (.Name 映射到 ItemConverterName 等等...)
我想要的是一个转换器,它可以通过传入要转换的属性的名称以及查找数据的字典的键来转换任何属性。
[ValueConversion(typeof(DictionaryData),typeof(object))]
public class ItemConverterGeneric : MarkupExtension,IValueConverter {
public object Convert(object value,Type targettype,object parameter,CultureInfo culture) {
try {
if (value != null && parameter != null) {
DictionaryData dict = (DictionaryData)value;
// Would like make the property use a class here
// SomeClass x = parameter as SomeClass;
// string key = x.Key;
// string prop = x.Prop;
string key = parameter as string;
if (dict.ContainsKey(key)) {
// switch(prop) { pick the right property }
return dict[key].Name; // pass as parameter?
}
}
return Binding.Donothing;
} catch (Exception ex) {
Console.WriteLine(ex.Message);
return Binding.Donothing;
}
}
public object ConvertBack(object value,Type targettypes,CultureInfo culture)
{ return DependencyProperty.UnsetValue; }
public override object ProvideValue(IServiceProvider serviceProvider)
{ return this; }
}
我见过使用数组传递几个参数的答案: multiple parameters not truly converted 和 multiple parameters order matters 和 a question asking for two parameters 。我见过的转换器无法使用多个命名参数,其中参数是一个类(无论如何它是一个对象),并且在 XAML 中具有该语法。
<ElementX Value="{av:Binding DD,Converter={Converters:ItemConverterGeneric},ConverterParameterKey='ItemXName',ConvertProperty='Name',ConvertSource='DatabaseX'}" .../>
<ElementX Value="{av:Binding DD,ConvertProperty='Value',ConvertSource='DatabaseX'}" .../>
使用 IValueConverter/MarkupExtension 的理由
字典是通过处理作为容器源的动态加载的 XAML 的内容动态构建的。 var tempLoad = XamlReader.Load(Fs);
使用转换器可以解决不存在的键、带有特殊字符的键以及必须依赖字符串解析 Binding b.Path.Path
与 Binding b.ConverterParameter
的内容的问题,因为后者只是 {{1} }.
当然其他人已经将字典或表格映射到众多(数百个)单个 FrameworkElement / Control / Custom Elements 并遇到了这个问题......
有没有办法让 key
扩展 XAML 语法并转换属性?
解决方法
虽然提供的答案有一些有趣的可能性,但它们依赖于反射和运行时编译器服务。
我最终在下面想到的没有。
我误解了基于 MarkupExtension
/ IValueConverter
的类如何将其属性引入 Converter 中的 XAML 而不是 ConverterParameter。此外,在我的情况下,我似乎不需要参数(尽管我真的很想看到一个使用 Converter 和 ConverterParameter 的示例)。
MarkupExtension/IValueConverter 应该在里面指定了命名属性:
[ValueConversion(typeof(DictionaryData),typeof(object))]
public class ItemConverterGeneric : MarkupExtension,IValueConverter {
public string Path {get; set;}
public string Property {get; set;}
public string Source {get; set;}
public object Convert(object value,Type targetType,object parameter,CultureInfo culture) {
try {
if !(value == null || string.IsNullorEmpty(Path) || string.IsNullorEmpty(Property) || string.IsNullorEmpty(Source)) {
DictionaryData dict = value as DictionaryData;
// These are hard coded,but you could use refelction to make it look nicer
// and automatically handle any new properties added to the class.
if (dict.ContainsKey(Path)) {
switch(Property.ToUpper()) {
case "SOURCE": return dict[Path].Source;
case "NAME": return dict[Path].Name;
case "QUALITY": return dict[Path].Quality;
case "VALUE: return dict[Path].Value;
//... etc and no default: needed as the outer return will handle it.
}
}
}
return Binding.DoNothing;
} catch (Exception ex) {
// Console.WriteLine(ex.Message); // or log the error
return Binding.DoNothing;
}
}
public object ConvertBack(object value,Type targetTypes,CultureInfo culture)
{ return DependencyProperty.UnsetValue; }
public override object ProvideValue(IServiceProvider serviceProvider)
{ return this; }
}
虽然我原来的 XAML 语法很接近,但第一个之后的每个属性都应该在用逗号分隔的转换器中指定:
<ElementX Value="{av:Binding DD,Converter={Converters:ItemConverterGeneric
Path='ItemXName001',Property='Value',Source='DataSourceX'}}" .../>
<!-- hundreds of DataSourceX bound items -->
<ElementX Value="{av:Binding DD,Converter={Converters:ItemConverterGeneric
Path='ItemXName401',Source='DataSourceX'}}" .../>
<ElementY Value="{av:Binding DD,Converter={Converters:ItemConverterGeneric
Path='ItemYName001',Source='DataSourceY'}}" .../>
<!-- hundreds of DataSourceY bound items -->
<ElementY Value="{av:Binding DD,Converter={Converters:ItemConverterGeneric
Path='ItemYName401',Source='DataSourceY'}}" .../>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。