没有资源密钥时,如何在应用的DataTemplate中获取根元素? [英] How to get the root element inside an applied DataTemplate when it does not have a resource key?

查看:93
本文介绍了没有资源密钥时,如何在应用的DataTemplate中获取根元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望将根元素放入已应用的DataTemplate中.我尝试了,但它对我不起作用,因为对于MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm)返回的ContentPresenter,其中vm是ViewModel,ContentPresenter.ContentTemplatenull,尽管ContentPresenter.Content是对应的数据(相同的ViewModel ).

I wish to get the root element inside an applied DataTemplate. I tried this but it does not work for me, because for the ContentPresenter returned by MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm) where vm is a ViewModel, ContentPresenter.ContentTemplate is null, although ContentPresenter.Content is the corresponding data (the same ViewModel).

我会以此处之类的资源访问DataTemplate,但是我无法提供DataTemplate的资源密钥,因为我希望将它们自动应用于ItemsControl中的所有项目.因此,我必须找到一种从ItemsControl中的项目获取DataTemplate的方法.

I would access DataTemplates as resources like here but I cannot give the DataTemplates resource keys because I want them to be automatically applied to all the items inside the ItemsControl. So I must find a way to get the DataTemplate from an item inside the ItemsControl.

我可以使用if-else来确定vm.GetType()的功能中的DataTemplate资源,但是我希望在没有ItemContainerGenerator的情况下并根据MVVM模式,实现我希望的功能,并且没有硬编码类型.

I could use if-else for determining the DataTemplate resource in function of the vm.GetType() but I would like to realize what I wish without ItemContainerGenerator and according to the MVVM pattern, if possible, and without hard-coding types.

以下是我认为与代码相关的内容.例如,我使用MainWindow中的MyAudioFileSelector将数据文件中的某些设置加载到UI中,我不确定MVVM的操作方式是什么.

Below is what I think is relevant in the code. I use, for example, MyAudioFileSelector from MainWindow to load some settings from the data file into the UI and I am not sure what is the MVVM way of doing this.

(我现在假设只有一个AudioFileSelector和一个ImageFileSelector,但将来可能会有更多.)

internal Control GetRootControlFromContentPresenter(ContentPresenter container)
{
    // what to put here?
    return null;
}
internal AudioFileSelector MyAudioFileSelector
{
    get
    {
        foreach (SettingDataVM vm in MyItemsControl.ItemsSource)
        {
            if (vm is AudioFileSettingDataVM)
            {
                return (AudioFileSelector)GetRootControlFromContentPresenter(
                    (ContentPresenter)MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm));
            }
        }
        return null;
    }
}
internal ImageFileSelector MyImageFileSelector
{
    get
    {
        foreach (SettingDataVM vm in MyItemsControl.ItemsSource)
        {
            if (vm is ImageFileSettingDataVM)
            {
                return (ImageFileSelector)GetRootControlFromContentPresenter(
                    (ContentPresenter)MyItemsControl.ItemContainerGenerator.ContainerFromItem(vm));
            }
        }
        return null;
    }
}

测试示例

XAML

<Window x:Class="wpf_test_6.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:wpf_test_6"
        mc:Ignorable="d"
        Title="MainWindow" Height="202" Width="274">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:ViewModel1}">
            <TextBlock>view model 1</TextBlock>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ViewModel2}">
            <TextBlock>view model 2</TextBlock>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <ItemsControl x:Name="MyItemsControl" Loaded="MyItemsControl_Loaded">
        </ItemsControl>
    </Grid>
</Window>

C#隐藏代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void MyItemsControl_Loaded(object sender, RoutedEventArgs e)
    {
        var oc = new ObservableCollection<ViewModelBase>();
        oc.Add(new ViewModel1());
        oc.Add(new ViewModel2());
        MyItemsControl.ItemsSource = oc;
        Dispatcher.BeginInvoke(new Action(() =>
        {
            var container = (ContentPresenter)MyItemsControl.ItemContainerGenerator.ContainerFromItem(oc[0]);
            // here container.ContentTemplate is null
            Debugger.Break();
        }), System.Windows.Threading.DispatcherPriority.Loaded);
    }
}
public class ViewModelBase
{
}
public class ViewModel1 : ViewModelBase
{
}
public class ViewModel2 : ViewModelBase
{
}

我的另一个相关问题是此处.

Another relevant question of mine is here.

