WPF understanding Selector.IsSynchronizedWithCurrentItem

Klaus Nji picture Klaus Nji · Aug 16, 2011 · Viewed 7.1k times · Source

Do not know if this is specific to the Infragistics xamDataGrid but here goes the question:

Infragistics xamDataGrid exposes a property IsSynchronizedWithCurrentItem, which according to their documentation, synchronizes ActiveRecord with current item of a datasource that implements ICollectionView.

I have the following MasterDetails window with details (ContentControl) content based on the type of objects bound to the grid:

 <DockPanel Name="dockPanel" LastChildFill="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="5" MaxHeight="5"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <igDP:XamDataGrid 
                Name="dataGrid"                                  
                IsSynchronizedWithCurrentItem="True"
                SelectedItemsChanged="dataGrid_SelectedItemsChanged">                    
            </igDP:XamDataGrid>
            <GridSplitter              
                Style="{StaticResource blueHrizontalGridSplitter}"
                Grid.Row="1" Grid.ColumnSpan="2"              
                BorderThickness="1" Margin="1,0"
                HorizontalAlignment="Stretch"   />            

            <ContentControl Grid.Row="2" Name="contentControl" />

        </Grid>
    </DockPanel>

In code behind, I am attempting to establish a link between the current item of the grid's data source to the DataContext of the details control in my MasterDetailsWindow's constructor as follows:

 if (detailsControl != null)
            {
                var fwDControl = detailsControl as FrameworkElement;
                if (fwDControl != null)
                {
                    var b = new Binding() { ElementName = "dataGrid", Path = new PropertyPath("DataSource") };
                    fwDControl.SetBinding(DataContextProperty, b);
                }

                contentControl.Content = detailsControl;                     
            }
            else
            {
                var b = new Binding() { ElementName = "dataGrid", Path = new PropertyPath("DataSource") };
                contentControl.SetBinding(ContentProperty, b);

                b = new Binding("DataDetailsTemplate");
                contentControl.SetBinding(ContentTemplateProperty, b);
            }  

When constructing a instance of the MasterDetails, the caller needs to provide either a detailsControl object or a string representing the URL to DataTemplate. If a detailsControl is provided, I execute code that checks if details is not null. Otherwise, I assume DataDetailsTemplate is provided instead.

I would have doubted my thinking here but if I construct an instance of the MasterDetails window, with a URL that resolves to the following dataTemplate:

<DataTemplate x:Key="LogDetailsTemplate">                 
        <Grid Margin="5,5,5,0">
            <TextBox Text="{Binding Message}" TextWrapping="WrapWithOverflow"/>            
        </Grid>     
    </DataTemplate>

selecting an item in the grid, displays the selected object's corresponding Message property in the TextBox.

However, if I provide a custom detailsControl object that derives from UserControl, selecting an item in the grid, does not cause change the DataContext of my detailsControl. Why is this?

TIA.

Answer

Mark Green picture Mark Green · Feb 8, 2012

Whoa there!!!!! I may be wrong but it looks like you've come from a WinForms background and are trying to to do things in WPF the way you would for WinForms.

The good news is, you don't have to: Master detail can be handled using a simple forwardslash. In the example below, look at the bindings in MainWindow.xaml - the forwardslash indicates the currently selected item.

MODELS

public class Country
{
    public string Name { get; set; }
    public int Population { get; set; }
}

public class Continent
{
    public string Name { get; set; }
    public int Area { get; set; }
    public IList<Country> Countries { get; set; }
}

VIEWMODELS

    public class MainViewModel
    {
        private ObservableCollection<ContinentViewModel> _continents;

        public ObservableCollection<ContinentViewModel> Continents
        {
            get { return _continents; }
            set
            {
            _continents = value;
            ContinentView = new ListCollectionView(_continents);
            ContinentView.CurrentChanged += (sender, agrs) => CurrentContinent = ContinentView.CurrentItem as ContinentViewModel;
        }
    }
    public ListCollectionView ContinentView {get; private set;}

    /// <summary>
    /// Use this to determine the current item in the list 
    /// if not willing to use \ notation in the binding.
    /// </summary>
    public ContinentViewModel CurrentContinent { get; set; }
}

