欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF之自定义控件及添加依赖属性

程序员文章站 2022-03-04 11:51:08
...

一、问题描述

    我们要自定义一个Button,不使用任何原生Button的样式效果,所以,直接继承自UserControl而不是Button,但是又要具备Button的功能,所以,内部添加Button控件并重新设置样式模板,UserControl中没有Command和CommandParameter,所以,我们添加相应的依赖属性,并将其绑定到内部Button控件的Command和CommadParameter属性上,这样就能在外部像使用原生Button一样使用自定义Button设置Command和CommandParameter了。

    不过,现实还是很残酷的,当我们将自定义依赖属性绑定到内部Button上时,我们设置了自定义控件的this.DataContext = this;而当我们使用自定义控件时,要绑定的命令又存在于另一个外部的DataContext,一个控件只能有一个DataContext,于是矛盾产生了。

<UserControl x:Class="CHC.AibirdGStationPro.Control.AppButton"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CHC.AibirdGStationPro.Control"
        mc:Ignorable="d"
        Name="appButton" Height="auto" Width="auto" BorderThickness="0" Background="Transparent">
    <!--定义控件-->
    <Grid>
        <Button x:Name="myButton" Height="102" Width="102" Style="{StaticResource AppButtonStyle}" 
                MouseLeftButtonDown="Button_MouseLeftButtonDown"
                MouseLeftButtonUp="Button_MouseLeftButtonUp" Command="{Binding  Command }" CommandParameter="{Binding CommandParameter}"/>
    </Grid>
</UserControl>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;

namespace CHC.AibirdGStationPro.Control
{
    /// <summary>
    /// AppButton.xaml 的交互逻辑
    /// </summary>
    public partial class AppButton : UserControl
    {
        public AppButton()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
        // Using a DependencyProperty as the backing store for CommandProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand), typeof(AppButton), new PropertyMetadata(null));

        public string CommandParameter
        {
            get { return (string)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
        // Using a DependencyProperty as the backing store for CommandParameterProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object), typeof(AppButton), new PropertyMetadata(""));
    }
}

二、解决方案

方案一、在外部绑定Command时,不使用DataContext进行绑定,而使用ElementName,也就是先利用一个控件将要绑定的Command获取到,然后再间接绑定该控件的Command属性,以此完成对外部命令的绑定,但比较麻烦。

WPF之自定义控件及添加依赖属性

方案二、使用RelativeSource进行内部绑定,参考wpf 自定义控件中ElementName和DataContext之间的冲突

由于内部绑定时使用了this.DataContext = this,导致外部绑定不能使用外部DataContext,因此,如果内部绑定可以不使用DataContext,问题就能得到解决,而RelativeSource能做到这一点。

    <!--定义控件-->
    <Grid>
        <Button x:Name="myButton" Height="102" Width="102" Style="{StaticResource AppButtonStyle}" 
                MouseLeftButtonDown="Button_MouseLeftButtonDown"
                MouseLeftButtonUp="Button_MouseLeftButtonUp" 
                Command="{Binding  Command ,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}"
                CommandParameter="{Binding CommandParameter ,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}},Mode=TwoWay}"/>
    </Grid>
 <control:AppButton x:Name="btnRoutePlanning" NormalImage="{StaticResource RoutePlanning}" FocusedImage="{StaticResource RoutePlanning_focused}" 
                                   Command="{Binding  Path=StartUpAppCommand}" CommandParameter="btnRoutePlanning"/>

小结:

1、要实现数据绑定,关键是要设置好数据源,保证能够找到对应的数据,默认会从DataContext中查找,所以,DataContext一般都要设置,绑定自身属性时要设为this.DataContext = this。

2、RelativeSource通过根据控件类型查找相应的控件并绑定其属性,在自定义控件时很好解决了使用DataContext带来的问题。

3、自定义控件如果有自定义依赖属性,那应当将DataContext留给外部绑定,而不要在内部使用this.DataContext = this;