Как получить доступ к основному DataContext из шаблона - PullRequest
0 голосов
/ 18 июня 2020

Сводка

У меня есть элемент в шаблоне данных, который я хочу привязать к некоторому свойству основного контекста данных. Я понимаю, что в этой конкретной ситуации c может быть предпочтительнее другое решение (и у меня есть рабочее решение, позволяющее избежать этого), но я подозреваю, что такая проблема может возникнуть снова, и я хочу знать, как решить ее в общий случай.

Ниже приведены особенности моей ситуации.

Детали

Иерархия данных : у меня есть список типа A , каждый экземпляр A имеет список типа B, каждый экземпляр B имеет некоторые другие данные, включая string для текстового журнала.

Структура пользовательского интерфейса : У меня есть ComboBox, чтобы выбрать элемент типа A. У меня есть TabControl с вкладками, представляющими элементы типа B, взятые из выбранного A выше. На каждой вкладке есть средства для ввода данных для заполнения объекта типа B и журнал, представляющий изменения этого экземпляра B.

Backing Logi c: я отслеживаю выбранный элемент в каждом списке с помощью свойств (SelectionA и SelectionB в контексте данных, MainWindowViewModel), которые уведомляют об изменении. Объект B также уведомляет об изменении текста журнала. Это гарантирует, что пользовательский интерфейс реагирует на изменения данных резервного копирования.

Проблема : я хочу переместить лог c уведомлений, чтобы все они были в одном месте (DataContext, т.е. MainWindowViewModel), а не в классе B и необходимости дублировать logi уведомлений c. Для этого я добавляю свойство (SelectionBLogText) для отслеживания свойства LogText объекта SelectionB и связываю журнал (в шаблонной панели вкладок) с основным свойством SelectionBLogText. Проблема в том, что внутри вкладки я могу привязаться только к свойствам выбранного объекта B (из выбранной вкладки), а вместо этого мне нужно привязаться к свойству 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));
    }
}

1 Ответ

1 голос
/ 18 июня 2020

пробовали ли вы что-то подобное при использовании относительной привязки? если нет, проверьте это.

     <TextBox IsReadOnly="True"
             Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window},
             Path=Datacontext.SelectionBLogText}"/>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...