Search code examples
wpfdata-binding

dataBinding a UserControl to data in DataGrid


I have a lilltle issue using Dependency Property for binding data;

I have a user control which contains a textBox. I created a DataGrid, in which I added the usercontrol for one of its columns. Then i did a binding using dataContext.

When I first run the apllication, the usercontrol is filled with data (Data binded to a specific field 'name' in my case), But when I modify the value, the new value is not written, it still holds the old value.

TT.xaml.cs

namespace WPF_Prj.View
{
    public partial class TT : UserControl
    {
        public float MyText
        {
            get { 
                return (float)GetValue(MyTextProperty); 
            }
            set { 
                SetValue(MyTextProperty, value ); 
            }
        }

    public static readonly DependencyProperty MyTextProperty =
        DependencyProperty.Register("MyText", typeof(float), typeof(TT), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });

    public TT()
    {
        InitializeComponent();
    }
}

}

TT.xaml

<UserControl x:Class="WPF_Prj.View.TT"
         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="300" d:DesignWidth="300">
<Grid>
    <TextBox Height="20" Width="100" 
                Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, 
                Path=MyText, 
                UpdateSourceTrigger=PropertyChanged}">
    </TextBox>
</Grid>

mainWindow.xaml

<Window x:Class="WPF_Prj.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:col="clr-namespace:System.Collections;assembly=mscorlib"
    xmlns:local="clr-namespace:WPF_Prj.View"
    Title="[Portfolio] MainWindow" Height="500" Width="800">

<StackPanel Orientation="Vertical">

    <DataGrid x:Name="datagrid" AutoGenerateColumns="False" CanUserAddRows="False"
            Width="Auto" Margin="5,5,0,5" HorizontalAlignment="Left" CellEditEnding="datagrid_CellEndEditing">
        <DataGrid.Columns>
            <DataGridTextColumn Header=""                                                                                            IsReadOnly="True"  Width="1"/>
            <DataGridTextColumn Header="Name"           Binding="{Binding Name, Mode=TwoWay, NotifyOnTargetUpdated=True}"            IsReadOnly="True"  Width="Auto"/>
            <DataGridTextColumn Header="Owned Qty"      Binding="{Binding OwnedQty, Mode=TwoWay, NotifyOnTargetUpdated=True}"        IsReadOnly="True"  Width="Auto"/>
            <DataGridTemplateColumn Header="Ordered/Eligible">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <local:TT x:Name="test" MyText="{Binding OwnedQty, Mode=TwoWay}"></local:TT>
                    </DataTemplate> 
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    </StackPanel>

in the mainWindow.xaml.cs I get data from a ViewModel class which implements ObservableCollection and the Model is a class implementing INotifyPropertyChanged

So in the picture below : the green box is what is loaded from my DB to the grid, in the red is my UserControl which hold and edit, at first it contains the values loaded, but when I change it to some other values, the values in the Green still unchanged.

enter image description here


Solution

  • This fixes it for me. You don't need Mode=TwoWay; it's already that by default in the dependency property definition. You do need UpdateSourceTrigger=PropertyChanged, whether that's in the DP definition or not.

    <DataTemplate>
        <local:TT 
            x:Name="test" 
            MyText="{Binding OwnedQty, UpdateSourceTrigger=PropertyChanged}"
            ></local:TT>
    </DataTemplate>
    

    I would expect to be able to do the same thing by initializing DefaultUpdateSourceTrigger in the Dependency Property definition, but this does not have the same effect. I don't know why that is the case.

    Note that I also changed the default value to 0F; null will throw an exception.

    public static readonly DependencyProperty MyTextProperty =
        DependencyProperty.Register("MyText", typeof(float), typeof(TT), 
            new FrameworkPropertyMetadata(0F) {
                BindsTwoWayByDefault = true
                //  Nope. 
                //, DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            });
    

    This is my viewmodel property. I'm putting a breakpoint on OnPropertyChanged() to detect when the property is updated by the binding.

    private float _ownedQty = 0F;
    public float OwnedQty
    {
        get { return _ownedQty; }
        set
        {
            if (value != _ownedQty)
            {
                _ownedQty = value;
                OnPropertyChanged();
            }
        }
    }