Search code examples
wpfdatagriduser-controlsselectionchanged

WPF DataGrid SelectionChange event doesn't fire when ItemsSourse is being set from UserControl


I have very interesting scenario here, look:

MainWindow XAML:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TabControl Grid.Row="0"
                Grid.Column="0"
                SelectionChanged="Selector_OnSelectionChanged">
        <TabItem Header="First"/>
        <TabItem Header="Second"/>
    </TabControl>
    <ContentPresenter Grid.Column="1"
                      Content="{Binding SelectedUserControl}">

    </ContentPresenter>
</Grid>

UserControlOne XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <DataGrid Grid.Row="0"
              ItemsSource="{Binding DataSource}"
              SelectedItem="{Binding SelectedItem}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <Command:EventToCommand Command="{Binding SelectionChangedCommand}" 
                                        CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </DataGrid>
    <Button Grid.Row="1" 
            Content="SetSource"
            Height="50"
            Command="{Binding SetSourceCommand}"/> 
    <Button Grid.Row="2" 
            Content="RemoveSource"
            Height="50"
            Command="{Binding RemoveSourceCommand}"/>
</Grid>

UserContolTwo XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" 
                        Content="SetSource"
                        Height="50"
                        Command="{Binding SetSourceCommand}"/>
    <Button Grid.Row="1" 
                        Content="RemoveSource"
                        Command="{Binding RemoveSourceCommand}"
                        Height="50"/>
</Grid>

CodeBehind:

public class GridItem
{
    public String Name { get; set; }
    public override string ToString()
    {
        return Name;
    }
}


public partial class Window1 : Window, INotifyPropertyChanged
{
    private List<GridItem> _items;
    private GridItem _selectedItem;
    private List<GridItem> _dataSource;
    private readonly List<UserControl> _userControlList;
    private UserControl _selectedUserControl;


    public UserControl SelectedUserControl
    {
        get { return _selectedUserControl; }
        set
        {
            _selectedUserControl = value;
            RaisePropertyChanged("SelectedUserControl");
        }
    }


    public GridItem SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            RaisePropertyChanged("SelectedItem");
        }
    }

    public List<GridItem> DataSource
    {
        get { return _dataSource; }
        set
        {
            _dataSource = value;
            RaisePropertyChanged("DataSource");
        }
    }

    public Window1()
    {
        InitializeComponent();
        DataContext = this;

        _items = new List<GridItem>
        {
            new GridItem { Name = "Igor" },
            new GridItem { Name = "Vasya"},
            new GridItem { Name = "Vladlen"}
        };

        _userControlList = new List<UserControl>
        {
            new UserControl1(),
            new UserControl2()
        };
    }

    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(String propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged

    private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        SelectedUserControl = _userControlList[((TabControl)sender).SelectedIndex];
    }


    #region SetSourceCommand

    private RelayCommand<Object> _setSourceCommand;
    public RelayCommand<Object> SetSourceCommand
    {
        get
        {
            return _setSourceCommand ?? (_setSourceCommand =
                new RelayCommand<Object>(SetSourceMethod));
        }
    }

    private void SetSourceMethod(Object obj)
    {
        DataSource = _items;
        SelectedItem = _items.FirstOrDefault();
    }

    #endregion SetSourceCommand


    #region RemoveSourceCommand

    private RelayCommand<Object> _removeSourceCommand;
    public RelayCommand<Object> RemoveSourceCommand
    {
        get
        {
            return _removeSourceCommand ?? (_removeSourceCommand =
                new RelayCommand<Object>(RemoveSourceMethod));
        }
    }

    private void RemoveSourceMethod(Object obj)
    {
        DataSource = null;
    }

    #endregion RemoveSourceCommand   


    #region SelectionChangedCommand

    private RelayCommand<Object> _selectionChangedCommand;
    public RelayCommand<Object> SelectionChangedCommand
    {
        get
        {
            return _selectionChangedCommand ?? (_selectionChangedCommand =
                new RelayCommand<Object>(SelectionChangedMethod));
        }
    }

    private void SelectionChangedMethod(Object obj)
    {
        Debug.WriteLine("Event have been rised! Selected item is {0}", ((DataGrid)obj).SelectedItem ?? "NULL");    
    }

    #endregion RemoveSourceCommand
}

I have MainWindow, that contains UserControl. UserControl is being set dinamically, so if you pick up FirstTabItem, then UserControlOne will be loaded and if you pick up SecondTabItem - UserControlTwo will be loaded into ContentPresenter of MainWindow.

If I click button SetSource on FirstTabItem (actually on UserControlOne) - then SelectionChanged event of DataGrid fires as usually. But if I click button SetSource on SecondTabItem (actually on UserControlTwo) - then SelectionChanged event of DataGrid doesn't fire at all. Despite on bouth buttons bound to the same command (SetSourceCommand).

If buttons don't placed on other controls, for example, only on different tabitems of the same TabControl - bouth buttons invokes SelectionChange event. So problem really in markup, in using UserControls.

Has anyone encoutered with that problem? How can I fix it? I don't want invoke eventhandler programmatically.

I posted all the required code here, so you can copy-paste it and try by yourself very quickly. Or I can load example project if someone interested.


Solution

  • Okay so here is the actual problem in your code.

    The UserControl1 has a grid in which you have used the SelectedItem Dependency Property. On this specific DP you have a Command SelectionChangedCommand.

    In you commands SetSourceCommand and RemoveSourceCommand you are updating SelectedItem of the data grid which fires the SelectionChangedCommand because of the event SelectionChanged.

    In your UserControl2 there is no data grid nor any control which fires SelectionChanged event to call SelectionChangedCommand. Hence it is never executed.