WPF 自定义 ItemsControl 控件

该控件叫 Summary, 主要是一些汇总信息的显示,有几个地方用,之前都是分散到各个XAML文件里,不统一。

 

本人WPF新手,对XAML了解不多,做这个软件基本上都是用CM,界面布局用 AvalonDock。由于缺乏相关经验,又没有一个能问得上的人指导,写这个控件费了我很长时间(啥时有空啥时动动)。

之前主要做一些功能方面的,没有心思美化界面,现在虽然还有很多功能没写,但是基本上够自己用了,放下心思来做一些界面上的东西,要不然何苦选择WPF?

 

先看一下图:

 

WPF 自定义 ItemsControl 控件

WPF 自定义 ItemsControl 控件

该CustomControl 由4部分组成:

大标题,小标题,值 及 Detail。

虽然细分这么多,但实质上还是一个列表类的控件,所以选择继承自 ItemsControl.

 

做这个控件的时候,遇到了一些详细的问题不知道怎么解决,Google/Bing 都没有找到我要了解的,Baidu 更不用提了,漫天的转载,Copy.

 

1, 类似 ComboBox 的 DisplayMemberPath 如何弄?

既然都自定控件了,当然是想让它适用不同场景, 不能局限于某一个实体类,最好是能像 DisplayMemberPath ValueMemberPath 这样的属性。

这里,我定义了:

ItemTitlePathProperty 及 ItemValuePathProperty 来处理。

 

2,有了上面两个依赖属性,但是还不足以处理更多的场景,最好是能有不同的 Template 。

这里我定义了:

TitleTemplate / ValueTemplate及DetailTemplate.

 

第一个问题,只需定义一个简单的 DataTemplate ,然后用 TemplateBinding 即可做到。但是要和第二个问题结合在一起,就牵扯到模板切换 及 数据切换的问题,因为第一问题所用的数据被指定为某实体类的某个属性,第二个问题的数据要指定为整个实体。

解决第一个问题,需要重写 PrepareContainerForItemOverride 方法,第二个需要重写 OnApplyTemplate

 

不多说了,怕误导大家,毕竟,我懂的不多,上代码,供参考:

