在实际工作中,面对不同的客户需求,需要让空间显示出不同的效果。当style已经不能足够满足客户需求时,就需要程序猿自己设计自定义控件了。
根据工作经历,LZ做了个关于自定义控件的小Demo,仅供参考。
实现操作分为三部分,话不多说,直接上代码。
一.控件后台设置
public class ImageButton : Button { public ImageButton() { this.PreviewMouseDown += ImageButton_PreviewMouseDown; this.PreviewMouseUp += ImageButton_PreviewMouseUp; } void ImageButton_PreviewMouseUp(object sender, MouseButtonEventArgs e) { MessageBox.Show("11111"); } void ImageButton_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("22222"); } public ImageSource ImageSource { get { return (ImageSource)GetValue(ImageSourceProperty); } set { SetValue(ImageSourceProperty, value); } } public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null)); }
根据控件需求对这个类做不同的继承,LZ做的控件是Button,所以继承自Button。
然后对ImageSource对应下文的Binding,做依赖属性。
this.PreviewMouseDown+=PreviewMouseDown;是对Button本身的响应事件做绑定。
二.控件界面设定
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:helper="clr-namespace:DemoZDY"> <Style TargetType="{x:Type helper:ImageButton}"> <Setter Property="SnapsToDevicePixels" Value="true"/> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="FocusVisualStyle" Value="{x:Null}"/> <Setter Property="Cursor" Value="Hand" /> <Setter Property="FontFamily" Value="微软雅黑" /> <Setter Property="FontSize" Value="14"/> <Setter Property="Foreground" Value="Black"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type helper:ImageButton}"> <StackPanel> <Image Name="Image" Stretch="Fill" Source="{TemplateBinding ImageSource}" /> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" /> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
控件内容比较简单,上面一个图片,下面一个Text内容。
Helper是指(一)中的命名空间。
三.MainWindow界面
<Window x:Class="DemoZDY.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" xmlns:helper="clr-namespace:DemoZDY"> <StackPanel> <helper:ImageButton ImageSource="Status-False.png" Width="100" Content="Click"/> </StackPanel> </Window>
简单一个自定义控件完成。但该控件不是很具体,下面再写一个稍微复杂点的自定义Buton控件。
四.新自定义button
控件界面差别不大,主要是后台代码和使用的图片资源不同。
(1)图片
点击效果:正常状态下显示上半部分,在被点击时显示下半部分,点击后恢复上半部分。
(2)控件后台代码
[TemplatePart(Name = "PART_Image", Type = typeof(Image))] public class HoverButton : Button { private void OnImageSourceChanged(ImageSource oldValue, ImageSource newValue) { if (newValue is BitmapSource) { int w = (int)(newValue.Width); int h = (int)(newValue.Height / 2); NormalImage = new CroppedBitmap(newValue as BitmapSource, new Int32Rect(0, 0, w, h)); PressedImage = new CroppedBitmap(newValue as BitmapSource, new Int32Rect(0, h, w, h)); } } public HoverButton() { this.PreviewMouseDown += OnPreviewMouseDown; this.PreviewMouseUp += OnPreviewMouseUp; } private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) { if (image != null) { image.Source = PressedImage; this.CaptureMouse(); } //e.Handled = true; } private void OnPreviewMouseUp(object sender, MouseButtonEventArgs e) { if (image != null) { if (this.IsMouseCaptured) { this.ReleaseMouseCapture(); } image.Source = NormalImage; this.OnClick(); } //e.Handled = true; } public override void OnApplyTemplate() { base.OnApplyTemplate(); image = this.GetTemplateChild("PART_Image") as Image; } public ImageSource ImageSource { get { return (ImageSource)GetValue(ImageSourceProperty); } set { SetValue(ImageSourceProperty, value); } } public ImageSource NormalImage { get { return (ImageSource)GetValue(NormalImageProperty); } set { SetValue(NormalImageProperty, value); } } public ImageSource PressedImage { get { return (ImageSource)GetValue(PressedImageProperty); } set { SetValue(PressedImageProperty, value); } } public static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { (d as HoverButton).OnImageSourceChanged(e.OldValue as ImageSource, e.NewValue as ImageSource); } static HoverButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(HoverButton), new FrameworkPropertyMetadata(typeof(HoverButton))); } public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(HoverButton), new UIPropertyMetadata(null, OnImageSourceChanged)); public static readonly DependencyProperty NormalImageProperty = DependencyProperty.Register("NormalImage", typeof(ImageSource), typeof(HoverButton), new UIPropertyMetadata(null)); public static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register("PressedImage", typeof(ImageSource), typeof(HoverButton), new UIPropertyMetadata(null)); public Image image; }
- [TemplatePart(Name = "PART_Image", Type = typeof(Image))],是一种契约,意思是告诉要来写ControlTemplate的用户 , 你的ControlTemplate中需要有一个x:Name为“PART_Image” , 类型为Image, 当然这个类型可以是继承来的, 为什么一定要包含这些契约规定的元素 , 因为逻辑部分对这些东西进行了引用,它们将对控件的默认行为起着关键作用, 可以理解为这个控件的最基本元素 , 是实现默认行为的最小集合, 自然,你的ControlTemplate中如果没有包含契约中的内容 , 则相应的逻辑将无法实现。
- OnImageSourceChanged里意思是将图片分为上下两部分(NormalImage和PressedImage)
- CaptureMouse和ReleaseMouseCapture是指光标的捕获和释放。
- OnApplyTemplate 显示由自定义控件定义的 OnApplyTemplate 重写,设计该自定义控件是因为考虑到调用方可能通过模板和样式系统来定义和应用自己的控件模板。 作为其定义的一部分,模板中命名元素的控件属性是必需的,如 UpButtonElement。 然后 OnApplyTemplate 在加载模板时基于此命名协定检索对象引用。 此外,此示例还调用专用方法 UpdateState(未显示定义)。 这是 OnApplyTemplate 的另一个常见方案:确保为控件的起始状态设置可视状态,在此例中,通过调用专用方法来确保,该专用方法考虑了控件的所有已定义状态并调用 GoToState 来设置适当的状态。 (msdn.microsoft.com中的定义)
- 其他的都很简单,切记要依赖属性。
五.Demo下载