0

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

Basssprosse
  • 334
  • 1
  • 3
  • 17
  • Don't you need a SelectedItem on the treeview and then do your bindings to the textboxes based on that? Otherwise, the bindings are just grabbing the first instance and not being updated when a treeview selection is made. – Josh Mar 01 '17 at 16:06
  • Thx for ur reply. I tried ur solution but it didnt work. I had to extend the treeview-control with the SelectedItem-property like on http://stackoverflow.com/a/3535089. I ended up with the following XAML: http://pastebin.com/VVgBtCqR Problem is that the treeview holds a list full of place and in each place is a different (datagrid)list of machines. So on treeview-select i could get which place-list is selected but i couldnt get which machine inside those place-lists is selected. Or am I wrong? – Basssprosse Mar 07 '17 at 18:49

1 Answers1

1

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.

  • Thank you for your help. Indeed i forgot the `INotifyPropertyChanged`-interface/`ObservableObject`. I added this idea to my code but it didnt solve the problem. In my question i added a project-example which shows the problem more exactly. I think rather that the `SelectedMachine` must be outside of the `PlaceModel`. But if i put the properties `PlaceList` and `SelectedMachine` into my viewmodel, i can not access `SelectedMachine` in my xaml-datagrid, because i set a `PlaceList` in the above treeview: http://pastebin.com/20HwpPga – Basssprosse Mar 13 '17 at 07:39
  • I am very thankful for your effort! Problematically visual studio doesn't show me the property `DataContext.SelectedMachine` on the binding-window, so i need to set it manually. See [this picture](https://picload.org/image/rlcgogwr/binding2.png). I would never have find that out without of ur help, thank you very much! – Basssprosse Mar 13 '17 at 14:32
  • No problem. I always learn something new from this questions. – Sebastian Richter Mar 14 '17 at 07:13