如何在 DataGridTemplateColumn 内的控件上双向绑定 属性?
How to bind a property both ways on a control inside a DataGridTemplateColumn?
我有 ObservableCollection
个 DataPoint
个对象。 class 有一个 Data
属性。我有一个DataGrid
。我有一个名为 NumberBox
的自定义 UserControl,它是 TextBox
的包装器,但对于数字,具有可绑定的 Value
属性(或至少是预期的)成为)。
我希望 DataGrid
在 NumberBox
列中显示我的 Data
,以便可以显示和更改值。我使用了 DataGridTemplateColumn
,将 Value
属性 绑定到 Data
,将 ItemsSource
设置为我的 ObservableCollection
.
添加或修改基础 Data
时,NumberBox
更新得很好。但是,当我在框中输入一个值时,Data
不会更新。
我找到了建议实施 INotifyPropertyChanged
的答案。首先,不确定我应该实施什么。其次,我尝试在 DataPoint
和 NumberBox
上实现它 thusly。我找到了建议将 Mode=TwoWay, UpdateSourceTrigger=PropertyChange
添加到我的 Value
绑定的答案。我已经分别和一起尝试过这两种方法。显然,问题依旧。
下面是我目前正在使用的最低限度的项目,试图让这个东西工作。我错过了什么?
MainWindow.xaml
<Window xmlns:BindingTest="clr-namespace:BindingTest" x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="Container" AutoGenerateColumns="False" CanUserSortColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserReorderColumns="False" CanUserAddRows="False" CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Sample Text" Width="100" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<BindingTest:NumberBox Value="{Binding Data}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Name="BTN" Click="Button_Click" Height="30" VerticalAlignment="Bottom" Content="Check"/>
</Grid>
</Window>
MainWindow.cs
public partial class MainWindow : Window
{
private ObservableCollection<DataPoint> Liste { get; set; }
public MainWindow()
{
InitializeComponent();
Liste = new ObservableCollection<DataPoint>();
Container.ItemsSource = Liste;
DataPoint dp1 = new DataPoint(); dp1.Data = 1;
DataPoint dp2 = new DataPoint(); dp2.Data = 2;
Liste.Add(dp1);
Liste.Add(dp2);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
BTN.Content = Liste[0].Data;
}
}
DataPoint.cs
public class DataPoint
{
public double Data { get; set; }
}
NumberBox.xaml
<UserControl x:Class="BindingTest.NumberBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="28" d:DesignWidth="200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Name="Container" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"/>
</Grid>
</UserControl>
NumberBox.cs
public partial class NumberBox : UserControl
{
public event EventHandler ValueChanged;
public NumberBox()
{
InitializeComponent();
}
private double _value;
public double Value
{
get { return _value; }
set
{
_value = value;
Container.Text = value.ToString(CultureInfo.InvariantCulture);
if (ValueChanged != null) ValueChanged(this, new EventArgs());
}
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
typeof(double),
typeof(NumberBox),
new PropertyMetadata(OnValuePropertyChanged)
);
public static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double? val = e.NewValue as double?;
(d as NumberBox).Value = val.Value;
}
}
What am I missing?
实际上有很多东西。
依赖项 属性 的 CLR 属性 应该 仅 获取和设置依赖项 属性 的值:
public partial class NumberBox : UserControl
{
public NumberBox()
{
InitializeComponent();
}
public double Value
{
get => (double)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
typeof(double),
typeof(NumberBox),
new PropertyMetadata(0.0)
);
private void Container_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
Value = double.Parse(Container.Text, CultureInfo.InvariantCulture);
}
}
控件中的 TextBox
应该绑定到 Value
属性:
<TextBox Name="Container" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"
Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}"
PreviewTextInput="Container_PreviewTextInput"/>
Value
和Data
属性之间的绑定模式应该设置为TwoWay
也是,这是因为输入控件在DataGrid
的CellTemplate
中,UpdateSourceTrigger
应该设置为PropertyChanged
:
<local:NumberBox x:Name="nb" Value="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
给我指明了正确的方向。
确实缺少两个关键要素:
更改 NumberBox.xaml 中的 TextBox
例如
<TextBox Name="Container"
Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"
/>
并更改 MainWindow.xaml 中的绑定,例如
...
<DataTemplate>
<BindingTest:NumberBox
Value="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
...
通过这两项修改,Value
和 Data
都在我的 DataGridTemplaceColumn
.
中进行了双向更新
然而绑定到文本被证明是有问题的。无论您的系统如何设置,WPF 显然都使用 en-US 文化,这真的不好,因为此控件处理数字。使用 this trick 解决了这个问题,现在可以识别正确的小数点分隔符。就我而言,我已将它添加到静态构造函数中以获得相同的效果,因此 NumberBox 可以按原样用于其他应用程序。
static NumberBox()
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
我已经在我的测试项目中对此进行了测试,然后再次测试了与我的真实项目的集成。据我所知,它是成立的,所以我会考虑回答这个问题。
我有 ObservableCollection
个 DataPoint
个对象。 class 有一个 Data
属性。我有一个DataGrid
。我有一个名为 NumberBox
的自定义 UserControl,它是 TextBox
的包装器,但对于数字,具有可绑定的 Value
属性(或至少是预期的)成为)。
我希望 DataGrid
在 NumberBox
列中显示我的 Data
,以便可以显示和更改值。我使用了 DataGridTemplateColumn
,将 Value
属性 绑定到 Data
,将 ItemsSource
设置为我的 ObservableCollection
.
添加或修改基础 Data
时,NumberBox
更新得很好。但是,当我在框中输入一个值时,Data
不会更新。
我找到了建议实施 INotifyPropertyChanged
的答案。首先,不确定我应该实施什么。其次,我尝试在 DataPoint
和 NumberBox
上实现它 thusly。我找到了建议将 Mode=TwoWay, UpdateSourceTrigger=PropertyChange
添加到我的 Value
绑定的答案。我已经分别和一起尝试过这两种方法。显然,问题依旧。
下面是我目前正在使用的最低限度的项目,试图让这个东西工作。我错过了什么?
MainWindow.xaml
<Window xmlns:BindingTest="clr-namespace:BindingTest" x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid Name="Container" AutoGenerateColumns="False" CanUserSortColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserReorderColumns="False" CanUserAddRows="False" CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Sample Text" Width="100" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<BindingTest:NumberBox Value="{Binding Data}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button Name="BTN" Click="Button_Click" Height="30" VerticalAlignment="Bottom" Content="Check"/>
</Grid>
</Window>
MainWindow.cs
public partial class MainWindow : Window
{
private ObservableCollection<DataPoint> Liste { get; set; }
public MainWindow()
{
InitializeComponent();
Liste = new ObservableCollection<DataPoint>();
Container.ItemsSource = Liste;
DataPoint dp1 = new DataPoint(); dp1.Data = 1;
DataPoint dp2 = new DataPoint(); dp2.Data = 2;
Liste.Add(dp1);
Liste.Add(dp2);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
BTN.Content = Liste[0].Data;
}
}
DataPoint.cs
public class DataPoint
{
public double Data { get; set; }
}
NumberBox.xaml
<UserControl x:Class="BindingTest.NumberBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="28" d:DesignWidth="200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Name="Container" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"/>
</Grid>
</UserControl>
NumberBox.cs
public partial class NumberBox : UserControl
{
public event EventHandler ValueChanged;
public NumberBox()
{
InitializeComponent();
}
private double _value;
public double Value
{
get { return _value; }
set
{
_value = value;
Container.Text = value.ToString(CultureInfo.InvariantCulture);
if (ValueChanged != null) ValueChanged(this, new EventArgs());
}
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
typeof(double),
typeof(NumberBox),
new PropertyMetadata(OnValuePropertyChanged)
);
public static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double? val = e.NewValue as double?;
(d as NumberBox).Value = val.Value;
}
}
What am I missing?
实际上有很多东西。
依赖项 属性 的 CLR 属性 应该 仅 获取和设置依赖项 属性 的值:
public partial class NumberBox : UserControl { public NumberBox() { InitializeComponent(); } public double Value { get => (double)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(double), typeof(NumberBox), new PropertyMetadata(0.0) ); private void Container_PreviewTextInput(object sender, TextCompositionEventArgs e) { Value = double.Parse(Container.Text, CultureInfo.InvariantCulture); } }
控件中的
TextBox
应该绑定到Value
属性:<TextBox Name="Container" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center" Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}" PreviewTextInput="Container_PreviewTextInput"/>
Value
和Data
属性之间的绑定模式应该设置为TwoWay
也是,这是因为输入控件在DataGrid
的CellTemplate
中,UpdateSourceTrigger
应该设置为PropertyChanged
:<local:NumberBox x:Name="nb" Value="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
确实缺少两个关键要素:
更改 NumberBox.xaml 中的 TextBox
例如
<TextBox Name="Container"
Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"
/>
并更改 MainWindow.xaml 中的绑定,例如
...
<DataTemplate>
<BindingTest:NumberBox
Value="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
...
通过这两项修改,Value
和 Data
都在我的 DataGridTemplaceColumn
.
然而绑定到文本被证明是有问题的。无论您的系统如何设置,WPF 显然都使用 en-US 文化,这真的不好,因为此控件处理数字。使用 this trick 解决了这个问题,现在可以识别正确的小数点分隔符。就我而言,我已将它添加到静态构造函数中以获得相同的效果,因此 NumberBox 可以按原样用于其他应用程序。
static NumberBox()
{
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
我已经在我的测试项目中对此进行了测试,然后再次测试了与我的真实项目的集成。据我所知,它是成立的,所以我会考虑回答这个问题。