Как установить 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 файлах отображается это окно

в этих 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>

ЭТО СЕЙЧАС РАБОТАЕТ!

Другие вопросы по тегам