如何在 DataGridTemplateColumn 内的控件上双向绑定属性?
Posted
技术标签:
【中文标题】如何在 DataGridTemplateColumn 内的控件上双向绑定属性?【英文标题】:How to bind a property both ways on a control inside a DataGridTemplateColumn? 【发布时间】:2021-11-26 10:36:50 【问题描述】:我有一个 ObservableCollection
的 DataPoint
对象。该类具有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>
数字框.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;
【问题讨论】:
如果你尝试<BindingTest:NumberBox Value="Binding Data, Mode=TwoWay"/>
会怎样?
@CorentinPane 我已经尝试过了
【参考方案1】:
我错过了什么?
实际上有很多东西。
依赖属性的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
中,@ 987654330@应该设置为PropertyChanged
:
<local:NumberBox x:Name="nb" Value="Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged"/>
【讨论】:
这主要是可行的,但是绑定文本是有问题的,因为它似乎与解析混淆。当我输入一个十进制数字时,它似乎使用英文数字格式,这不是我的系统设置的方式。 (我已经完全删除了 PreviewTextInput 事件,所以这不是导致问题的那一行)。【参考方案2】:mm8's answer 为我指明了正确的方向。
确实缺少两个关键要素:
更改 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)));
我已经在我的测试项目中对此进行了测试,然后再次测试了与我的真实项目的集成。据我所知,它是有水的,所以我会考虑回答这个问题。
【讨论】:
以上是关于如何在 DataGridTemplateColumn 内的控件上双向绑定属性?的主要内容,如果未能解决你的问题,请参考以下文章
如何在异步任务中调用意图?或者如何在 onPostExecute 中开始新的活动?