谢谢.

  1. 在我的实际程序中,我有更复杂的DataTemplate. TextBlock只是一个例子.
  2. 我需要一个ContentTemplate来为特定的容器/项目/索引查找所使用的DataTemplate.我使用了多个根据其DataType自动应用的DataTemplate.
  1. In my actual program I have more complex DataTemplates. The TextBlock is just an example.
  2. I need a ContentTemplate to find, for a specific container/item/index, the DataTemplate that was used. I use multiple DataTemplates applied automatically based on their DataType.

更新2

我需要DataTemplate在应用程序的设置"窗口中显示ItemsControl中的不同控件,每个控件的DataContext设置为每种设置类型的ViewModel子类型的实例. CheckBoxSettingDataVMAudioFileSettingDataVM等都从SettingDataVM继承.

Update 2

I need DataTemplates to display in a Settings window of an application different controls in an ItemsControl, each with the DataContext set to an instance of a ViewModel subtype for each setting type, e.g. CheckBoxSettingDataVM, AudioFileSettingDataVM etc. all inheriting from SettingDataVM.

我不想显式地分配ContentTemplate属性,因此我想从项目(ViewModel)中获取它,我可以获取容器(类型为ContentPresenter),并从中获取根元素在ViewModel的隐式DataTemplate中,可以为AudioFileSelectorImageFileSelector或其他类型.我需要ContentTemplate属性与null不同,以便将来可以存储对AudioFileSelectorImageFileSelector以及其他引用的引用.我将使用这些引用将应用程序打开的文件中的某些设置加载到这些Control中.

I do not want to assign the ContentTemplate property explicitly, I want to get it so, from an item (ViewModel) I can get the container (of type ContentPresenter) and from it I can get the root element inside the implicit DataTemplate for the ViewModel, which can be AudioFileSelector, ImageFileSelector or other type. I need the ContentTemplate property to be different than null so I can store a reference to the AudioFileSelector and to the ImageFileSelector and maybe others in the future. I will use these references to load some settings from the application's opened file into these Controls.

也许我做错了什么,但我仍在学习MVVM.我认为,如果我可以设置DataTemplateDataType,并且即使它具有资源密钥,它也将自动应用于它们范围内的ItemsControl内.

Maybe I am doing something wrong, but I am still learning MVVM. I think that my problem would be solved if I could set the DataType of the DataTemplate and, even if it has a resource key, it would still be automatically applied inside the ItemsControls in their scope.

我试图通过制定这个方案来更好地理解,希望它能有所帮助(我意识到这只是复杂的事情,但这是我的问题的一部分.):

I tried to understand better by making this scheme, I hope it helps (I realized it just complicated things, but it is part of my question.):

推荐答案

通过以下代码,您可以通过ItemsControl为给定的ViewModel对象检索实例化的DataTemplate的根可视对象,方法如下: :

From your code-behind, you can retrieve the root visual object of an instantiated DataTemplate by an ItemsControl for a given ViewModel object by doing the following:

//Assuming you have access to a viewModel variable and to your MyItemsControl:
//We retrieve the generated container
var container = MyItemsControl.ItemContainerGenerator.ContainerFromItem(viewModel) as FrameworkElement;
//We retrieve the closest ContentPresenter in the visual tree
FrameworkElement firstContentPresenter = FindVisualSelfOrChildren<ContentPresenter>(container);
//We get the first child which is the root of the DataTemplate
FrameworkElement visualRoot = (FrameworkElement)VisualTreeHelper.GetChild(firstContentPresenter, 0); //this is what you want

您需要此帮助程序功能,该功能可以将可视化树分解下来,以查找正确类型的第一个子项.

You need this helper function which parses the visual tree down, looking for the first child of the correct type.

/// <summary>
/// Parses the visual tree down looking for the first descendant (or self if correct type) of the given type.
/// </summary>
/// <typeparam name="T">Type of the descendant to find in the visual tree</typeparam>
/// <param name="child">Visual element to find descendant of</param>
/// <returns>First visual descendant of the given type or null. Can be the passed object itself if type is correct.</returns>
public static T FindVisualSelfOrChildren<T>(DependencyObject parent) where T : DependencyObject {
    if (parent == null) {
        //we've reached the end of the tree
        return null;
    }
    if (parent is T) {
        return parent as T;
    }
    //We get the immediate children
    IEnumerable<DependencyObject> children = Enumerable.Range(0, VisualTreeHelper.GetChildrenCount(parent)).Select(i => VisualTreeHelper.GetChild(parent, i));
    //We parse them to get the first child of correct type
    foreach (var child in children) {
        T result = FindVisualSelfOrChildren<T>(child);
        if (result != null) {
            return result as T;
        }
    }
    //Nothing found
    return null;
}

这篇关于没有资源密钥时,如何在应用的DataTemplate中获取根元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