Basssprosse
Basssprosse

Reputation: 466

How to get the SelectedItem of several datagrids?

Foreach treeview-item i got an own datagrid. Treeview-items and datagrids are filled by binding.

On textboxes i got a binding to the selected item of the datagrids. But the binding on these textboxes only works with the first datagrid. Every other datagrid doesn't transfer the selecteditem to the textboxes:

wrong binding

Here is the treeview with the datagrid:

            <TreeView ItemsSource="{Binding Path=PlaceList}">
                <TreeView.ItemTemplate>
                    <DataTemplate>
                        <TreeViewItem Header="{Binding Path=Name}">
                            <DataGrid ItemsSource="{Binding MachinesInPlace, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                      SelectionUnit="FullRow"
                                      SelectedItem="{Binding SelectedMachine, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                      AutoGenerateColumns="True"
                                      IsSynchronizedWithCurrentItem="True"
                                      SelectionMode="Single">
                            </DataGrid>
                        </TreeViewItem>
                    </DataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>

Here is the textbox:

<TextBox Text="{Binding PlaceList/SelectedMachine.Name, ValidatesOnDataErrors=True}" />

I am working with MvvmLight. My ViewModel holds the PlaceList:

    public ObservableCollection<PlaceModel> PlaceList { get; set; } = new ObservableCollection<PlaceModel>();

    public ObjectInspectorViewModel()
    {
        PlaceList = PlaceModel.GetPlaces(BaseResourcePaths.PlacesCsv);
    }

That s my place-model:

public class PlaceModel
{
    public int Id { get; set; }
    public string Name { get; set; } = "_CurrentObjectName";
    public string Length { get; set; }
    public string Width { get; set; }
    public string Height { get; set; }
    public ObservableCollection<MachineModel> MachinesInPlace { get; set; }
    public MachineModel SelectedMachine { get; set; }

    public static ObservableCollection<PlaceModel> GetPlaces(string filepath)
    {
        [...]
    }
}

I tried something out but at last i dont know how to fix the bug. What s the problem? My suggestion is the property ''SelectedMachine'' inside the place-model...

Here is an example-project (with the additional solution of Sebastian Richter). It shows the problems: https://www.file-upload.net/download-12370581/DatagridTreeViewError.zip.html

Upvotes: 0

Views: 336

Answers (1)

Sebastian Richter
Sebastian Richter

Reputation: 485

I'm quiet sure you forget to implement INotifyPropertyChanged in you class PlaceModel. The problem is after you changed the selection, the Property Placemodel.SelectedMachine will be updated but no event will be fired to populate this change in the View.

Because you use MVVM Light you can derive from ObservableObject which already implements this Interface. So change your PlaceModel to following code:

public class PlaceModel : ObservableObject
{
    private MachineModel _selectedMachine;
    public int Id { get; set; }
    public string Name { get; set; } = "_CurrentObjectName";
    public string Length { get; set; }
    public string Width { get; set; }
    public string Height { get; set; }
    public ObservableCollection<MachineModel> MachinesInPlace { get; set; }

    public MachineModel SelectedMachine
    {
        get
        {
            return _selectedMachine;
        }

        set
        {
            // raises Event PropertyChanged after setting value
            Set(ref _selectedMachine, value);
        }
    }

    public static ObservableCollection<PlaceModel> GetPlaces(string filepath)
    {
    [...]
}

Edit:

I guess the binding doesn't know which element to bind to from your ObservableCollection (many to one relation) because you set it as the reference in your TextBox.

So try to remove the SelectedMachine property from the Model and add it back to the ViewModel:

class ViewModel : ViewModelBase
{
     ...
     private MachineModel _selectedMachine;
     public MachineModel SelectedMachine
     {
        get
        {
            return _selectedMachine;
        }
        set
        {
        // raises Event PropertyChanged after setting value
        Set(ref _selectedMachine, value);
        }
    }
    ...
}

Also change your XAML to following code (I used your example project):

<Grid x:Name="LayoutRoot">
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>

    <!-- Row #1 -->
    <Grid>
        <!-- TreeView und DataGrids-->
        <TreeView ItemsSource="{Binding Path=PlaceList}">
            <TreeView.ItemTemplate>
                <DataTemplate>
                    <TreeViewItem Header="{Binding Path=Name}">
                        <DataGrid ItemsSource="{Binding MachinesInPlace, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                  SelectedItem="{Binding DataContext.SelectedMachine, RelativeSource={RelativeSource AncestorType=Window},Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    </TreeViewItem>
                </DataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>

    <!-- Row #2 -->
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition Width="2*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

        <Label Grid.Row="0"
               Content="ID" />
        <!-- Textboxen aktualisieren nur bei Auswahl der ersten Datagrid -->
        <TextBox Grid.Column="2"
                 Grid.Row="0"
                 Text="{Binding SelectedMachine.Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <Label Grid.Row="1"
               Content="Name" />
        <TextBox Grid.Column="2"
                 Grid.Row="1"
                 Text="{Binding SelectedMachine.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</Grid>

The key was to set the correct DataContext for SelectedItem. For this i used following XAML code:

<DataGrid ItemsSource="{Binding MachinesInPlace, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                  SelectedItem="{Binding DataContext.SelectedMachine, RelativeSource={RelativeSource AncestorType=Window},Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

With this the your example project updates the TextBoxes correctly.

Upvotes: 1

Related Questions