I have an ObservableCollection of DataPoint objects. That class has a Data property. I have a DataGrid. I have a custom UserControl called NumberBox, which is a wrapper for a TextBox but for numbers, with a Value property that is bind-able (or at least is intended to be).
I want the DataGrid to display my Data in a column NumberBox, so the value can be displayed and changed. I've used a DataGridTemplateColumn, bound the Value property to Data, set the ItemsSource to my ObservableCollection.
When the underlying Data is added or modified, the NumberBox updates just fine. However, when I input a value in the box, the Data doesn't update.
I've found answers suggesting to implement INotifyPropertyChanged. Firstly, not sure on what I should implement it. Secondly, I tried to implement it thusly on both my DataPoint and my NumberBox. I've found answers suggesting to add Mode=TwoWay, UpdateSourceTrigger=PropertyChange to my Value binding. I've tried both of these, separately, and together. Obviously, the problem remains.
Below is the bare minimum project I'm currently using to try to make this thing work. What am I missing?
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;
}
}