В XAML я могу объявить DataTemplate, чтобы шаблон использовался всякий раз, когда отображается определенный тип. Например, этот DataTemplate будет использовать TextBlock для отображения имени клиента:
<DataTemplate DataType = "{x:Type my:Customer}">
<TextBlock Text = "{Binding Name}" />
</DataTemplate>
Мне интересно, можно ли определить DataTemplate, который будет использоваться каждый раз, когда отображается IList <Customer>. Итак, если ContentControl Content является, скажем, ObservableCollection <Customer>, он будет использовать этот шаблон.
Можно ли объявить универсальный тип, такой как IList, в XAML с помощью расширения разметки {x: Type}?
Для последних версий фреймворка см. stackoverflow.com/questions/7573712/…





Не из коробки, нет; но есть предприимчивые разработчики, которые это сделали.
Майк Хиллберг из Microsoft, например, играл с ним в эта почта. У Google, конечно, есть и другие.
Не непосредственно в XAML, однако вы можете ссылаться на DataTemplateSelector из XAML, чтобы выбрать правильный шаблон.
public class CustomerTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DataTemplate template = null;
if (item != null)
{
FrameworkElement element = container as FrameworkElement;
if (element != null)
{
string templateName = item is ObservableCollection<MyCustomer> ?
"MyCustomerTemplate" : "YourCustomerTemplate";
template = element.FindResource(templateName) as DataTemplate;
}
}
return template;
}
}
public class MyCustomer
{
public string CustomerName { get; set; }
}
public class YourCustomer
{
public string CustomerName { get; set; }
}
Словарь ресурсов:
<ResourceDictionary
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local = "clr-namespace:WpfApplication1"
>
<DataTemplate x:Key = "MyCustomerTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height = "Auto"/>
<RowDefinition Height = "150"/>
</Grid.RowDefinitions>
<TextBlock Text = "My Customer Template"/>
<ListBox ItemsSource = "{Binding}"
DisplayMemberPath = "CustomerName"
Grid.Row = "1"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key = "YourCustomerTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height = "Auto"/>
<RowDefinition Height = "150"/>
</Grid.RowDefinitions>
<TextBlock Text = "Your Customer Template"/>
<ListBox ItemsSource = "{Binding}"
DisplayMemberPath = "CustomerName"
Grid.Row = "1"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
Окно XAML:
<Window
x:Class = "WpfApplication1.Window1"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
Title = "Window1"
Height = "300"
Width = "300"
xmlns:local = "clr-namespace:WpfApplication1"
>
<Grid>
<Grid.Resources>
<local:CustomerTemplateSelector x:Key = "templateSelector"/>
</Grid.Resources>
<ContentControl
Content = "{Binding}"
ContentTemplateSelector = "{StaticResource templateSelector}"
/>
</Grid>
</Window>
Код окна позади:
public partial class Window1
{
public Window1()
{
InitializeComponent();
ObservableCollection<MyCustomer> myCustomers
= new ObservableCollection<MyCustomer>()
{
new MyCustomer(){CustomerName = "Paul"},
new MyCustomer(){CustomerName = "John"},
new MyCustomer(){CustomerName = "Mary"}
};
ObservableCollection<YourCustomer> yourCustomers
= new ObservableCollection<YourCustomer>()
{
new YourCustomer(){CustomerName = "Peter"},
new YourCustomer(){CustomerName = "Chris"},
new YourCustomer(){CustomerName = "Jan"}
};
//DataContext = myCustomers;
DataContext = yourCustomers;
}
}
Возможно лучшее и простое решение
Я считаю, что это легче понять, чем другие руководства по выбору шаблонов. Спасибо
У aelij (координатора проекта WPF Contrib) есть еще один путь для этого.
Что еще круче (даже если это когда-нибудь в будущем) ... то, что XAML 2009 (XAML 2006 - текущая версия) будет поддерживать это изначально. Ознакомьтесь с этим Сессия PDC 2008, чтобы узнать об этом и многое другое.
XAML 2009 поддерживается только (начиная с .NET 4.0, WPF 4.0) в свободных файлах xaml. То есть Blend, Cider (дизайнер Visual Studio) и скомпилированный BAML (в который компилируется ваш встроенный xaml) ... не поддерживают новый синтаксис. Надеюсь, это изменится в будущей версии WPF. Смотрите следующую ссылку и голосуйте: dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/…
Вы также можете заключить свой универсальный класс в производный класс, который определяет T
public class StringList : List<String>{}
и используйте StringList из XAML.
Это сработало для меня. Немного странно иметь полностью пустой класс, но он выполняет свою работу.
Я создал общую оболочку, используя эту технику в stackoverflow.com/a/33827448/11635
Совершенно противоречит цели универсального, но вы можете определить класс, производный от универсального, подобным образом, с единственной целью - иметь возможность использовать этот тип в XAML.
public class MyType : List<int> { }
И используйте его в xaml, например. нравиться
<DataTemplate DataType = {x:Type myNamespace:MyType}>
На самом деле у вас есть 2 проблемы: во-первых, DataTemplate не поддерживает интерфейсы, а второй - универсальные.