wpf 向上绑定:绑定到嵌套 uiElement 内的视图模型 属性

wpf binding upwards: bind to view model property inside nested uiElement

我有一个带有视图模型和一些嵌套 UI 元素的 WPF 项目。这是XAML的(相关部分):

<UserControl> // DataContext is MyVM (set programmatically)
    <TreeView ItemsSource="{Binding Trees}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Subtrees}">
                <StackPanel>
                    <ListView ItemsSource="{Binding Contents}"
                              SelectedValue="{Binding SelectedContent}" // won't work: Tree has no such property
                              SelectionMode="Single"/>
                </StackPanel>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</UserControl>

这里是 ViewModel 的代码 class:

public class MyVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    public IEnumerable<Tree> Trees { get; set; }

    private object _selectedContent;
    public string SelectedContent
    {
        get => _selectedContent;
        set
        {
            _selectedContent = value;
            OnPropertyChanged();
        }
    }
}

这里是 class Tree 的代码:

public class Tree
{
    public IEnumerable<Tree> Subtrees { get; set; }
    public IEnumerable<string> Contents { get; set; }
}

我想为所有 ListViews 在全局范围内只允许一个选择。就像here一样,我想将所有ListViews绑定到视图模型MyVM.

中的属性SelectedContent

问题是 ListView 的数据上下文是 Tree,而不是来自顶级用户控件的 MyVM。 (它应该是 Tree,因为我们想显示 Contents。)我知道我可以使用 SelectedValuePath 向下绑定,但是我如何向上绑定才能绑定 [=25] =] 到 MyVM 属性 SelectedContent?

我试过SelectedValue="{Binding RelativeSource={RelativeSource AncestorType ={x:Type UserControl}}, Path=SelectedContent}",但是没有用。

尝试使用

SelectedValue="{Binding RelativeSource={RelativeSource 
  Mode=FindAncestorBindingContext, AncestorType ={x:Type MyVM}}, 
  Path=SelectedContent}"

也许,您需要在 header 中描述模型的命名空间:

xmlns:viewmodel="clr-namespace:_your_namespace_.ViewModels"

并使用

SelectedValue="{Binding RelativeSource={RelativeSource 
  Mode=FindAncestorBindingContext, AncestorType ={x:Type viewmodel:MyVM}}, 
  Path=SelectedContent}"

有评论here,说:

Just wanted to note here that if you want to bind to a property in the DataContext of the RelativeSource then you must explicitly specify it: {Binding Path=DataContext.SomeProperty, RelativeSource=.... This was somewhat unexpected for me as a newbie when I was trying to bind to a parent's DataContext within a DataTemplate.

这条评论值得更多关注,所以我会把它作为正确答案。