是否可以将多个资源传递到DataTemplate中? [英] Is it possible to pass multiple resources into a DataTemplate?

查看:58
本文介绍了是否可以将多个资源传递到DataTemplate中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想为ListView中的多个列重用DataTemplate。给定两个 XmlDataProvider ,我通过使用第一个中的选定项从第二个中选择值。
如果我在 DataTemplate 中指定其他资源,则此方法有效。但这迫使我复制DataTemplate的代码,而只是交换附加资源。
我想做的是这样:

I want to reuse a DataTemplate for multiple columns in a ListView. Given the two XmlDataProvider I select values from the second by using the selected item in the first. This works if I specify the additional resource in the DataTemplate. But this forces me to duplicate the code of the DataTemplate and just exchange the addtional resource. What I would like to do is this:

<Window x:Class="LayoutTests.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:LayoutTests"
        Title="Window2" Height="300" Width="300">
  <Window.Resources>
    <XmlDataProvider x:Key="XmlDataA" IsInitialLoadEnabled="True">
      <x:XData>
        <Items xmlns="">
          <Item id="1" text="A:1"/>
          <Item id="2" text="A:2"/>
          <Item id="3" text="A:3"/>
        </Items>
      </x:XData>
    </XmlDataProvider>
    <XmlDataProvider x:Key="XmlDataB" IsInitialLoadEnabled="True">
      <x:XData>
        <Items xmlns="">
          <Item id="1" text="B:1"/>
          <Item id="2" text="B:2"/>
          <Item id="3" text="B:3"/>
        </Items>
      </x:XData>
    </XmlDataProvider>
    <local:MultiXmlConverter x:Key="MultiXmlConverter"/>
    <local:DatabindingDebugConverter x:Key="DatabindingDebugConverter"/>
    <DataTemplate x:Key="Template" >
      <TextBlock Text="{Binding Converter={StaticResource MultiXmlConverter}}"/>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <ListView ItemsSource="{Binding Source={StaticResource XmlDataA}, XPath='/Items/Item'}" Background="Transparent">
      <ListView.View>
        <GridView>
          <GridViewColumn CellTemplate="{StaticResource Template}">
            <GridViewColumn.DisplayMemberBinding>
              <MultiBinding>
                <Binding Path="/"/>
                <Binding Source="{StaticResource XmlDataB}"/>
              </MultiBinding>
            </GridViewColumn.DisplayMemberBinding>
          </GridViewColumn>
        </GridView>
      </ListView.View>
    </ListView>
  </Grid>
</Window>

为了完整性(和参考),这里是一个可能的转换器:

For completeness (and reference) here is a possible converter:

  public class MultiXmlConverter : IMultiValueConverter
  {
    public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      var element = value[0] as XmlElement;
      var dataProvider = value[1] as XmlDataProvider;
      XmlNodeList nodes = dataProvider.Document.SelectNodes("/Items/Item/[@id='" + element.Attributes["id"].Value.ToString() + "']");
      return nodes[0].Attributes["Text"].Value.ToString();
    }
    public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new Exception("The method or operation is not implemented.");
    }
  }

请注意,上述XAML代码将无法正常工作并产生出现以下错误:由于必须指定MultiValueConverter,因此无法设置MultiBinding。。 MultiBinding 只是我想做的事情的占位符。研究没有发现将其他参数传递给 DataTemplate 的可能性-但我无法相信如此有用的东西并未隐藏在某处。

Note that above XAML code will not work and produces the following error: "Cannot set MultiBinding because MultiValueConverter must be specified.". The MultiBinding is merely a placeholder for the thing I want to do. Research didnt reveal any possibility to pass additional parameters to a DataTemplate - but I cant believe that something so usefull is not hidden somewhere.

那么,如何将其他资源传递到 DataContext DataTemplate

So how do I pass an additional resource into a DataTemplate next to the DataContext?

推荐答案

经过大量调试和讨论,我找到了解决上述问题的方法。
要将其他数据传递到模板,可以将属性附加到层次结构中的父元素。不幸的是,我们可以访问的东西- GridViewColumn 没有显示在视觉树中。为了能够指定正确的资源,我们必须进行一些包装。我将上面的示例修改为完整,因此需要更长的时间:

After a lot of debugging and discussion I found a solution to above problem. To pass additional data to a template one can attach properties to parent elements in the hierarchy. Unfortunately the thing where we have access - the GridViewColumn isn't showing up in the visual tree. To be able to specify the right resources, we have to wrap things up a little bit. I modified above example to be complete so this is a little bit longer:

