Как установить DataContext через HierarchicalDataTemplate?
Я в настоящее время злюсь на это: как распространять DataContext через иерархическое дерево? (учитывая, что подэлементы MyItem известны и фиксированы, целью является установка исходного URI и DataContext фрейма) (учитывая также, что другие категории и подэлементы будут добавлены позже)
Во-первых, вот конкретное определение элемента:
namespace WpfApplication1
{
using System.Collections.Generic;
public class MyItem
{
public string Name { get; set; }
public List<MyItem> GetItems()
{
return new List<MyItem>() {
new MyItem() { Name = "Item A" },
new MyItem() { Name = "Item B" }
};
}
}
}
Во-вторых, определение конкретного селектора DataTemplate:
namespace WpfApplication1
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using System.Windows;
public class MyTemplateSelector : DataTemplateSelector
{
public static List<DataTemplate> DataTemplates = new List<DataTemplate>();
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
var datatemplate = DataTemplates.FirstOrDefault(dt => dt.DataType as Type == item.GetType());
return datatemplate ?? base.SelectTemplate(item, container);
}
}
}
В-третьих, вот главное окно
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:WpfApplication1"
Title="MainWindow" Height="250" Width="800" >
<Window.Resources>
<ObjectDataProvider x:Key="ItemsProvider" ObjectType="{x:Type my:MyItem}" MethodName="GetItems" />
<my:MyTemplateSelector x:Key="MySelector" />
<CompositeCollection x:Key="fixedsubitems">
<HeaderedItemsControl Header="category #i" >
<HeaderedItemsControl Header="Products j" />
<HeaderedItemsControl Header="Products k" />
</HeaderedItemsControl>
</CompositeCollection>
<HierarchicalDataTemplate x:Key="myItemTemplate" DataType="{x:Type my:MyItem}" ItemsSource="{Binding Source={StaticResource fixedsubitems}}" ItemTemplateSelector="{StaticResource MySelector}">
<!--<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem" >
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self}}" ></Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>-->
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="HeaderedItemsHierarchicalTemplate" DataType="{x:Type HeaderedItemsControl}" ItemsSource="{Binding Path=Items}" ItemTemplateSelector="{StaticResource MySelector}">
<StackPanel Orientation="Horizontal" ContextMenu="{Binding ContextMenu}" >
<TextBlock Text="{Binding Header}" Margin="3" />
</StackPanel>
</HierarchicalDataTemplate>
<CompositeCollection x:Key="sections" >
<HeaderedItemsControl Header="Section #1" >
<HeaderedItemsControl Header="Entry 1.1" >
</HeaderedItemsControl>
<HeaderedItemsControl Header="My items" ItemsSource="{Binding Source={StaticResource ItemsProvider}}" >
</HeaderedItemsControl>
</HeaderedItemsControl>
<HeaderedItemsControl Header="Section #2" >
<HeaderedItemsControl Header="Entry 2.1" >
</HeaderedItemsControl>
<HeaderedItemsControl Header="Entry 2.2" >
</HeaderedItemsControl>
</HeaderedItemsControl>
</CompositeCollection>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" DataContext="{Binding Source={StaticResource sections}}">
<ListBox Name="list" ItemsSource="{Binding}" HorizontalContentAlignment="Stretch" SelectedItem="{Binding Path=/}">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel >
<Border >
<TextBlock Text="{Binding Header}" TextAlignment="Center" VerticalAlignment="Center" Padding="5" />
</Border>
<Border >
<TreeView Name="tv" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource HeaderedItemsHierarchicalTemplate}" >
<TreeView.Resources>
<Style TargetType="TreeViewItem" >
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.Resources>
</TreeView>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
<Frame Name="frame" Source="" Grid.Column="1" />
</Grid>
</Window>
Наконец, код MainWindow позади:
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
foreach (var res in this.Resources.Cast<DictionaryEntry>())
{
if (res.Value is DataTemplate)
{
MyTemplateSelector.DataTemplates.Add(res.Value as DataTemplate);
}
}
}
}
}
в этих 4 файлах отображается это окно
Как получить контекст MyItem при щелчке по подэлементу, например, категории или продуктам?
Я пробовал несколько способов с ItemContainerStyle, Setter внутри Style, определенного в ресурсах DataTemplate... никогда не получалось!
1 ответ
Сначала я изменяю взаимодействие с выделенным кодом TreeView с помощью команды, управляемой с помощью триггеров взаимодействия:
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
../..
<TreeView Name="tv" ItemsSource="{Binding Items}" ItemTemplate="{StaticResource HeaderedItemsHierarchicalTemplate}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="Open" CommandParameter="{Binding ElementName=tv, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TreeView.Resources>
<Style TargetType="TreeViewItem" >
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.Resources>
</TreeView>
с этим связанным кодом позади
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
var tvi = e.OriginalSource as TreeViewItem;
if (tvi != null && tvi.Tag != null)
{
frame.DataContext = tvi.Tag;
frame.Source = new Uri("Page1.xaml", UriKind.Relative);
}
}
2: я разделяю свои фиксированные подпункты на уровни:
<CompositeCollection x:Key="fixedsubitems">
<HeaderedItemsControl Header="category #i" >
<HeaderedContentControl Header="Products j" />
<HeaderedContentControl Header="Products k" />
</HeaderedItemsControl>
</CompositeCollection>
HeaderedItemsControl для уровня 1, HeaderedContentControl для уровня 2
3: я устанавливаю тег моего источника через ItemContainerStyle объекта HeaderedItemsControl HierarchicalDataTemplate:
<HierarchicalDataTemplate.ItemContainerStyle>
<Style >
<Setter Property="Control.Tag" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}, AncestorLevel=2}, Path=DataContext}" ></Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
4. Я дублирую HeaderedItemsControl HierarchicalDataTemplate, чтобы создать HeaderedContentControl HierarchicalDataTemplate, и устанавливаю AncestorLevel=3 в ItemContainerStyle
5: я обрабатываю событие LoadCompleted кадра
<Frame Name="frame" Grid.Column="1" LoadCompleted="frame_LoadCompleted" />
с этим связанным кодом
private void frame_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
var content = frame.Content as FrameworkElement;
if (content == null)
return;
content.DataContext = frame.DataContext;
}
Вот мой Page1:
<Page x:Class="WpfApplication1.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:my="clr-namespace:WpfApplication1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance Type=my:MyItem}"
Title="Page1" >
<Grid>
<TextBlock Text="{Binding Path=Name}" FontSize="16" Background="WhiteSmoke" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Page>
ЭТО СЕЙЧАС РАБОТАЕТ!