该控件叫 Summary, 主要是一些汇总信息的显示,有几个地方用,之前都是分散到各个XAML文件里,不统一。
本人WPF新手,对XAML了解不多,做这个软件基本上都是用CM,界面布局用 AvalonDock。由于缺乏相关经验,又没有一个能问得上的人指导,写这个控件费了我很长时间(啥时有空啥时动动)。
之前主要做一些功能方面的,没有心思美化界面,现在虽然还有很多功能没写,但是基本上够自己用了,放下心思来做一些界面上的东西,要不然何苦选择WPF?
先看一下图:
该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>
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 }
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 }
用法:
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>
还有一些小细节需要自行调整。
谢绝不加出处的转载