绑定到 WPF 中的可为 null 的数据类型
Binding to a nullable data type in WPF
好吧,WPF
的这个小怪癖让我很反感。所以,我有一个像这样的 DataGrid
:
<DataGrid x:Name="Rates" AlternatingRowBackground="#FCFCFC" AutoGenerateColumns="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserAddRows="False" SelectionUnit="FullRow" CanUserSortColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Width="Auto" IsReadOnly="True" Binding="{Binding item}"/>
<DataGridTextColumn Header="Rate" Width ="*" Binding="{Binding rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
它只是在 A 列中有一个项目列表,我们必须在 B 列中输入费率。听起来很简单。
这是ViewModel
中绑定的属性。
private BindableCollection<Rate> _rates;
public BindableCollection<Rate> Rates
{
get
{
return _rates;
}
set
{
if (value == _rates)
{
return;
}
_rates = value;
NotifyOfPropertyChange();
}
}
这里是 Rate
的定义:
public partial class Rate
{
public int ID { get; set; }
public string client { get; set; }
public string item { get; set; }
public Nullable<decimal> rate { get; set; }
}
因此,我的 DataGrid
的第二列正在绑定到 Rate.rate
,即 Nullable<decimal>
。问题就出在这里。 Nullable<decimal>
确实接受 NULL
但 TextBox
不允许我输入空白值。此外,如果我输入像 5 这样的值然后删除它,ViewModel
会保留以前的值。
我见过涉及 IValueConverter
的解决方案,但我更喜欢基于 XAML 的解决方案。有没有?我有几个模块,其中 DataType
是 Nullable
,如果我每次都必须使用 IValueConverter
,它很快就会变得非常乏味。
编辑
我正在使用 Caliburn.Micro
,因此使用 TargetNullValue=''
不会在任何地方都有效,因为绑定是自动的。它会在上面的示例中工作,其中明确提到了 binding
。
嗯,在 Caliburn
的 ConventionManager
中自定义绑定是正确的选择。
解决方法如下:
https://github.com/Caliburn-Micro/Caliburn.Micro/issues/448
如果您正在使用 Caliburn.Micro,您可以配置 CM 在您的 View 和 ViewModel 之间应用自动绑定的方式。
所以在 Bootstrapper
类的 Configure
方法中你可以这样写:
protected override void Configure()
{
ConventionManager.SetBinding = delegate(Type viewModelType, string path, PropertyInfo property, FrameworkElement element, ElementConvention convention, DependencyProperty bindableProperty)
{
Binding binding = new Binding(path);
ConventionManager.ApplyBindingMode(binding, property);
ConventionManager.ApplyValueConverter(binding, bindableProperty, property);
ConventionManager.ApplyStringFormat(binding, convention, property);
ConventionManager.ApplyValidation(binding, viewModelType, property);
ConventionManager.ApplyUpdateSourceTrigger(bindableProperty, element, binding, property);
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
{
binding.TargetNullValue = String.Empty;
}
BindingOperations.SetBinding(element, bindableProperty, binding);
};
// Your own configurations...
}
如您所见,如果源 属性 是 Nullable
,此代码会设置 Binding
的 TargetNullValue
属性。它适用于 CM 在您的应用程序中为您创建的所有自动绑定。
希望对您有所帮助。
编辑
关于你最后的评论,考虑到 Caliburn Micro 在应用 [=40] 时不处理已经绑定(在 XAML 中)到 属性 =].
因此,如果您想自动设置 "manual" 绑定的 TargetNullValue
,那么最简单的想法就是创建您自己的绑定:
public class NullableBinding : Binding
{
public NullableBinding(string path)
: base(path)
{
TargetNullValue = String.Empty;
}
public NullableBinding()
: base()
{
TargetNullValue = String.Empty;
}
}
然后在您的 XAML:
中使用它
<DataGridTextColumn Header="Rate" Width ="*" Binding="{local:NullableBinding rate}"/>
好吧,WPF
的这个小怪癖让我很反感。所以,我有一个像这样的 DataGrid
:
<DataGrid x:Name="Rates" AlternatingRowBackground="#FCFCFC" AutoGenerateColumns="False" CanUserReorderColumns="True" CanUserResizeColumns="True" CanUserAddRows="False" SelectionUnit="FullRow" CanUserSortColumns="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Width="Auto" IsReadOnly="True" Binding="{Binding item}"/>
<DataGridTextColumn Header="Rate" Width ="*" Binding="{Binding rate, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
它只是在 A 列中有一个项目列表,我们必须在 B 列中输入费率。听起来很简单。
这是ViewModel
中绑定的属性。
private BindableCollection<Rate> _rates;
public BindableCollection<Rate> Rates
{
get
{
return _rates;
}
set
{
if (value == _rates)
{
return;
}
_rates = value;
NotifyOfPropertyChange();
}
}
这里是 Rate
的定义:
public partial class Rate
{
public int ID { get; set; }
public string client { get; set; }
public string item { get; set; }
public Nullable<decimal> rate { get; set; }
}
因此,我的 DataGrid
的第二列正在绑定到 Rate.rate
,即 Nullable<decimal>
。问题就出在这里。 Nullable<decimal>
确实接受 NULL
但 TextBox
不允许我输入空白值。此外,如果我输入像 5 这样的值然后删除它,ViewModel
会保留以前的值。
我见过涉及 IValueConverter
的解决方案,但我更喜欢基于 XAML 的解决方案。有没有?我有几个模块,其中 DataType
是 Nullable
,如果我每次都必须使用 IValueConverter
,它很快就会变得非常乏味。
编辑
我正在使用 Caliburn.Micro
,因此使用 TargetNullValue=''
不会在任何地方都有效,因为绑定是自动的。它会在上面的示例中工作,其中明确提到了 binding
。
嗯,在 Caliburn
的 ConventionManager
中自定义绑定是正确的选择。
解决方法如下: https://github.com/Caliburn-Micro/Caliburn.Micro/issues/448
如果您正在使用 Caliburn.Micro,您可以配置 CM 在您的 View 和 ViewModel 之间应用自动绑定的方式。
所以在 Bootstrapper
类的 Configure
方法中你可以这样写:
protected override void Configure()
{
ConventionManager.SetBinding = delegate(Type viewModelType, string path, PropertyInfo property, FrameworkElement element, ElementConvention convention, DependencyProperty bindableProperty)
{
Binding binding = new Binding(path);
ConventionManager.ApplyBindingMode(binding, property);
ConventionManager.ApplyValueConverter(binding, bindableProperty, property);
ConventionManager.ApplyStringFormat(binding, convention, property);
ConventionManager.ApplyValidation(binding, viewModelType, property);
ConventionManager.ApplyUpdateSourceTrigger(bindableProperty, element, binding, property);
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
{
binding.TargetNullValue = String.Empty;
}
BindingOperations.SetBinding(element, bindableProperty, binding);
};
// Your own configurations...
}
如您所见,如果源 属性 是 Nullable
,此代码会设置 Binding
的 TargetNullValue
属性。它适用于 CM 在您的应用程序中为您创建的所有自动绑定。
希望对您有所帮助。
编辑
关于你最后的评论,考虑到 Caliburn Micro 在应用 [=40] 时不处理已经绑定(在 XAML 中)到 属性 =].
因此,如果您想自动设置 "manual" 绑定的 TargetNullValue
,那么最简单的想法就是创建您自己的绑定:
public class NullableBinding : Binding
{
public NullableBinding(string path)
: base(path)
{
TargetNullValue = String.Empty;
}
public NullableBinding()
: base()
{
TargetNullValue = String.Empty;
}
}
然后在您的 XAML:
中使用它<DataGridTextColumn Header="Rate" Width ="*" Binding="{local:NullableBinding rate}"/>