ListBox selectedItem get working but set not working in MVVM

13,338

Solution 1

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>

Solution 2

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>

Solution 3

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}"

Solution 4

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.

Share:
13,338

Related videos on Youtube

fhnaseer
Author by

fhnaseer

C#, WPF, MVVM, Entity Framework, WCF, TDD, Unit-Testing, Agile, Scrum,

Updated on July 24, 2022

Comments

  • fhnaseer
    fhnaseer almost 2 years

    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.

    View

    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.

  • fhnaseer
    fhnaseer almost 11 years
    I did this, but it didn't worked. I am not setting SelectedItem through code. I am selecting an action through the view. And upon clicking on a action, setter doesn't get called.
  • fhnaseer
    fhnaseer almost 11 years
    Tried that, its not working. It works fine for SelectedItem in ViewModel class. But doesn't work in inner list.
  • Xelom
    Xelom almost 11 years
    Inner list is not affected by property changed or observablecollection. So you can't see any changes in the inner list by this way.
  • Витёк Синёв
    Витёк Синёв over 5 years
    tnx! you helped me a lot!