wpf在代码隐藏中应用数据模板
我正在创建一个自定义ItemsControl,其中包含用于显示任何内容的网格。我希望用户能够使用datatemplates来显示这些内容,但是我该怎么做呢 我知道如何创建模板,但我不确定如何应用模板,以使项目正确地位于网格中(我的代码),并根据用户的需要(通过datatemplate)显示每个项目 --编辑--wpf在代码隐藏中应用数据模板,wpf,datatemplate,Wpf,Datatemplate,我正在创建一个自定义ItemsControl,其中包含用于显示任何内容的网格。我希望用户能够使用datatemplates来显示这些内容,但是我该怎么做呢 我知道如何创建模板,但我不确定如何应用模板,以使项目正确地位于网格中(我的代码),并根据用户的需要(通过datatemplate)显示每个项目 --编辑-- 我问的问题似乎有点困惑。想象一下,我想从头开始创建自己的ListView,使用网格作为布局(这不是我实际要做的,只是作为一个示例…)。如果用户提供了一个DataTemplate,我如何使
我问的问题似乎有点困惑。想象一下,我想从头开始创建自己的ListView,使用网格作为布局(这不是我实际要做的,只是作为一个示例…)。如果用户提供了一个DataTemplate,我如何使用它来确保每个网格单元中的元素都按照模板显示?控件可以公开自己的属性,这些属性是您在代码中声明的。 如果需要一个
DataTemplate
,则可以公开类型为DataTemplate
的属性。用户在XAML中声明控件类型时,可以提供以下模板:
<ns:YourControl>
<ns:YourControl.DataTemplate>
<DataTemplate>
…
</DataTemplate>
</ns:YourControl.DataTemplate>
</ns:YourControl>
MyControl.xaml.cs
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemTemplateProperty
= DependencyProperty.Register("ItemTemplate", typeof (DataTemplate),
typeof (MyControl), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// Other dependency properties (ItemsSource, SelectedItem, etc.)
}
消费者:
<Grid>
<ns:MyControl ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem}">
<ns:MyControl.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2"
BorderBrush="Black">
<TextBlock Foreground="DarkGray"
Text="{Binding Name}"
Margin="4" />
</Border>
</DataTemplate>
</ns:MyControl.ItemTemplate>
</ns:MyControl>
</Grid>
以及背后的代码:
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof (IEnumerable), typeof (MyControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public IEnumerable ItemsSource
{
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// This is the DataTemplate that the consumer of your control specifies
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof (DataTemplate), typeof (MyControl), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// This is declared private, because it is only to be consumed by this control
private static readonly DependencyProperty BindableItemsProperty = DependencyProperty.Register(
"BindableItems", typeof (ObservableCollection<object>), typeof (MyControl), new PropertyMetadata(new ObservableCollection<object>()));
private ObservableCollection<object> BindableItems
{
get { return (ObservableCollection<object>) GetValue(BindableItemsProperty); }
set { SetValue(BindableItemsProperty, value); }
}
private static void OnItemsSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var myControl = dependencyObject as MyControl;
if (myControl == null)
{
return;
}
// Get reference to the Grid using reflection. You could also walk the tree.
var grid = (Grid) typeof (ItemsControl).InvokeMember("ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null, myControl.ItemsControl, null);
var columns = grid.ColumnDefinitions;
columns.Clear();
myControl.BindableItems.Clear();
var items = args.NewValue as IEnumerable;
if (items != null)
{
var columnIndex = 0;
foreach (var item in items)
{
columns.Add(new ColumnDefinition{ Width = GridLength.Auto });
var container = new MyItem
{
Row = columnIndex,
Column = columnIndex++,
Content = item
};
myControl.BindableItems.Add(container);
}
}
}
}
public class MyItem
{
public object Content { get; set; }
public int Row { get; set; }
public int Column { get; set; }
}
}
使用系统集合;
使用System.Collections.ObjectModel;
运用系统反思;
使用System.Windows;
使用System.Windows.Controls;
命名空间WpfApplication1
{
公共部分类MyControl:UserControl
{
公共MyControl()
{
初始化组件();
}
公共静态只读DependencyProperty项SourceProperty=DependencyProperty.Register(
“ItemsSource”、typeof(IEnumerable)、typeof(MyControl),
新属性元数据(默认值(IEnumerable),OnItemSourceChanged);
公共IEnumerable ItemsSource
{
get{return(IEnumerable)GetValue(ItemsSourceProperty);}
set{SetValue(ItemsSourceProperty,value);}
}
//这是控件使用者指定的数据模板
公共静态只读DependencyProperty ItemTemplateProperty=DependencyProperty.Register(
“ItemTemplate”、typeof(DataTemplate)、typeof(MyControl)、新PropertyMetadata(默认值(DataTemplate));
公共数据模板ItemTemplate
{
获取{return(DataTemplate)GetValue(ItemTemplateProperty);}
set{SetValue(ItemTemplateProperty,value);}
}
//这被声明为私有,因为它只能由该控件使用
私有静态只读DependencyProperty BindableItemsProperty=DependencyProperty.Register(
“BindableItems”、typeof(ObservableCollection)、typeof(MyControl)、new PropertyMetadata(new ObservableCollection());
私有可观察集合可绑定项
{
get{return(ObservableCollection)GetValue(BindableItemsProperty);}
set{SetValue(BindableItemsProperty,value);}
}
私有静态资源已更改(DependencyObject DependencyObject,DependencyPropertyChangedEventArgs args)
{
var myControl=作为myControl的dependencyObject;
if(myControl==null)
{
返回;
}
//使用反射获取对网格的引用。您还可以在树上漫游。
var grid=(grid)typeof(ItemsControl).InvokeMember(“ItemsHost”,
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null,myControl.ItemsControl,null);
var columns=grid.ColumnDefinitions;
columns.Clear();
myControl.BindableItems.Clear();
var items=args.NewValue作为IEnumerable;
如果(项!=null)
{
风险价值指数=0;
foreach(项目中的var项目)
{
Add(新的ColumnDefinition{Width=GridLength.Auto});
var container=newmyitem
{
行=列索引,
Column=columnIndex++,
内容=项目
};
myControl.BindableItems.Add(容器);
}
}
}
}
公共类MyItem
{
公共对象内容{get;set;}
公共int行{get;set;}
公共int列{get;set;}
}
}
使用DataTemplateSelector选择模板。不要做代码隐藏的事情。正确学习WPF并使用正确的XAML和DataBinding@HighCore请提供更有用的评论。我对我的问题进行了编辑,以便对我正在做的事情进行更多的澄清。如果你能告诉我如何通过XAML做到这一点,那就太好了。如果我已经知道怎么做,我就不会在这里问这个问题。@ryan0270我不能提供完整的答案,因为我现在很忙。我有上百个答案解释了如何正确使用WPF,而不是代码隐藏的东西(我可以用它将你的问题标记为重复并关闭它)。搜索我的答案历史记录或谷歌“stackoverflow highcore delete all your code”(删除所有代码),你就会得到它。让我们先尝试成为一个有帮助的社区,然后再尝试成为一个坚持者。正如我们所知,向自定义控件添加属性意味着在代码隐藏中工作;OP试图了解所有的片段是如何连接在一起的。如果你发现一个真正相关的重复问题,并有一个像样的答案,请发布一个链接。对不起,但我仍然感到困惑。首先,绑定的一端有DataTemplate,但另一端有什么?其次,如果控件的ItemsSource绑定到字符串列表,例如,如何通过模板传递每个字符串,然后将其显示在相应的网格单元中?@ryan0270我添加了一个示例以供参考。如果这无助于澄清问题,请告诉我。您没有在UI元素中放置常规CLR属性。你的代码错了。请改用适当的DependencyProperties。顺便说一句,看起来您正在重新设计ItemsControl
。没有必要这样做。这是一个明确的例子,因为我不知道OP的确切用例。我仍然不知道如何做布局。如何指定将datatemplate应用于第一个源项的结果应该放在网格的单元格(0,0)中
<UserControl x:Class="WpfApplication1.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="ThisControl"
d:DesignHeight="300" d:DesignWidth="300">
<ItemsControl x:Name="ItemsControl"
ItemsSource="{Binding BindableItems, ElementName=ThisControl, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Grid.Column" Value="{Binding Column}" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter Content="{Binding Content}"
ContentTemplate="{Binding ItemTemplate, ElementName=ThisControl}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</UserControl>
using System.Collections;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof (IEnumerable), typeof (MyControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public IEnumerable ItemsSource
{
get { return (IEnumerable) GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// This is the DataTemplate that the consumer of your control specifies
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof (DataTemplate), typeof (MyControl), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate) GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// This is declared private, because it is only to be consumed by this control
private static readonly DependencyProperty BindableItemsProperty = DependencyProperty.Register(
"BindableItems", typeof (ObservableCollection<object>), typeof (MyControl), new PropertyMetadata(new ObservableCollection<object>()));
private ObservableCollection<object> BindableItems
{
get { return (ObservableCollection<object>) GetValue(BindableItemsProperty); }
set { SetValue(BindableItemsProperty, value); }
}
private static void OnItemsSourceChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var myControl = dependencyObject as MyControl;
if (myControl == null)
{
return;
}
// Get reference to the Grid using reflection. You could also walk the tree.
var grid = (Grid) typeof (ItemsControl).InvokeMember("ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null, myControl.ItemsControl, null);
var columns = grid.ColumnDefinitions;
columns.Clear();
myControl.BindableItems.Clear();
var items = args.NewValue as IEnumerable;
if (items != null)
{
var columnIndex = 0;
foreach (var item in items)
{
columns.Add(new ColumnDefinition{ Width = GridLength.Auto });
var container = new MyItem
{
Row = columnIndex,
Column = columnIndex++,
Content = item
};
myControl.BindableItems.Add(container);
}
}
}
}
public class MyItem
{
public object Content { get; set; }
public int Row { get; set; }
public int Column { get; set; }
}
}