public class ContinentViewModel
{
    private Continent _model;

    public Continent Model
    {
        get { return _model; }
        set
        {
            _model = value;
            Countries = _model.Countries
                .Select(p => new CountryViewModel { Model = p })
                .ToList();
        }
    }

    public string Name
    {
        get { return Model.Name; }
    }

    public int Area
    {
        get { return Model.Area; }
    }

    public List<CountryViewModel> Countries { get; private set; }
}

public class CountryViewModel
{
    public Country Model { get; set; }

    public string Name
    {
        get { return Model.Name; }
    }

    public int Population 
    {
        get { return Model.Population; }
    }
}

MainWindow.xaml

<Window x:Class="XamDataGridMasterDetail.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Views="clr-namespace:XamDataGridMasterDetail.Views"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        Title="MainWindow">
    <Grid>
        <StackPanel Orientation="Vertical">

            <!-- Continent list -->
            <igDP:XamDataGrid HorizontalAlignment="Left"
                              Margin="10,10,0,0"
                              Name="xamDataGrid1"
                              Height="300"
                              VerticalAlignment="Top"
                              DataSource="{Binding ContinentView}"
                              IsSynchronizedWithCurrentItem="True">
                <igDP:XamDataGrid.FieldSettings>
                    <igDP:FieldSettings  CellClickAction="SelectRecord" />
                </igDP:XamDataGrid.FieldSettings>
                <igDP:XamDataGrid.FieldLayouts>
                    <igDP:FieldLayout>
                        <igDP:FieldLayout.Settings>
                            <igDP:FieldLayoutSettings AutoGenerateFields="False" />
                        </igDP:FieldLayout.Settings>
                        <igDP:FieldLayout.Fields>
                            <igDP:Field Name="Name"
                                        Label="Name" />
                            <igDP:Field Name="Area"
                                        Label="Area" />
                            <igDP:UnboundField Label="# Countries"
                                               Binding="{Binding Countries.Count}" />
                        </igDP:FieldLayout.Fields>
                    </igDP:FieldLayout>
                </igDP:XamDataGrid.FieldLayouts>
            </igDP:XamDataGrid>

            <!-- Continent detail -->
            <ListBox ItemsSource="{Binding ContinentView/Countries}"
                     DisplayMemberPath="Name"
                     IsSynchronizedWithCurrentItem="True"
                     Height="200" />

            <!-- Country detail -->
            <StackPanel Orientation="Horizontal">
                <Label Content="Name: " />
                <TextBlock Text="{Binding ContinentView/Countries/Name}" />
                <Label Content="Population: " />
                <TextBlock Text="{Binding ContinentView/Countries/Population}" />
            </StackPanel>



        </StackPanel>
    </Grid>
</Window>

App.xaml.cs

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using XamDataGridMasterDetail.ViewModels;
using System.Collections.ObjectModel;
using XamDataGridMasterDetail.Model;

namespace XamDataGridMasterDetail
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnSessionEnding(SessionEndingCancelEventArgs e)
        {
            base.OnSessionEnding(e);
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var view = new MainWindow();
            var vm = new MainViewModel();
            vm.Continents = new ObservableCollection<ContinentViewModel>();
            vm.Continents.Add(new ContinentViewModel
            {
                Model = new Continent
                {
                    Name = "Australasia",
                    Area = 100000,
                    Countries = new[] 
                    {
                        new Country 
                        {
                            Name="Australia",
                            Population=100
                        },
                        new Country
                        {
                            Name="New Zealand",
                            Population=200
                        }
                    }
                }
            });
            vm.Continents.Add(new ContinentViewModel
            {
                Model = new Continent
                {
                    Name = "Europe",
                    Area = 1000000,
                    Countries = new[] 
                    {
                        new Country 
                        {
                            Name="UK",
                            Population=70000000
                        },
                        new Country
                        {
                            Name="France",
                            Population=50000000
                        },
                        new Country
                        {
                            Name="Germany",
                            Population=75000000
                        }
                    }
                }
            });
            view.DataContext = vm;
            view.Show();
        }

    }
}