Search code examples
c#wpfxamllistviewdata-binding

Two-way link to the properties of all SelectedItems of a ListView?


I have a ListView bound to an ObservableCollection of objects. Next to that I have a bunch of TextBoxes and ComboBoxes that are TwoWay bound to the properties of the SelectedItem of that ListView. The items in my ListView have INotifyPropertyChanged. With that the user can select an item out of the ListView and edit the properties of it.

However editing a lot of items takes long, so I wanted to user to be able to select multiple items and edit the properties of all the selected items at once using the controls next to the ListView.

I already tried changing the DataContext to the SelectedItems property of the ListView, but that didn't work.

Can someone tell me how to do that?

Edit: To clarify, when the user selects more than one item, I want the editing controls next to the ListView to show nothing and then only when the user changes something in those controls the changes go to all the SelectedItems and the change stays visible in the TextBox or ComboBox because then the property is the same in all SelectedItems.

<StackPanel x:Name="EditPanel" Grid.Row="0" Grid.RowSpan="2" DataContext="{Binding SelectedItem, ElementName=LayersList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CanVerticallyScroll="True">
    <TextBlock FontSize="14" HorizontalAlignment="Stretch" Foreground="#FFD6D6D6" Margin="0,10,0,0"><Run Text="Name:"/></TextBlock>
    <TextBox x:Name="SelectedNameBox" HorizontalAlignment="Stretch" TextWrapping="NoWrap" Foreground="#FFD6D6D6" Text="{Binding Name, Mode=TwoWay}"/>
    <TextBlock FontSize="14" HorizontalAlignment="Stretch" Foreground="#FFD6D6D6" Margin="0,10,0,0"><Run Text="Hitsound info:"/></TextBlock>
    <ComboBox x:Name="SelectedSampleSetBox" Margin="0,10,0,0" HorizontalAlignment="Stretch" Text="{Binding SampleSetString, Mode=TwoWay}" Cursor="Hand">
        <ComboBoxItem Content="Normal" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Soft" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Drum" HorizontalAlignment="Left" Cursor="Hand"/>
    </ComboBox>
    <ComboBox x:Name="SelectedHitsoundBox" Margin="0,10,0,0" HorizontalAlignment="Stretch" Text="{Binding HitsoundString, Mode=TwoWay}" Cursor="Hand">
        <ComboBoxItem Content="Normal" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Whistle" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Finish" HorizontalAlignment="Left" Cursor="Hand"/>
        <ComboBoxItem Content="Clap" HorizontalAlignment="Left" Cursor="Hand"/>
    </ComboBox>
    ...
</StackPanel>

Solution

  • I ended up not using data bindings for the editing controls. Instead I explicitly update them on a SelectionChangedEvent of the ListView. In there it also describes the logic for how it displays multiple selected items at once.

    suppressEvents = true;
    
    if (selectedLayers.TrueForAll(o => o.Name == selectedLayer.Name)) {
        SelectedNameBox.Text = selectedLayer.Name;
    } else {
        SelectedNameBox.Text = "";
    }
    ...
    
    suppressEvents = false;
    

    Then the ChangedEvents of the editing controls update all the SelectedItems.

    private void SelectedNameBox_TextChanged(object sender, TextChangedEventArgs e) {
        if (suppressEvents) return;
    
        string t = (sender as TextBox).Text;
        foreach (HitsoundLayer hitsoundLayer in LayersList.SelectedItems) {
            hitsoundLayer.Name = t;
        }
    }