Я пишу программу WPF в C#, в котором у меня есть ListView, для которого столбцы будут заполнены во времени выполнения. Я хотел бы использовать пользовательский DataTemplate для объектов GridViewColumn в ListView.
В примерах я видел, где число столбцов фиксируется заранее, пользовательский DataTemplate часто создается с помощью чего-то как XAML ниже.
<DataTemplate x:Key="someKey">
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
Этот DataTemplate мог также позже быть присвоен GridViewColumn. CellTemplate в коде - позади путем вызова FindResource ("someKey"). Однако это одно бесполезно мне, потому что в этом примере элемент Пути прикреплен к FirstName. Действительно мне нужно что-то, где я могу установить Путь в коде.
Это - мое впечатление, что что-то вдоль этих строк может быть возможным, если бы XamlReader используется, но я не уверен, как на практике я сделал бы это. Любые решения значительно ценятся.
Легко создать то, что вам нужно, используя два рабочих шаблона данных согласованно: внешний DataTemplate просто устанавливает DataContext для внутреннего DataTemplate, как показано ниже:
<DataTemplate x:Key="DisplayTemplate">
<Border ...>
<TextBlock Text="{Binding}" ... />
</Border>
</DataTemplate>
<DataTemplate x:Key="CellTemplate">
<ContentPresenter Content="{Binding FirstName}"
ContentTemplate="{StaticResource DisplayTemplate}" />
</DataTemplate>
Единственная сложность - сделать его удобным для установки этого в GridViewColumn. Я бы сделал это с помощью прикрепленных свойств, которые позволили бы вам написать:
<GridViewColumn
my:GVCHelper.DisplayPath="FirstName"
my:GVCHelper.Template="{StaticResource DisplayTemplate}" />
Или, что эквивалентно, в коде:
var col = new GridViewColumn();
GVCHelper.SetDisplayPath(col, "FirstName");
GVCHelper.SetTemplate(col, (DataTemplate)FindResource("DisplayTemplate"));
Любой из них приведет к использованию DataTemplate с именем «DisplayTemplate» для отображения FirstName в столбце.
Вспомогательный класс может быть реализован следующим образом:
public class GVCHelper : DependencyObject
{
public static string GetDisplayPath(DependencyObject obj) { return (string)obj.GetValue(DisplayPathProperty); }
public static void SetDisplayPath(DependencyObject obj, string value) { obj.SetValue(DisplayPathProperty, value); }
public static readonly DependencyProperty DisplayPathProperty = DependencyProperty.RegisterAttached("DisplayPath", typeof(string), typeof(GVCHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) => Update(obj)
});
public static DataTemplate GetTemplate(DependencyObject obj) { return (DataTemplate)obj.GetValue(TemplateProperty); }
public static void SetTemplate(DependencyObject obj, DataTemplate value) { obj.SetValue(TemplateProperty, value); }
public static readonly DependencyProperty TemplateProperty = DependencyProperty.RegisterAttached("Template", typeof(DataTemplate), typeof(GVCHelper), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) => Update(obj)
});
private static void Update(DependencyObject obj)
{
var path = GetDisplayPath(obj);
var template = GetTemplate(obj);
if(path!=null && template!=null)
{
var factory = new FrameworkElementFactory(typeof(ContentPresenter));
factory.SetBinding(ContentPresenter.ContentProperty, new Binding(path));
factory.SetValue(ContentPresenter.ContentTemplateProperty, template);
obj.SetValue(GridViewColumn.CellTemplateProperty,
new DataTemplate { VisualTree = factory };
}
}
}
Как это работает: при установке обоих свойств создается новый DataTemplate и обновляется свойство GridViewColumn.CellTemplate.
Может быть GridViewColumn.CellTemplateSelector вам поможет? Или вы можете создать пользовательский элемент управления, привязать его к чему-то достаточно большому (одинаковому для каждого столбца). Затем позволить этому элементу управления определить, что именно он должен отображать, основываясь на том, что у него есть в DataContext
...