如何从模板中访问主 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 项目,其属性(数据上下文中的 SelectionA
和 SelectionB
, MainWindowViewModel
) 在它们发生变化时发出通知。 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}"/>
总结
我在数据模板中有一个元素,我想将其绑定到主数据上下文的某些 属性。 我意识到在这种特定情况下,不同的解决方案可能更可取(我有一个避免这种情况的可行解决方案),但我怀疑这种问题可能会再次出现,我想知道在一般情况下如何解决.
下面是我的具体情况。
详情
Data Hierarchy:我有一个 A
类型的列表,A
的每个实例都有一个 B
类型的列表,每个B
的实例有一些其他数据,包括文本日志的 string
。
UI结构:我有一个ComboBox
到select一个A
类型的项目。我有一个 TabControl
,其中的选项卡代表 B
类型的项目,取自上面的 selected A
。在每个选项卡中,都有一种方法可以输入数据以填充类型 B
的对象,以及一个日志,表示对 B
.
支持逻辑:我跟踪每个列表中的 selected 项目,其属性(数据上下文中的 SelectionA
和 SelectionB
, MainWindowViewModel
) 在它们发生变化时发出通知。 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}"/>