Можно ли передать несколько ресурсов в DataTemplate?

Я хочу повторно использовать DataTemplate для нескольких столбцов в ListView. Учитывая два XmlDataProvider Я выбираю значения из второго, используя выбранный элемент в первом. Это работает, если я укажу дополнительный ресурс в DataTemplate, Но это вынуждает меня дублировать код DataTemplate и просто обмениваться дополнительным ресурсом. Что я хотел бы сделать, это:

<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>

Для полноты (и ссылки) вот возможный конвертер:

  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 не будет работать и выдает следующую ошибку: "Невозможно установить MultiBinding, поскольку необходимо указать MultiValueConverter.". MultiBinding это просто заполнитель для того, что я хочу сделать. Исследования не выявили какой-либо возможности передать дополнительные параметры DataTemplate - но я не могу поверить, что что-то настолько полезное не спрятано где-то.

Так как мне передать дополнительный ресурс в DataTemplate сразу после DataContext?

1 ответ

Решение

После долгих отладок и обсуждений я нашел решение вышеуказанной проблемы. Для передачи дополнительных данных в шаблон можно прикрепить свойства к родительским элементам в иерархии. К сожалению, вещь, к которой у нас есть доступ - GridViewColumn не отображается в визуальном дереве. Чтобы иметь возможность указать правильные ресурсы, мы должны немного обернуть вещи. Я изменил приведенный выше пример, чтобы завершить его, так что это немного дольше:

<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:

  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 и нужно просто изменить ресурсы, над которыми он работает, это действительно хорошее решение.

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