如何在WPF应用程序中绑定到子视图模型属性?

3
我正在使用Prism MVVM框架开发WPF应用程序。我不知道如何在父视图模型和子视图模型之间正确地传递数据。
我有两个视图模型 - 父视图模型和内部的子视图模型。
public class ParentViewModel
{
    public ParentViewModel
    {
        ChildViewModel = new ChildViewModel(params);
    }

        private ChildViewModel _childViewModel;
        public ChildViewModel ChildViewModel
        {
            get { return _childViewModel; }
            set
            {
                SetProperty(ref _childViewModel, value);
            }
        }

        //This is does not work
        public int SelectedChildNumber
        {
            return _childViewModel.SelectedNumber;
        }
}

public class ChildViewModel
{
    public ChildViewModel
    {
        _numbers = new List<int>();
    }

    private List<int> _numbers;
    public List<int> Numbers
    {
        get { return _numbers; }
        set
        {
            SetProperty(ref _numbers, value);
        }
    }

    private int _selectedNumber;
    public int SelectedNumber
    {
        get { return _selectedNumber; }
        set
        {
            SetProperty(ref _selectedNumber, value);
        }
    }
}

我希望能够获取并使用子视图模型中的选定值。我的方法不起作用 - 如果在ChildViewModel中更改SelectedNumber,则SelectedChildNumber不想刷新。
更新: 假设我在ParentViewModel中有一个ChildViewModel集合。其中一个ChildViewModel的IsSelected属性为true。如何从集合中获取此已选视图模型?
public class ParentViewModel
{
    public ParentViewModel
    {
        Items = GetItems();
    }

    private ObservableCollection<ChildViewModel> _items;
    public ObservableCollection<ChildViewModel> Items
    {
        get
        {
            return _items;
        }
        set
        {
            SetProperty(ref _items, value);
        }
    }
}

public class ChildViewModel
{
    public ChildViewModel
    {
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            SetProperty(ref _isSelected, value);
        }
    }
}

如何获取选定的视图模型?也许使用转换器?
<someUserControl DataContext="{Binding ParentViewModel.Items, Converter={x:Static c:GetSelectedItemConverter.Instance}}" />

在转换器中我可以找到所选项目。这是一个不错的想法吗?

更新2:

好的,经过Ed Plunkett的帮助,我解决了这个问题。最终版本应该是:

public class ParentViewModel
{
    public ParentViewModel
    {
        Items = GetItems();
        foreach (var item in Items)
        {
            item.PropertyChanged += ChildViewModel_PropertyChanged;
        }
    }

    private ObservableCollection<ChildViewModel> _items;
    public ObservableCollection<ChildViewModel> Items
    {
        get
        {
            return _items;
        }
        set
        {
            SetProperty(ref _items, value);
        }
    }

    private ChildViewModel _selectedChild;
    public ChildViewModel SelectedChild
    {
        get { return _selectedChild; }
        set
        {
            SetProperty(ref _selectedChild, value);
        }
    }

    private void ChildViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var child = (ChildViewModel)sender;
        if (e.PropertyName == nameof(ChildViewModel.IsSelected) && child.IsSelected)
        {
            SelectedChild = child;
        }
    }
} 

你在SelectedNumber的setter中引发了PropertyChanged事件吗? - taquion
因为我正在使用Prism框架,所以我不需要手动引发PropertyChanged事件。 - Oblomingo
@taquion 的 SetProperty() 就是这样的。他很厉害。 - 15ee8f99-57ff-4f92-890c-b56153
我不知道那件事。请原谅我的疏忽。 - taquion
1个回答

4
直接绑定到子属性:
<ListBox 
    ItemsSource="{Binding ChildViewModel.Numbers}"
    SelectedItem="{Binding ChildViewModel.SelectedNumber}"
    />

<Label Content="{Binding ChildViewModel.SelectedNumber}" />

那是绑定路径中父级的ChildViewModel属性的名称,而不是类型。现在,Binding知道要监听ChildViewModel对象的PropertyChanged通知,以了解SelectedNumber和Numbers的情况。
您版本不能正常工作的原因是,当SelectedChildNumber更改时,父级未引发PropertyChanged。实际上,父级对其更改的了解程度与UI一样少。父级可能会处理子项的PropertyChanged事件,并且有时会这样做。
public ParentViewModel()
{
    ChildViewModel = new ChildViewModel(params);

    //  Handle child's PropertyChanged event
    ChildViewModel.PropertyChanged += ChildViewModel_PropertyChanged;
}

private void ChildViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    var child = (ChildViewModel)sender;

    if (e.PropertyName == nameof(ChildViewModel.SelectedNumber))
    {
        //  Do stuff
    }
}

但是像这种情况,您不需要这样做。

ChildViewModel.Numbers 应该使用 ObservableCollection<int> 而不是 List<int>。这样,如果您向其中添加或删除任何数字,UI 将自动通过集合得到通知,并且 ListBox 会相应地自动更新。


太好了!如果我在ParentViewModel中有一个ChildViewModel集合。其中一个ChildViewModel的属性IsSelected等于true。如何从集合中获取这个被选中的视图模型? - Oblomingo
1
在这种情况下,父级可能希望处理每个子项的“PropertyChanged”事件。有趣的是,在我看到你的评论时,我正准备在更新中添加这个想法。 - 15ee8f99-57ff-4f92-890c-b56153
我不知道如何从每个子元素中引发事件。你能给个例子吗? - Oblomingo
@Oblomingo 子级对 SetProperty() 的调用引发了事件。您正在处理该事件。请参见更新。 - 15ee8f99-57ff-4f92-890c-b56153
Ed Plunkett,请查看更新的帖子。我不知道如何为每个项目添加句柄。 - Oblomingo
显示剩余2条评论

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