Datagrid binding, are conventions possible ?

Oct 19, 2010 at 3:32 PM
Edited Oct 19, 2010 at 3:36 PM

I was having trouble with the simplest of examples, trying to get a Silverlight 4.0 Datagrid to bind by convention, to a view-model's backing store of property:BindableCollection.

 

Are conventions possible with Datagrid?  I have had to resort to setting the ItemsSource="{Binding MyData}" , versus trying to use name convention off of the Datagrid ( where x:Name is the same as property name in my view-model)

view:

 <StackPanel>
        <TextBlock FontSize="32">Page One</TextBlock>
        <sdk:DataGrid x:Name="MyData" AutoGenerateColumns="True" />
    </StackPanel>

view-model:

public ObservableCollection<MyRow> MyData
        {
            get { return _myData; }
            set { _myData = value;
                NotifyOfPropertyChange(() => MyData);
            }
        }

Thanks.

Coordinator
Oct 19, 2010 at 3:45 PM

They are possible, but there is not convention set up for DataGrid by default, so you would have to add one. You can use the ConventionManager for that.

Oct 19, 2010 at 7:50 PM

The problem with the datagrid is that it has it's own datacontext (the ItemsSource). I found the below little gem, and while I would like to use its ideas to implement a ConventionManager approach for this use case, I am a brain cell or two short :). I did use it straight out in a non CM project and it worked flawlessly. Maybe it will help you:

http://www.scottlogic.co.uk/blog/colin/2009/02/relativesource-binding-in-silverlight/

Oct 19, 2010 at 8:40 PM
Edited Oct 19, 2010 at 9:24 PM

Hi!

I use these in my solutions:

   ConventionManager.Singularize = original => { return !original.EndsWith("ies") ? original.TrimEnd('s') : original.Replace("ies""y"); };
    ConventionManager.AddElementConvention<DataGrid>(DataGrid.ItemsSourceProperty, "SelectedItem""SelectionChanged")
        .ApplyBinding = (viewModelType, path, property, element, convention) =>
        {
            if (!ConventionManager.SetBinding(viewModelType, path, property, element, convention)) return;

            if (ConventionManager.HasBinding(element, DataGrid.SelectedItemProperty)) return;

            var index = path.LastIndexOf('.');
            index = index == -1 ? 0 : index + 1;
            var baseName = path.Substring(index);

            foreach (var potentialName in ConventionManager.DerivePotentialSelectionNames(baseName))
            {
                if (viewModelType.GetProperty(potentialName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null)
                {
                    var selectionPath = path.Replace(baseName, potentialName);
                    BindingOperations.SetBinding(element, DataGrid.SelectedItemProperty, new Binding(selectionPath) { Mode = BindingMode.TwoWay });
                    return;
                }
            }
        };
 
   ConventionManager.AddElementConvention<DataPager>(DataPager.SourceProperty, "DataContext""Loaded");
ViewModel:
        private PagedEntityCollection<TEntity> _entities;
        public PagedEntityCollection<TEntity> Entities
        {
            get
            {
                if (_entities == null)
                {
                    _entities = new PagedEntityCollection<TEntity>(DataCtx.EntityContainer.GetEntitySet<TEntity>(), this);
                    _entities.Refreshed += (s, e) => MoveToPage(PageIndex);
                }
                return _entities;
            }
            set
            {
                if (_entities == valuereturn;
                _entities = value;
                NotifyOfPropertyChange(() => Entities);
            }
        }
    private TEntity _selectedEntity;
    public TEntity SelectedEntity
    {
        get { return _selectedEntity; }
        set
        {
            if (_selectedEntity == valuereturn;
            _selectedEntity = value;
            NotifyOfPropertyChange(() => SelectedEntity);
        }
    }
XAML:
    <sdk:DataGrid Name="Entities" Margin="0,5,0,0"  VerticalAlignment="Top" HorizontalAlignment="Left"
                IsReadOnly="True" AutoGenerateColumns="False" Height="300" Width="300" >
        <sdk:DataGrid.Columns>
            <sdk:DataGridTextColumn Header="Name" Binding="{Binding CountryName}" />
            <sdk:DataGridTextColumn Header="Code2" Binding="{Binding CountryCode2}" />
            <sdk:DataGridTextColumn Header="Code3" Binding="{Binding CountryCode3}" />
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>
    <sdk:DataPager Name="Entities_" PageSize="10" HorizontalAlignment="Left"/>
Since I am using the "_"-trick (see more here) on the names they are both bound to the Entities collection.
Hope this helps!
Oct 19, 2010 at 8:53 PM

Hi!

What is missing from the above example is of course a nice way to also bind the columns by convention.

I have tried several approaches without being satisfied.

Anyone having some ideas here?