创建使用数据模板的 WPF 控件
Creating a WPF control that uses datatemplates
我正在尝试构建一个控件,该控件将在某种日程安排网格中显示项目,以便根据项目所属的类别水平放置项目,并根据安排的时间和时间垂直放置项目该项目将需要完成。我目前继承自System.Windows.Controls.Control,默认样式和模板如下所示:
<Style TargetType="KHS:TimeGrid">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="KHS:TimeGrid">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<DockPanel>
<Grid Name="PART_HeaderGrid" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ActualWidth, ElementName=TimeColumn}"/>
</Grid.ColumnDefinitions>
</Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid Name="PART_BodyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="TimeColumn" Width="Auto"/>
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
</DockPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
对于我的项目,我只是使用这个接口:
Public Interface ITimeGridItem
Property StartTime As Date
Property Duration As TimeSpan
End Interface
为每个时间增量(默认表示 15 分钟)向 "BodyGrid" 添加行,并根据项目的开始时间和持续时间。
当我尝试为项目实施 DataTemplates 时,我的问题就来了。我目前正在尝试使用 ContentPresenters 来显示项目。为 TimeGrid 创建一个 ItemTemplate 属性 并将其用于所有项目会很容易,但是如果我需要使用唯一模板对不同类型的项目进行模板化怎么办?
这让我开始思考其他控件如何将 DataTemplates 添加到具有不同数据类型的资源集合中,并且该控件知道如何搜索资源并选择正确的模板。不幸的是,我一直无法弄清楚如何实现它。任何人都知道我怎么能做到这一点?这一定是可能的,因为微软在他们的控制中做到了。
我对你的具体实现了解不多,无法给你一个确切的答案,但我可能会考虑使用你应用自定义模板的包装控件,然后在其中放置一个 ContentControl 来呈现子项用他们自己模板化的任何东西来查看。
老实说,虽然我不会为这样的事情使用网格,但我过去的做法是使用数据绑定。为所有要显示的项目创建一个视图模型,并为其指定 X 和 Y 属性。然后在您的 XAML 中使用一个以 Canvas 作为背景并将项目的 Canvas.Top 和 Canvas.Left dp 绑定到 X 和 Y 的单个 ItemsControl 绘制它们。这样,您的视图模型代码就可以完全自由地将子项准确定位到您想要的位置,并且您还可以进行单元测试,而无需自己调用视图 类。一旦我不再试图在 XAML 上变得过于聪明,而是坚持将其用于松散耦合的数据绑定,复杂的 GUI 设计突然变得非常容易实现。
天哪,我简直不敢相信它是如此简单。实际上,我必须感谢@ed-plunkett 的无懈可击的评论。我仔细看了看,无意中发现,如果我改变这个:
<DataTemplate DataType="local:TestItem">
对此:
<DataTemplate DataType="{x:Type local:TestItem}">
有效!
几分钟后,我找到了这篇文章:
http://briannoyesblog.azurewebsites.net/2013/11/23/use-xtype-with-wpf-implicit-datatemplates
*叹气*我希望我早点发现。
所以只要我使用 {x:Type} 语法,我就可以将 DataTemplate 放在范围内的任何位置,它就会被应用。
我正在尝试构建一个控件,该控件将在某种日程安排网格中显示项目,以便根据项目所属的类别水平放置项目,并根据安排的时间和时间垂直放置项目该项目将需要完成。我目前继承自System.Windows.Controls.Control,默认样式和模板如下所示:
<Style TargetType="KHS:TimeGrid">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="KHS:TimeGrid">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<DockPanel>
<Grid Name="PART_HeaderGrid" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ActualWidth, ElementName=TimeColumn}"/>
</Grid.ColumnDefinitions>
</Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid Name="PART_BodyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Name="TimeColumn" Width="Auto"/>
</Grid.ColumnDefinitions>
</Grid>
</ScrollViewer>
</DockPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
对于我的项目,我只是使用这个接口:
Public Interface ITimeGridItem
Property StartTime As Date
Property Duration As TimeSpan
End Interface
为每个时间增量(默认表示 15 分钟)向 "BodyGrid" 添加行,并根据项目的开始时间和持续时间。
当我尝试为项目实施 DataTemplates 时,我的问题就来了。我目前正在尝试使用 ContentPresenters 来显示项目。为 TimeGrid 创建一个 ItemTemplate 属性 并将其用于所有项目会很容易,但是如果我需要使用唯一模板对不同类型的项目进行模板化怎么办?
这让我开始思考其他控件如何将 DataTemplates 添加到具有不同数据类型的资源集合中,并且该控件知道如何搜索资源并选择正确的模板。不幸的是,我一直无法弄清楚如何实现它。任何人都知道我怎么能做到这一点?这一定是可能的,因为微软在他们的控制中做到了。
我对你的具体实现了解不多,无法给你一个确切的答案,但我可能会考虑使用你应用自定义模板的包装控件,然后在其中放置一个 ContentControl 来呈现子项用他们自己模板化的任何东西来查看。
老实说,虽然我不会为这样的事情使用网格,但我过去的做法是使用数据绑定。为所有要显示的项目创建一个视图模型,并为其指定 X 和 Y 属性。然后在您的 XAML 中使用一个以 Canvas 作为背景并将项目的 Canvas.Top 和 Canvas.Left dp 绑定到 X 和 Y 的单个 ItemsControl 绘制它们。这样,您的视图模型代码就可以完全自由地将子项准确定位到您想要的位置,并且您还可以进行单元测试,而无需自己调用视图 类。一旦我不再试图在 XAML 上变得过于聪明,而是坚持将其用于松散耦合的数据绑定,复杂的 GUI 设计突然变得非常容易实现。
天哪,我简直不敢相信它是如此简单。实际上,我必须感谢@ed-plunkett 的无懈可击的评论。我仔细看了看,无意中发现,如果我改变这个:
<DataTemplate DataType="local:TestItem">
对此:
<DataTemplate DataType="{x:Type local:TestItem}">
有效!
几分钟后,我找到了这篇文章:
http://briannoyesblog.azurewebsites.net/2013/11/23/use-xtype-with-wpf-implicit-datatemplates
*叹气*我希望我早点发现。
所以只要我使用 {x:Type} 语法,我就可以将 DataTemplate 放在范围内的任何位置,它就会被应用。