MVVM中ListBox的selectedItem属性可以获取,但无法设置

9

我正在开发一个使用MVVM模式的WPF应用程序。在我的视图中,有一个包含不同列的网格视图。其中一列是一个ListBox。现在的问题是,对于这个ListBox列,SelectedItem的获取工作正常,但设置操作却无效。

以下是我的视图代码:

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

在我的ViewModel中,我有一个主ViewModel类,其中包含一个项目列表。项目类包含名称、操作列表和选定的操作。
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; }
    }
}

现在Items列表中的SelectedItem可以正常工作。但是Actions类中SelectedItem并没有完全起作用。我在SelectedAction的getter和setter中插入了断点,get断点触发了。但是如果我从UI中选择一个操作,那么设置SelectedAction的断点就不会被触发。
问题出在哪里?
当我选择Archive Project或Restore Project时,SelectedAction的setter没有被调用。
注意:我已经删除了不必要的信息,比如在列表中加载数据,实现INotifyOfPropertyChanged等。

这句话的意思是MyViewModel中的SelectedItem正常工作吗? - MikroDel
是的,这对我来说很困惑。 - MikroDel
@vc74 不,这个不在setter中。 - fhnaseer
@Fendy 不好意思,我对此一无所知。 - fhnaseer
尝试实现类似于此 MSDN 链接中的内容:http://msdn.microsoft.com/en-us/library/system.componentmodel.ieditableobject.aspx。虽然我不能保证,但它似乎是网格编辑的要求。 - Fendy
显示剩余11条评论
7个回答

11
我不知道是否符合你的情况,但是由于你说已经剪切了一些信息,我会尝试回答。
当我向ListBox或ListView添加自定义模板时,如果ListBoxItem内部的控件处理了单击事件,则会发生这种情况。
例如,如果你有一个像这样的单选按钮:
<ListBox ItemsSource="{Binding List}" SelectedItem="{Binding item}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <RadioButton Content="{Binding Name}" GroupName="groupName">
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

当你实际点击单选按钮时,SelectedItem将不会被设置,因为单选按钮将处理点击事件并且不会冒泡,因此ListBox可能会更改其选择。

您必须确保ListBox获取事件,并且模板中的控件相应地作出反应。在这种情况下,您需要执行以下操作。

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

谢谢!你帮了我很多! - Витёк Синёв

4
这是修复方法。我们需要为listbox的selectedItem设置UpdateSourceTrigger。
<DataTemplate>
   <ListBox DisplayMemberPath="Name" ItemsSource="{Binding Actions}" SelectedItem="{Binding SelectedAction, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>

4

您需要在SelectedItem上指定双向绑定。否则,UI上所做的更改不会传递到您的视图模型。

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

尝试过了,不起作用。在ViewModel类中的SelectedItem可以正常工作,但在内部列表中无法工作。 - fhnaseer
内部列表不受属性更改或ObservableCollection的影响。因此,您无法通过这种方式看到内部列表中的任何更改。 - Xelom

2

MyViewModel类的SelectedItem属性在setter更改其值时未使用INotifyPropertyChanged接口通过引发PropertyChanged事件:

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

你应该对所有ViewModel中的所有属性都执行此操作。

我这样做了,但是没有起作用。我没有通过代码设置SelectedItem。我是通过视图选择一个操作的。在单击操作时,Setter方法没有被调用。 - fhnaseer

1

不仅"Erno de Weerd"的答案是正确的,你需要在所选属性上通知属性更改,而且WPF DataGrid是微软提供的与MVVM相关性最多的有缺陷的控件。

你会注意到,当你改变外部列表的选择时,内部列表的SelectedItem属性将会更新。为了在内部列表选择更改时实时刷新它,你需要更改列表上的绑定以添加这个:

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

当列表框没有嵌入到数据网格列中时,这是不需要的。


0
我不确定我是否走在正确的道路上,但你尝试过将你的字符串包装在一个类中吗?
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; }
}
}

0

查看VS中的输出窗口。 可能您的属性类型与项目类型不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接