模板文件:

  1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  3                         xmlns:local="clr-namespace:AsNum.WPF.Controls"

  4                     >

  5 

  6 

  7     <DataTemplate x:Key="SummaryTitleTemplate">

  8         <TextBlock Text="{Binding}" Foreground="White" />

  9     </DataTemplate>

 10 

 11     <DataTemplate x:Key="SummaryValueTemplate">

 12         <Border HorizontalAlignment="Right" Background="#FF535C7B" CornerRadius="10">

 13             <TextBlock Text="{Binding}" Padding="8,3" Foreground="White" />

 14         </Border>

 15     </DataTemplate>

 16 

 17 

 18     <Style TargetType="{x:Type local:Summary}">

 19         <Setter Property="Template">

 20             <Setter.Value>

 21                 <ControlTemplate TargetType="{x:Type local:Summary}">

 22                     <Border Background="{TemplateBinding Background}"

 23                             BorderBrush="{TemplateBinding BorderBrush}"

 24                             BorderThickness="{TemplateBinding BorderThickness}"

 25                             >

 26 

 27                         <StackPanel Height="auto">

 28                             <Border Padding="20,5" CornerRadius="5,5,0,0" Background="#10a8ab">

 29                                 <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:Summary}}, Path=Title}" FontSize="18.667" Foreground="White" HorizontalAlignment="Center" />

 30                             </Border>

 31                             <Border Background="#FF394165" CornerRadius="0,0,3,3" Padding="5">

 32                                 <ItemsPresenter />

 33                             </Border>

 34                         </StackPanel>

 35 

 36                     </Border>

 37                 </ControlTemplate>

 38             </Setter.Value>

 39         </Setter>

 40     </Style>

 41 

 42     <Style TargetType="{x:Type local:SummaryItem}">

 43         <Setter Property="Template">

 44             <Setter.Value>

 45                 <ControlTemplate TargetType="{x:Type local:SummaryItem}">

 46                     <Border x:Name="Item"

 47                             Padding="3"

 48                             BorderBrush="{TemplateBinding BorderBrush}"

 49                             BorderThickness="{TemplateBinding BorderThickness}">

 50 

 51 

 52                         <Border.Background>

 53                             <SolidColorBrush />

 54                         </Border.Background>

 55 

 56                         <StackPanel>

 57                             <DockPanel>

 58                                 <ContentControl x:Name="PART_Title" 

 59                                                 Content="{TemplateBinding Title}" 

 60                                                 ContentTemplate="{StaticResource SummaryTitleTemplate}"

 61                                                 />

 62 

 63                                 <ContentControl x:Name="PART_Value" 

 64                                                 Content="{TemplateBinding Value}" 

 65                                                 ContentTemplate="{StaticResource SummaryValueTemplate}" 

 66                                                 Width="auto"

 67                                                 DockPanel.Dock="Right"

 68                                                 HorizontalAlignment="Right"

 69                                                 />

 70 

 71                             </DockPanel>

 72                             <ContentControl Margin="0,2,0,2" x:Name="PART_Detail" ContentTemplate="{TemplateBinding DetailTemplate}" />

 73                         </StackPanel>

 74 

 75                         <Border.Triggers>

 76                             <EventTrigger RoutedEvent="Border.MouseEnter">

 77                                 <BeginStoryboard>

 78                                     <Storyboard>

 79                                         <ColorAnimation To="#FF535C7B" Duration="0:0:1"

 80                                                     Storyboard.TargetName="Item"

 81                                                     Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"/>

 82                                     </Storyboard>

 83                                 </BeginStoryboard>

 84                             </EventTrigger>

 85                             <EventTrigger RoutedEvent="Border.MouseLeave">

 86                                 <BeginStoryboard>

 87                                     <Storyboard>

 88                                         <ColorAnimation To="Transparent" Duration="0:0:1"

 89                                                     Storyboard.TargetName="Item"

 90                                                     Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"/>

 91                                     </Storyboard>

 92                                 </BeginStoryboard>

 93                             </EventTrigger>

 94                         </Border.Triggers>

 95 

 96 

 97                     </Border>

 98                 </ControlTemplate>

 99             </Setter.Value>

100         </Setter>

101     </Style>

102 

103 </ResourceDictionary>
View Code

