0

我有一个ObservableCollection对象DataPoint。该类有一个Data属性。我有一个DataGrid. 我有一个名为 的自定义 UserControl NumberBox,它是数字的包装器TextBox,具有Value可绑定的属性(或至少是可绑定的)。

我希望在列DataGrid中显示我的值,以便可以显示和更改值。我使用了 a ,将属性绑定到,将 设置为 my 。DataNumberBoxDataGridTemplateColumnValueDataItemsSourceObservableCollection

当底层证券Data被添加或修改时,NumberBox更新就好了。但是,当我在框中输入值时,Data不会更新。

我找到了建议实施的答案INotifyPropertyChanged。首先,不确定我应该实施什么。其次,我试图在 myDataPointmy NumberBox. 我找到了建议添加Mode=TwoWay, UpdateSourceTrigger=PropertyChange到我的Value绑定的答案。我已经尝试过这两种方法,分别和一起。显然,问题仍然存在。

下面是我目前用来尝试使这个东西工作的最小项目。我错过了什么?

主窗口.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>

主窗口.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;
    }
}

数据点.cs

public class DataPoint
{
    public double Data { get; set; }
}

数字框.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;
    }
}
4

2 回答 2

1

我错过了什么?

其实不少东西。

  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);
         }
     }
    
  2. 控件中的TextBox应该绑定到Value属性:

     <TextBox Name="Container" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Center"
              Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}"
              PreviewTextInput="Container_PreviewTextInput"/>
    
  3. Value和属性的绑定模式Data应该设置为TwoWayand,这是因为输入控件在的CellTemplateDataGridUpdateSourceTrigger应该设置为PropertyChanged

     <local:NumberBox x:Name="nb" Value="{Binding Data, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    
于 2021-10-06T14:57:11.273 回答
0

mm8 的回答为我指明了正确的方向。

确实缺少两个关键元素:更改TextBoxNumberBox.xaml 中的例如

<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>
...

通过这两个修改,ValueData都在我的DataGridTemplaceColumn.

然而,绑定到文本被证明是有问题的。无论您的系统如何设置,WPF 显然都使用 en-US 文化,这真的很糟糕,因为此控件处理数字。使用这个技巧可以解决这个问题,现在可以识别正确的小数分隔符。就我而言,我已将它添加到静态构造函数中以获得相同的效果,因此 NumberBox 可以按原样在其他应用程序中使用。

static NumberBox()
{
    FrameworkElement.LanguageProperty.OverrideMetadata(
        typeof(FrameworkElement),
        new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}

我已经在我的测试项目中对此进行了测试,然后再次测试了与我真实项目的集成。据我所知,它是有水的,所以我会考虑回答这个问题。

于 2021-10-08T08:38:55.443 回答