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