Summary.cs

  1 using System;

  2 using System.Collections.Generic;

  3 using System.Collections.ObjectModel;

  4 using System.Linq;

  5 using System.Text;

  6 using System.Threading.Tasks;

  7 using System.Windows;

  8 using System.Windows.Controls;

  9 using System.Windows.Data;

 10 using System.Windows.Documents;

 11 using System.Windows.Input;

 12 using System.Windows.Media;

 13 using System.Windows.Media.Imaging;

 14 using System.Windows.Navigation;

 15 using System.Windows.Shapes;

 16 

 17 namespace AsNum.WPF.Controls {

 18     public class Summary : ItemsControl {

 19 

 20 

 21         public static DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(Summary));

 22 

 23         public static DependencyProperty ItemTitlePathProperty = DependencyProperty.Register("ItemTitlePath", typeof(string), typeof(Summary));

 24 

 25         public static DependencyProperty ItemValuePathProperty = DependencyProperty.Register("ItemValuePath", typeof(string), typeof(Summary));

 26 

 27         public static DependencyProperty ItemDetailTemplateProperty = DependencyProperty.Register("ItemDetailTemplate", typeof(DataTemplate), typeof(Summary));

 28 

 29         public static DependencyProperty ItemTitleTemplateProperty = DependencyProperty.Register("ItemTitleTemplate", typeof(DataTemplate), typeof(Summary));

 30 

 31         public static DependencyProperty ItemValueTemplateProperty = DependencyProperty.Register("ItemValueTemplate", typeof(DataTemplate), typeof(Summary));

 32 

 33         public string Title {

 34             get {

 35                 return (string)this.GetValue(TitleProperty);

 36             }

 37             set {

 38                 this.SetValue(TitleProperty, value);

 39             }

 40         }

 41 

 42         public string ItemTitlePath {

 43             get {

 44                 return (string)this.GetValue(ItemTitlePathProperty);

 45             }

 46             set {

 47                 this.SetValue(ItemTitlePathProperty, value);

 48             }

 49         }

 50 

 51         public string ItemValuePath {

 52             get {

 53                 return (string)this.GetValue(ItemValuePathProperty);

 54             }

 55             set {

 56                 this.SetValue(ItemValuePathProperty, value);

 57             }

 58         }

 59 

 60         public DataTemplate ItemDetailTemplate {

 61             get {

 62                 return (DataTemplate)this.GetValue(ItemDetailTemplateProperty);

 63             }

 64             set {

 65                 this.SetValue(ItemDetailTemplateProperty, value);

 66             }

 67         }

 68 

 69         public DataTemplate ItemTitleTemplate {

 70             get {

 71                 return (DataTemplate)this.GetValue(ItemTitleTemplateProperty);

 72             }

 73             set {

 74                 this.SetValue(ItemTitleTemplateProperty, value);

 75             }

 76         }

 77 

 78         public DataTemplate ItemValueTemplate {

 79             get {

 80                 return (DataTemplate)this.GetValue(ItemValueTemplateProperty);

 81             }

 82             set {

 83                 this.SetValue(ItemValueTemplateProperty, value);

 84             }

 85         }

 86 

 87         static Summary() {

 88             DefaultStyleKeyProperty.OverrideMetadata(typeof(Summary), new FrameworkPropertyMetadata(typeof(Summary)));

 89         }

 90 

 91         protected override void PrepareContainerForItemOverride(DependencyObject element, object item) {

 92             base.PrepareContainerForItemOverride(element, item);

 93             var ele = element as SummaryItem;

 94 

 95             {

 96                 Binding binding = new Binding();

 97                 ele.SetBinding(SummaryItem.ItemProperty, binding);

 98             }

 99 

100             if (!string.IsNullOrEmpty(this.ItemTitlePath)) {

101                 Binding binding = new Binding(this.ItemTitlePath);

102                 ele.SetBinding(SummaryItem.TitleProperty, binding);

103             }

104 

105             if (!string.IsNullOrEmpty(this.ItemValuePath)) {

106                 Binding binding = new Binding(this.ItemValuePath);

107                 ele.SetBinding(SummaryItem.ValueProperty, binding);

108             }

109 

110             ele.DetailTemplate = this.ItemDetailTemplate;

111             ele.TitleTemplate = this.ItemTitleTemplate;

112             ele.ValueTemplate = this.ItemValueTemplate;

113         }

114 

115         protected override DependencyObject GetContainerForItemOverride() {

116             return new SummaryItem();

117         }

118 

119     }

120 }
View Code

