简体   繁体   中英

ListView with ComboBox as item template don't update source when selection in ComboBox changes

I have ListView bound to ObservableCollection<string> and each item should be displayed as ComboBox . Problem is, when i change selection in ComboBox , it doesn't update ObservableCollection<string> . Here is xaml:

<ListView ItemsSource="{Binding CellValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ComboBox ItemsSource="{Binding Path=DataContext.Column.CellValueChoices, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" SelectedIndex="0" IsEditable="True">
                            <i:Interaction.Behaviors>
                                <behaviors:CellFocusBehavior/>
                                <behaviors:FocusOnLoadBehavior/>
                            </i:Interaction.Behaviors>
                            <ComboBox.InputBindings>
                                <KeyBinding Command="{Binding Path=DataContext.ValidateAndInsertNewCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}" Key="Tab"/>
                            </ComboBox.InputBindings>
                        </ComboBox>
                    </DataTemplate>
                </ListView.ItemTemplate>
</ListView>

DataContext for ListView is CellViewModel object that contains ObservableCollection<string> CellValue .

Looking at your xaml, it seems to me that you think "ItemsSource="{Binding CellValue, Mode=TwoWay" is the way of binding a wpf control to it's corresponding VM property which is not quite right. The Itemsource binding should only be one way as the ViewModel loading the list that implement INotifyCollectionChanged to the GUI in one-way fashion. Making the ItemsSource binding to TwoWay doesn't make the GUI know how to update all the control's value inside the DataTemplate back to your ViewModel.

Instead, you should binding each control to each one of your ViewModel property in order to do that. I made you a sample for it.

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class OrderViewModel : ViewModelBase
{
    public ObservableCollection<string> ComboBoxOptions { get; set; }

    private string orderId;
    private string instrumentId;
    private string selectedComboOption;

    public OrderViewModel()
    {
        ComboBoxOptions = new ObservableCollection<string>
        {
            "Option1",
            "Option2",
            "Option3",
        };            
    }

    public string OrderId
    {
        get { return orderId; }
        set
        {
            orderId = value;
            OnPropertyChanged();
        }
    }

    public string InstrumentId
    {
        get { return instrumentId; }
        set
        {
            instrumentId = value;
            OnPropertyChanged();
        }
    }

    public string SelectedComboOption
    {
        get { return selectedComboOption; }
        set
        {
            selectedComboOption = value;
            OnPropertyChanged();
        }
    }
}

[Export]
public class MainViewModel : ViewModelBase
{
    private OrderViewModel selectedOrder;
    public ObservableCollection<OrderViewModel> Orders { get; set; }        

    public MainViewModel()
    {
        Orders = new ObservableCollection<OrderViewModel>
        {
            new OrderViewModel {OrderId = "Order1", InstrumentId = "Instrument1"},
            new OrderViewModel {OrderId = "Order2", InstrumentId = "Instrument2"},
            new OrderViewModel {OrderId = "Order2", InstrumentId = "Instrument3"}
        };
    }

    public OrderViewModel SelectedOrder
    {
        get { return selectedOrder; }
        set
        {
            selectedOrder = value;
            OnPropertyChanged();
        }
    }
}

<Window x:Class="WpfTestProj.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:local="clr-namespace:WpfTestProj"
    mc:Ignorable="d" 
    d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=False}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ListView ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBox Text="{Binding OrderId}" />
                    <TextBox Text="{Binding InstrumentId}" />
                    <ComboBox ItemsSource="{Binding ComboBoxOptions}"                                  
                              SelectedItem="{Binding SelectedComboOption}"/>
                </StackPanel>                    
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM