WPF自定义按钮最好的方法(wpf custom button best approach)

2019-07-03 15:25发布

我想创建一个自定义的Button里面WPF。 当然,按钮将是一个用户控件,它会包含许多视觉元素(如中风,高亮,阴影,发光,图像等)。

问题是,如果我使用DependencyProperties和XAML绑定他们,我将无法看到在设计时的结果(我试过实现IsInDesignMode方法,但由于某种原因,我无法理解我刚刚VS时崩溃我使用用户控件此方法,否则它只是罚款),这绝对不是好。

因此,我想在所有不使用XAML和做我背后的代码的所有工作。

你们有什么感想?

Answer 1:

和你一样,当我入门,想了解如何/回事,并使用模板的,花了大量的试验和错误的。 希望我的研究和一些一步一步的组件可以帮助您自定义根据自己的喜好,知道那里的东西是从哪里来的。

首先,试图了解一个新的“模板式”将工作的时候,我创建了一个简单的单机WPF应用程序(“AMS”)对我的任何操纵样式。 这样一来,我没有永远等待,看看会是什么样的东西试/误差过程中与我的主要项目和主题的其余部分。

从这一点,我创建了一个新的WPF窗口称为“TestingStyles”。 保存/编译,运行没有问题。

现在,在TestingStyles窗口的“视图代码”,我已经把一切我与自定义类打...帮助显示一步一步的,我创建了以下内容:

namespace AMS
{
    /// <summary>
    /// Interaction logic for TestingStyles.xaml
    /// </summary>
    public partial class TestingStyles : Window
    {
        public TestingStyles()
        {
            InitializeComponent();
        }
    }

    // Enumerator for a custom property sample...
    public enum HowToShowStatus
    {
        ShowNothing,
        ShowImage1
    }


    public class YourCustomButtonClass : Button
    {
        public YourCustomButtonClass()
        {
            // auto-register any "click" will call our own custom "click" handler
            // which will change the status...  This could also be done to simplify
            // by only changing visibility, but shows how you could apply via other
            // custom properties too.
            Click += MyCustomClick;
        }

        protected void MyCustomClick(object sender, RoutedEventArgs e)
        {
            if( this.ShowStatus == HowToShowStatus.ShowImage1 )
                this.ShowStatus = HowToShowStatus.ShowNothing;
            else
                this.ShowStatus = HowToShowStatus.ShowImage1;
        }


        public static readonly DependencyProperty ShowStatusProperty =
              DependencyProperty.Register("ShowStatus", typeof(HowToShowStatus),
              typeof(YourCustomButtonClass), new UIPropertyMetadata(HowToShowStatus.ShowNothing));

        public HowToShowStatus ShowStatus
        {
            get { return (HowToShowStatus)GetValue(ShowStatusProperty); }
            set { SetValue(ShowStatusProperty, value); }
        }
    }

}

正如你所看到的,自定义“按钮”类的,我都不得不默认TestingStyles外底:窗口申报...所以一切都在同一个“项目”。

在此XAML示例中,我提及了“TaskComplete.png”图形文件(这应该只是为了样品的目的,直接添加到项目中。即使一个简单的笑脸样品的目的)。 所以,即使使用微软画图和绘图用眼睛和微笑圈创建这样一个简单的PNG文件...。 保存到在根项目(进入寻路的东西后,得到它的工作第一)。

保存并重新编译项目,因此该项目公开知道新“类”(按钮)是什么,当你开始定义的XAML模板。

现在,回到TestingStyles设计师,让它进入画面分割,所以你可以同时看到设计者和XAML标记...并就其替换为以下...

<Window x:Class="AMS.TestingStyles"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:AMS"
        Title="TestingStyles" Height="300" Width="300" >

    <Window.Resources>
        <!-- Build a "Style" based on an anticpated target control type of YourCustomButtonClass.
            per the "my:" reference, the "my" is an "alias" to the xmlsn:my in the declaration above,
            so the XAML knows which library to find such control.  In this case, I've included within
            the actual forms's 'View Code' as a class at the bottom.  

            As soon as you assign an "x:Key" reference, its like its telling XAML to make this a PRIVATE
            style so you don't reference it explicitly (yet)
        -->
        <Style TargetType="my:YourCustomButtonClass" x:Key="keyYourCustomButtonClass">
            <!-- put whatever normal "settings" you want for your common look / feel, color -->
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="0,0,1,1"/>
            <Setter Property="Width" Value="100" />
            <Setter Property="Height" Value="30" />

            <!-- Now, for the template of the button.  Things can get really crazy here
              as you are now defining what you want the "button" to look like, borders, 
              content, etc. In this case, I have two borders to give the raise/sunken effect 
              of a button and it has its own colors -->
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button" >
                        <!-- The x:Name references used during triggers to know what it is "applying" changes to -->
                        <Border x:Name="BorderTopLeft"
                                BorderBrush="Gainsboro" 
                                BorderThickness="0,0,1.5,1.5">

                            <Border x:Name="BorderBottomRight"
                                BorderBrush="Gray" 
                                BorderThickness="1.5,1.5,0,0">
                                <!-- Now, what control  type do you want the button to have... 
                                    Ex: You could use a grid (as I have here), stack panels, etc -->
                                <Grid Background="LightBlue" >
                                    <!-- I'm defining as two columns wide, one row tall.  
                                        First column fixed width 20 pixels example for an image -->
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="20px" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition />
                                    </Grid.RowDefinitions>

                                    <!-- Now, create the controls I want available within my "template".
                                        when assigned with "x:Name", thats like a property withing the template
                                        that triggers can associate and update to. -->
                                    <Image x:Name="btnImage" 
                                        Grid.Row="0" Grid.Column="0"
                                        Stretch="None"
                                        VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
                                        Source="TaskComplete.png"
                                        Visibility="Visible" />

                                    <!-- and also have the text for the button to show the user -->
                                    <TextBlock x:Name="txtNewBtn" 
                                        Grid.Row="0" Grid.Column="1"
                                        Padding="5"
                                        HorizontalAlignment="Left"
                                        VerticalAlignment="Center"
                                    Text="{TemplateBinding Content}" />
                                    <!-- The "{TemplateBinding Content}" means to set the text based on 
                                        the "CONTENT" property of the original button and not use a fixed value -->
                                </Grid>
                            </Border>
                        </Border>
                        <!-- Now, some triggers for the button itself... some can be property based, others data-based -->
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsPressed" Value="true">
                                <!-- What properties do we want to change when user CLICKS
                                    on the button, give the "EFFECT" of click down/up by
                                    changing the "Margin" and border thicknesses...  -->
                                <Setter Property="Margin" Value="1,1,0,0"/>
                                <!-- Notice the "TargetName" below referring to the x:Name I've applied in template above 
                                    so when the user clicks on the button, it changes the border thickness properties of
                                    each to give the effect of a normal button clicking.  I'm widening one border, shrinking other -->
                                <Setter TargetName="BorderTopLeft" Property="BorderThickness" Value="2.5,2.5,0,0"/>
                                <Setter TargetName="BorderBottomRight" Property="BorderThickness" Value="0,0,.5,.5"/>
                            </Trigger>

                            <!-- Here, I have a custome property on the class for "ShowStatus".  The binding is to itself
                                regardless of how many instances of this type of "button" are on a given form 
                                First trigger happens when the value is changed to "ShowNothing", but can also change 
                                when set to "ShowImage1" or other as you may need applicable
                            -->
                            <DataTrigger Binding="{Binding Path=ShowStatus, RelativeSource={RelativeSource Self}}" Value="ShowNothing">
                                <Setter TargetName="btnImage" Property="Visibility" Value="Hidden"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding Path=ShowStatus, RelativeSource={RelativeSource Self}}" Value="ShowImage1">
                                <Setter TargetName="btnImage" Property="Visibility" Value="Visible"/>
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- NOW, we can expose any instance of "YourCustomButtonClass" button to use the style based on definition above 
            any instance of such YourCustomButtonClass will automatically reflect this style / look -->
        <Style TargetType="my:YourCustomButtonClass" BasedOn="{StaticResource keyYourCustomButtonClass}" />

    </Window.Resources>

    <Grid>
        <my:YourCustomButtonClass Content="Button" VerticalAlignment="Top" ShowStatus="ShowImage1" />
    </Grid>
</Window>

这应该给你一个很好的快速启动,以确定自己的模板和元素是如何开始绑在一起。 一旦这个样本正在运行,为你改变任何颜色,边距,填充等的模板,你会马上看到的视觉冲击该组件对控制。

玩得开心,不靠墙太多碰你的头......

顺便说一句,一旦这样的工作,那么你可以采取内的样式元素的东西

<Window.Resources>
</Window.Resources> 

并把它变成一个Windows资源字典,使其全局到您的项目,而不是仅仅这个测试形式。



文章来源: wpf custom button best approach