<Window x:Class="LayoutTests.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:LayoutTests"
        Title="Window2" Height="300" Width="300">
  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
          <XmlDataProvider x:Key="XmlDataA" IsInitialLoadEnabled="True">
            <x:XData>
              <Items xmlns="">
                <Item id="1" text="A:1"/>
                <Item id="2" text="A:2"/>
                <Item id="3" text="A:3"/>
              </Items>
            </x:XData>
          </XmlDataProvider>
        </ResourceDictionary>
        <ResourceDictionary>
          <XmlDataProvider x:Key="XmlDataB" IsInitialLoadEnabled="True">
            <x:XData>
              <Items xmlns="">
                <Item id="1" text="B:1"/>
                <Item id="2" text="B:2"/>
                <Item id="3" text="B:3"/>
              </Items>
            </x:XData>
          </XmlDataProvider>
        </ResourceDictionary>
        <ResourceDictionary>
          <local:MultiXmlConverter x:Key="MultiXmlConverter"/>
          <local:DatabindingDebugConverter x:Key="DatabindingDebugConverter"/>
          <DataTemplate x:Key="Template" >
            <TextBlock>
              <TextBlock.Text>
                <MultiBinding Converter="{StaticResource MultiXmlConverter}">
                  <Binding/>
                  <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="(local:Window2.AttachedXmlDataProvider)"/>
                </MultiBinding>
              </TextBlock.Text>
            </TextBlock>
          </DataTemplate>
          <DataTemplate x:Key="TemplateA">
            <ContentPresenter ContentTemplate="{StaticResource Template}" local:Window2.AttachedXmlDataProvider="{StaticResource XmlDataA}"/>
          </DataTemplate>
          <DataTemplate x:Key="TemplateB">
            <ContentPresenter ContentTemplate="{StaticResource Template}" local:Window2.AttachedXmlDataProvider="{StaticResource XmlDataB}"/>
          </DataTemplate>
        </ResourceDictionary>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>
  <Grid>
    <ListView ItemsSource="{Binding Source={StaticResource XmlDataA}, XPath='/Items/Item'}" Background="Transparent">
      <ListView.View>
        <GridView>
          <GridViewColumn CellTemplate="{StaticResource TemplateA}"/>
          <GridViewColumn CellTemplate="{StaticResource TemplateB}"/>
        </GridView>
      </ListView.View>
    </ListView>
  </Grid>
</Window>

.cs文件中的内容:

And the stuff from the .cs file:

  public partial class Window2 : Window
  {
    public Window2()
    {
      InitializeComponent();
    }

    public static readonly DependencyProperty AttachedXmlDataProviderProperty =
    DependencyProperty.RegisterAttached("AttachedXmlDataProvider",
                                         typeof(XmlDataProvider),
                                         typeof(Window2),
                                         new FrameworkPropertyMetadata((XmlDataProvider)null, FrameworkPropertyMetadataOptions.AffectsRender));
    public static void SetAttachedXmlDataProvider(DependencyObject depObj, XmlDataProvider value)
    {
      depObj.SetValue(AttachedXmlDataProviderProperty, value);
    }
    public static XmlDataProvider GetAttachedXmlDataProvider(DependencyObject depObj)
    {
      return (XmlDataProvider)depObj.GetValue(AttachedXmlDataProviderProperty);
    }
  }

  public class MultiXmlConverter : IMultiValueConverter
  {
    public object Convert(object[] value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      var element = value[0] as XmlElement;
      var dataProvider = value[1] as XmlDataProvider;
      string id = element.Attributes["id"].Value.ToString();
      if( dataProvider.Document == null )
        return null;
      XmlNodeList nodes = dataProvider.Document.SelectNodes("/Items/Item[@id='" + id + "']");
      string result = nodes[0].Attributes["text"].Value;
      return result;
    }
    public object[] ConvertBack(object value, Type[] targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new Exception("The method or operation is not implemented.");
    }
  }

上面代码的优点在于我们将不同的资源集成到 DataTemplate 中,并且仅需少量代码即可交换资源。即在编写 DataTemplate 来简单包装真实模板的过程中。
从上面的示例中可能并不明显,但是如果您有一个复杂的 DataTemplate ,并且只需要更改正在处理的资源,那么这是一个非常好的解决方案

The beauty of the code above lies in the fact that we integrate different resources in a DataTemplate and can exchange the resource with only a small amount of code. Namely in writing a DataTemplate that simply wraps up the real template. It may not be obvious from above example, but if you have a real complicate DataTemplate and need to just change the resources it's working on this is a really nice solution.

这篇关于是否可以将多个资源传递到DataTemplate中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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