SummaryItem.cs

  1 using System;

  2 using System.Collections.Generic;

  3 using System.Linq;

  4 using System.Text;

  5 using System.Threading.Tasks;

  6 using System.Windows;

  7 using System.Windows.Controls;

  8 using System.Windows.Data;

  9 using System.Windows.Documents;

 10 using System.Windows.Input;

 11 using System.Windows.Media;

 12 using System.Windows.Media.Imaging;

 13 using System.Windows.Navigation;

 14 using System.Windows.Shapes;

 15 

 16 namespace AsNum.WPF.Controls {

 17 

 18     [TemplatePart(Name = "PART_Value", Type=typeof(ContentControl))]

 19     [TemplatePart(Name = "PART_Title", Type = typeof(ContentControl))]

 20     [TemplatePart(Name = "PART_Detail", Type = typeof(ContentControl))]

 21     public class SummaryItem : Control {

 22 

 23         public static DependencyProperty ItemProperty = DependencyProperty.Register("Item", typeof(object), typeof(Summary));

 24         public static DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(SummaryItem));

 25         public static DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(SummaryItem));

 26         public static DependencyProperty DetailTemplateProperty = DependencyProperty.Register("DetailTemplate", typeof(DataTemplate), typeof(SummaryItem));

 27         public static DependencyProperty TitleTemplateProperty = DependencyProperty.Register("TitleTemplate", typeof(DataTemplate), typeof(SummaryItem));

 28         public static DependencyProperty ValueTemplateProperty = DependencyProperty.Register("ValueTemplate", typeof(DataTemplate), typeof(SummaryItem));

 29 

 30         public object Item {

 31             get {

 32                 return (object)this.GetValue(ItemProperty);

 33             }

 34             set {

 35                 this.SetValue(ItemProperty, value);

 36             }

 37         }

 38         public string Title {

 39             get {

 40                 return (string)this.GetValue(TitleProperty);

 41             }

 42             set {

 43                 this.SetValue(TitleProperty, value);

 44             }

 45         }

 46 

 47         public object Value {

 48             get {

 49                 return this.GetValue(ValueProperty);

 50             }

 51             set {

 52                 this.SetValue(ValueProperty, value);

 53             }

 54         }

 55 

 56         public DataTemplate DetailTemplate {

 57             get {

 58                 return (DataTemplate)this.GetValue(DetailTemplateProperty);

 59             }

 60             set {

 61                 this.SetValue(DetailTemplateProperty, value);

 62             }

 63         }

 64 

 65         public DataTemplate TitleTemplate {

 66             get {

 67                 return (DataTemplate)this.GetValue(TitleTemplateProperty);

 68             }

 69             set {

 70                 this.SetValue(TitleTemplateProperty, value);

 71             }

 72         }

 73 

 74         public DataTemplate ValueTemplate {

 75             get {

 76                 return (DataTemplate)this.GetValue(ValueTemplateProperty);

 77             }

 78             set {

 79                 this.SetValue(ValueTemplateProperty, value);

 80             }

 81         }

 82 

 83         static SummaryItem() {

 84             DefaultStyleKeyProperty.OverrideMetadata(typeof(SummaryItem), new FrameworkPropertyMetadata(typeof(SummaryItem)));

 85         }

 86 

 87         public override void OnApplyTemplate() {

 88             base.OnApplyTemplate();

 89 

 90             var pv = (ContentControl)this.Template.FindName("PART_Value", this);

 91             if (pv != null && this.ValueTemplate != null) {

 92                 pv.Content = this.Item;

 93                 pv.ContentTemplate = this.ValueTemplate;

 94             }

 95 

 96             var pt = (ContentControl)this.Template.FindName("PART_Title", this);

 97             if (pt != null && this.TitleTemplate != null) {

 98                 pt.Content = this.Item;

 99                 pt.ContentTemplate = this.TitleTemplate;

100             }

101 

102             var pd = (ContentControl)this.Template.FindName("PART_Detail", this);

103             if (pd != null && this.DetailTemplate != null) {

104                 pd.Content = this.Item;

105             }

106         }

107     }

108 }
View Code

用法:

 1         <ac:Summary Grid.Row="0" Title="用法" ItemsSource="{Binding Items2}" ItemTitlePath="Title2" ItemValuePath="Value" />

 2         

 3         <ac:Summary Grid.Row="2" Width="200"

 4                     Title="{Binding Title2}" ItemsSource="{Binding Items2}" 

 5                     ItemTitlePath="Title2" ItemValuePath="Value">

 6             <ac:Summary.ItemDetailTemplate>

 7                 <DataTemplate>

 8                     <TextBlock Text="{Binding Title2}" />

 9                 </DataTemplate>

10             </ac:Summary.ItemDetailTemplate>

11 

12             <ac:Summary.ItemValueTemplate>

13                 <DataTemplate>

14                     <Button Content="{Binding Value}" />

15                 </DataTemplate>

16             </ac:Summary.ItemValueTemplate>

17         </ac:Summary>
View Code

还有一些小细节需要自行调整。

谢绝不加出处的转载

你可能感兴趣的:(WPF)