简体   繁体   中英

ListBox selectedItem get working but set not working in MVVM

I am working on a WPF application and following MVVM. In my view there is a grid view which contains different columns. One of these column is a ListBox. Now problem is that for the ListBox column, SelectedItem get works fine but set doesn't.

Here is my View code

<DataGrid ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" SelectionMode="Single">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
        <DataGridTemplateColumn Header="Actions">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ListBox DisplayMemberPath="Name" ItemsSource="{Binding Actions}" SelectedItem="{Binding SelectedAction}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

In my ViewModel, I have Main ViewModel class, which contains a list of Items. Item class contains name, a list of actions and selected action.

public class MyViewModel : INotifyOfPropertyChanged
{
    private ObservableCollection<Item> _items;
    public ObservableCollection<Item> Items
    {
        get { return _items?? (_items= new ObservableCollection<Item>); }
    }

    private Item _selectedItem;
    public Item SelectedItem
    {
        get { return _selectedItem; }
        set { _selectedItem= value; }
    }
}

public class Item : INotifyOfPropertyChanged
{
    public string Name;

    private ObservableCollection<string> _actions;
    public ObservableCollection<string> Actions
    {
        get { return _actions?? (_actions= new ObservableCollection<string>); }
    }

    private string _selectedAction;
    public string SelectedAction
    {
        get { return _selectedAction; }
        set { _selectedAction = value; }
    }
}

Now SelectedItem for Items list works fine. But SelectedItem insde Item class for Actions doesn't work completely. I inserted breakpoints on getter and setter of SelectedAction. get breakpoint hits. But if I select an action from the UI then set breakpoint for SelectedAction doesn't get hit.

What's the problem.

视图

When I select Archive Project or Restore Project, setter of SelectedAction doesn't get called.

NOTE: I have removed unnecessary information like loading data in lists, implementation of INotifyOfPropertyChanged etc.

I don't know if it is your case but since you've said that you have cut some information I'll try to answer.

this happen to me when I add a custum template to a ListBox or ListView and that the controls within the ListBoxItem handle the click event themselve.

For example if you have a radio button like this

<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding item}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <RadioButton Content="{Binding Name}" GroupName="groupName">
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

The SelectedItem will not be set when you actualy click on the radio button because the radio button will handle the click event and will not bubble it up so the ListBox may change it's selection.

You have to make sure that the ListBox get the event and that the controls in the template react accordingly. in this case you'll have to do something like this.

<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding Item}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <RadioButton Content="{Binding Name}" IsHitTestVisible="False">
        <RadioButton.Style>
          <Style TargetType="{x:Type RadioButton}">
            <Setter Property="IsChecked" Value="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
          </Style>
        </RadioButton.Style>
      </RadioButton>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

You need to specify a TwoWay-Binding on the SelectedItem. Otherwise are changes made on the UI not passed to your viewmodel.

SelectedItem="{Binding SelectedAction, Mode=TwoWay}"

The SelectedItem property of the MyViewModel class does not use the INotifyPropertyChanged interface by raising the PropertyChanged event when the setter changed its value:

public Item SelectedItem
{
    get { return _selectedItem; }
    set 
    {
        if(value != _selectedItem)
        {
            _selectedItem= value; 
            OnPropertyChanged("SelectedItem");
        }
    }
}

You should do this for all properties in all ViewModels.

Here is the fix. We need to set UpdateSourceTrigger for the listbox for selectedItem,

<DataTemplate>
   <ListBox DisplayMemberPath="Name" ItemsSource="{Binding Actions}" SelectedItem="{Binding SelectedAction, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>

i dont know if i'm on the right way, but did you try to wrap your string in a class?

public class Item : INotifyOfPropertyChanged
{
public string Name;

private ObservableCollection<MyActionString> _actions;
public ObservableCollection<MyActionString> Actions
{
    get { return _actions?? (_actions= new ObservableCollection<MyActionString>); }
}

private MyActionString _selectedAction;
public MyActionString SelectedAction
{
    get { return _selectedAction; }
    set { _selectedAction = value; }
}
}

Look into Output Window in VS. Probably your property type differs from item type.

Not only is "Erno de Weerd" answer right, you need to notify the property change on your selected properties, but also WPF DataGrid are the most bugged control offered by Microsoft in relation to MVVM.

You will notice that the SelectedItem property of the innerlist WILL update, only when you change the selection of the outer list. To refresh it live when the inner list selection change, you need to change your binding on the list to add this:

SelectedItem="{Binding SelectedAction, UpdateSourceTrigger=PropertyChanged }"

This is not needed when the listbox is not embedded in a datagrid column.

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