Silverlight Data Grid twoway Data binding in code

data-bindingdatagridsilverlighttwo-way-binding

I have a DataGrid with two way binding and not sure why this does not work, any help would be appreciated.

I wanted to dynamically bind to the DataGrid using a twoway binding object.

I used the columns in XAML. If I just set the 'ItemSource" property directly – it works but then the two binding doesn't work – if I change my source in code the Grid doesn't reflect that change.

I created a simple sample to illustrate my setup

Here is the XAML

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="356" d:DesignWidth="590" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White"> 
        <sdk:DataGrid AutoGenerateColumns="False" Height="136" HorizontalAlignment="Left" Margin="71,116,0,0" Name="MyGrid" VerticalAlignment="Top" Width="453" >
            <sdk:DataGrid.Columns>
                <sdk:DataGridTextColumn Binding="{Binding Path=Label, Mode=TwoWay}" CanUserReorder="True" CanUserResize="True" CanUserSort="True" Width="Auto" Header="Selected" />
                <sdk:DataGridTextColumn Binding="{Binding Path=YValue, Mode=TwoWay}" CanUserReorder="True" CanUserResize="True" CanUserSort="True" Header="Name" Width="*" />
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
    </Grid>
</UserControl>

Here is the code behind

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent(); 
            ObservableCollection<Value> values = new ObservableCollection<Value>(); 
            values.Add(new Value() { Label = "Sony", YValue = 50 });
            values.Add(new Value() { Label = "Dell", YValue = 35 });
            values.Add(new Value() { Label = "HP", YValue = 27 });
            values.Add(new Value() { Label = "HCL", YValue = 17 });
            values.Add(new Value() { Label = "Toshiba", YValue = 16 });

            PagedCollectionView p = new PagedCollectionView(values); 

            Binding b = new Binding("ValuesBinding");
            b.Mode = BindingMode.TwoWay;
            b.Source = values;
            MyGrid.SetBinding(DataGrid.ItemsSourceProperty, b);  
        }
    }

    public class Value : INotifyPropertyChanged
    {
        public String Label
        {
            get
            { return _label; }
            set
            {
                _label = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Label"));
            }
        }
         public Double YValue
        {
            get
            {return _yValue;}
            set
            {
                _yValue = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("YValue"));
            }
        }
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        Double _yValue;
        String _label;
    } 

}

Best Answer

There are a couple of problems that I can see here.

The first line that you use to create your binding is

Binding b = new Binding("ValuesBinding");

This won't do what you want. The string ValuesBinding is being used as a property-path, and the ObservableCollection you're binding the DataGrid to has no property on it named ValuesBinding. Indeed, if you look in the VS Output window, you should see a message such as

System.Windows.Data Error: BindingExpression path error: 'ValuesBinding' property not found on 'System.Collections.ObjectModel.ObservableCollection`1 ...

However, if you remove "ValuesBinding" from the above to leave you with

Binding b = new Binding();

then you get an error about two-way bindings needing a Path. However, you don't need a two-way binding here. You can simply remove the line b.Mode = BindingMode.TwoWay; and the error goes away.

Two-way bindings are used to allow the view-layer to set properties in the view-model layer. The Path specifies where to find the view-model property to set. However, since you're binding straight to a collection, there's no property involved and hence nothing that the view-layer could set.

In your case, this binding doesn't need to be two-way. Changes to the collection itself (e.g. adding or removing items) can still be made, even when using a one-way binding for the ItemsSource. The two-way bindings you have on the Label and YValue properties of your Value class will also work as you expect. Setting a one-way binding on the DataGrid's ItemsSource doesn't make the whole grid read-only.

Finally, I'm not sure why you're creating a binding in code-behind to bind to a collection already available in the code-behind. You can achieve the same by just writing

MyGrid.ItemsSource = values;