如何从模板中访问主 DataContext

How to access the main DataContext from within a template

总结

我在数据模板中有一个元素,我想将其绑定到主数据上下文的某些 属性。 我意识到在这种特定情况下,不同的解决方案可能更可取(我有一个避免这种情况的可行解决方案),但我怀疑这种问题可能会再次出现,我想知道在一般情况下如何解决.

下面是我的具体情况。

详情

Data Hierarchy:我有一个 A 类型的列表,A 的每个实例都有一个 B 类型的列表,每个B 的实例有一些其他数据,包括文本日志的 string

UI结构:我有一个ComboBox到select一个A类型的项目。我有一个 TabControl,其中的选项卡代表 B 类型的项目,取自上面的 selected A。在每个选项卡中,都有一种方法可以输入数据以填充类型 B 的对象,以及一个日志,表示对 B.

实例的更改

支持逻辑:我跟踪每个列表中的 selected 项目,其属性(数据上下文中的 SelectionASelectionBMainWindowViewModel) 在它们发生变化时发出通知。 B 对象还会在其日志文本更改时发出通知。这些确保 UI 响应对支持数据的更改。

问题:我想将通知逻辑移动到一个地方(DataContext,即MainWindowViewModel),而不是让一些在 B class 中并且需要复制通知逻辑。为此,我添加了一个 属性 (SelectionBLogText) 来跟踪 SelectionB 对象的 LogText 属性,并绑定日志(在模板化的标签面板中) 到主要 SelectionBLogText 属性。问题是在标签页中,我似乎只能绑定到 selected B 对象的属性(来自 selected 选项卡),我需要绑定到 属性 而不是 DataContext。我试过使用 RelativeSource,但到目前为止我都没有尝试过,而且我看文档越多,我就越觉得它是为另一份工作而设计的。

XAML(删除了不相关的细节):

<Window x:Class="WPFQuestion.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFQuestion"
        mc:Ignorable="d"

        Title="MainWindow"
        Height="350"
        Width="930">
    <DockPanel>
        <ComboBox
            ItemsSource="{Binding ListOfA}"
            SelectedItem="{Binding SelectionA}"
            DisplayMemberPath="Name"/>
        <TabControl
            ItemsSource="{Binding SelectionA}"
            SelectedItem="{Binding SelectionB}"
            DisplayMemberPath="Name">
            <TabControl.ContentTemplate>
                <ItemContainerTemplate>
                    <StackPanel>
                        <TextBox
                            IsReadOnly="True"
                            Text="{Binding Path=???.SelectionBLogText}"/>
                        <Button Click="ClearLogButton_Click"/>
                    </StackPanel>
                </ItemContainerTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </DockPanel>
</Window>

和代码隐藏:

public partial class MainWindow : Window
{
    internal MainWindowViewModel vm;
    public MainWindow()
    {
        InitializeComponent();
        vm = new MainWindowViewModel();
        DataContext = vm;
    }
    // Various methods for event handling
}

public class A : IEnumerable<B>
{
    public string Name { get; set; }
    public List<B> Bs { get; set; }
}

public class B // previously : INotifyPropertyChanged
{
    public string Name { get; set; }
    public string LogText { get; set; }

    // various other properties
}

public class MainWindowViewModel : INotifyPropertyChanged
{
    private A _a;
    private B _b;

    public event PropertyChangedEventHandler PropertyChanged;

    public List<A> ListOfA { get; set; }

    public A SelectionA
    {
        get => _a;
        set
        {
            if (_a == value)
            {
                return;
            }
            _a = value;
            RaisePropertyChanged(nameof(SelectionA));
        }
    }

    public B SelectionB
    {
        get => _b;
        set
        {
            if (_b == value)
            {
                return;
            }
            _b = value;
            RaisePropertyChanged(nameof(SelectionB));
            RaisePropertyChanged(nameof(SelectionBLogText));
        }
    }

    public string SelectionBLogText
    {
        get => SelectionB.LogText;
        set
        {
            if (SelectionB.LogText == value)
            {
                return;
            }
            SelectionB.LogText = value;
            RaisePropertyChanged(nameof(SelectionBLogText));
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

你在使用相对绑定的时候试过这样的事情吗?如果没有,请检查一下。

     <TextBox IsReadOnly="True"
             Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
             Path=Datacontext.SelectionBLogText}"/>