由于目标类型,在 WPF 中链接 IValueConverter 时出现问题
Issue in chaining IValueConvertes in WPF becauase of target type
我试图将转换器链接为 Town 在 Is there a way to chain multiple value converters in XAML??
中的回答
我也喜欢通过检查 targetType 来使单个转换器更严格:-
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a
boolean");
但是链失败了,因为最终目标类型与每个阶段的目标不同。
我可以删除类型检查以降低在大多数 SO 示例中给出的严格程度,但我更喜欢一个也尊重每个转换器类型检查的链接。例如。为了更好的单元测试等
此外,IValueConverter 接口没有公开目标类型,我发现自己很难添加该检查。
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
if (!(value is bool))
throw new ArgumentException("Argument 'value' must be of type bool");
return !(bool)value;
}
....
}
[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityFromBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a Visibility");
if (!(value is bool))
throw new ArgumentException("Argument 'value' must be of type bool");
var isVisible = (bool)value;
return isVisible ? Visibility.Visible : Visibility.Collapsed;
}
....
}
复合就像:-
<Converters:ValueConverterGroup x:Key="InvertAndVisible">
<Converters:InverseBooleanConverter />
<Converters:VisibilityFromBoolConverter />
</Converters:ValueConverterGroup>
但我从 InverseBooleanConverter 得到异常 "The target must be a boolean",因为它期望目标是 bool 而不是 Visibility(链的最终目标)。
我想你可能误解了targetType
参数。 According to the doco,它是 target 绑定的类型 属性。
这意味着对于您的 InverseBooleanConverter,目标类型将是 System.Visibility
类型。
对于这种检查,您应该检查传入(绑定)对象的类型:
if (value != null && value.GetType() != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
但是.... 我强烈建议您不要从您的转换器中抛出异常 - 这会减慢 UI 渲染的速度,并且它们可能会令人难以置信当你有一屏好东西时很难追踪(例如,你有一个包含几千行的网格,其中一个模板化数据网格单元格抛出异常 - 你将如何识别它?)。如果您坚持抛出异常,那么至少用 #if DEBUG
定义将其包围,这样它就不会出现在您的发布代码中。相反,你应该 return DependencyProperty.UnsetValue if your converter cannot successfully convert a value. This ensures you don't get hard to track runtime exceptions, and it also ensures the binding subsystem can use things like the FallbackValue.
原始 ValueConverterGroup 代码将最终的 targetType 传递到每个阶段,这就是您的检查失败的原因。您需要做的就是修改该行为以改为传递下一个转换器 targetType:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
for (int i = 0; i < this.Count(); i++)
{
var targ = (i == this.Count() - 1)
? targetType
: (this[i + 1].GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).SourceType;
value = this[i].Convert(value, targ, parameter, culture);
}
if (value.GetType() != (this.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).TargetType)
throw new InvalidOperationException("Last target must be of type " + targetType.Name);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
我试图将转换器链接为 Town 在 Is there a way to chain multiple value converters in XAML??
中的回答我也喜欢通过检查 targetType 来使单个转换器更严格:-
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a
boolean");
但是链失败了,因为最终目标类型与每个阶段的目标不同。
我可以删除类型检查以降低在大多数 SO 示例中给出的严格程度,但我更喜欢一个也尊重每个转换器类型检查的链接。例如。为了更好的单元测试等
此外,IValueConverter 接口没有公开目标类型,我发现自己很难添加该检查。
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
if (!(value is bool))
throw new ArgumentException("Argument 'value' must be of type bool");
return !(bool)value;
}
....
}
[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityFromBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(Visibility))
throw new InvalidOperationException("The target must be a Visibility");
if (!(value is bool))
throw new ArgumentException("Argument 'value' must be of type bool");
var isVisible = (bool)value;
return isVisible ? Visibility.Visible : Visibility.Collapsed;
}
....
}
复合就像:-
<Converters:ValueConverterGroup x:Key="InvertAndVisible">
<Converters:InverseBooleanConverter />
<Converters:VisibilityFromBoolConverter />
</Converters:ValueConverterGroup>
但我从 InverseBooleanConverter 得到异常 "The target must be a boolean",因为它期望目标是 bool 而不是 Visibility(链的最终目标)。
我想你可能误解了targetType
参数。 According to the doco,它是 target 绑定的类型 属性。
这意味着对于您的 InverseBooleanConverter,目标类型将是 System.Visibility
类型。
对于这种检查,您应该检查传入(绑定)对象的类型:
if (value != null && value.GetType() != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
但是.... 我强烈建议您不要从您的转换器中抛出异常 - 这会减慢 UI 渲染的速度,并且它们可能会令人难以置信当你有一屏好东西时很难追踪(例如,你有一个包含几千行的网格,其中一个模板化数据网格单元格抛出异常 - 你将如何识别它?)。如果您坚持抛出异常,那么至少用 #if DEBUG
定义将其包围,这样它就不会出现在您的发布代码中。相反,你应该 return DependencyProperty.UnsetValue if your converter cannot successfully convert a value. This ensures you don't get hard to track runtime exceptions, and it also ensures the binding subsystem can use things like the FallbackValue.
原始 ValueConverterGroup 代码将最终的 targetType 传递到每个阶段,这就是您的检查失败的原因。您需要做的就是修改该行为以改为传递下一个转换器 targetType:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
for (int i = 0; i < this.Count(); i++)
{
var targ = (i == this.Count() - 1)
? targetType
: (this[i + 1].GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).SourceType;
value = this[i].Convert(value, targ, parameter, culture);
}
if (value.GetType() != (this.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false).First() as ValueConversionAttribute).TargetType)
throw new InvalidOperationException("Last target must be of type " + targetType.Name);
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}