Сводка
У меня есть элемент в шаблоне данных, который я хочу привязать к некоторому свойству основного контекста данных. Я понимаю, что в этой конкретной ситуации 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));
}
}