Optimal approach to custom wpf button

I want to create a custom Button inside WPF. Of course, the button will be a UserControl and will contain many visual elements (for example, stroke, highlight, shadow, glow, images, etc.).

The problem is that if I use DependencyProperties and bind them in XAML, I cannot see the result in DesignTime (I tried to implement the IsInDesignMode method, but for some reason I can not understand my VS just crashes when I use this method in UserControls, otherwise it works just fine) and that is definitely not very good.

So, I think that I do not use XAML at all and do not do all my work in the code.

What do you guys think?

+7
source share
1 answer

Like you, when I started and wanted to understand how / what happens and works with templates, it required a lot of trial and error. I hope my research and some step-by-step components will help you customize to your taste and KNOW where things come from.

First, trying to understand how the new “template style” will work, I created a simple standalone WPF application (“AMS”) for my Any Manipulating Styles. So I don’t have to wait long to see what the trial / error will look like with the rest of my main project and topic.

From this, I created a new WPF window called "TestingStyles". Save / Compile, run, no problem.

Now, in the "VIEW CODE" window of TestingStyles, I have set everything I play for a custom class ... To show step by step, I created the following:

 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); } } } } 

As you can see, the custom class is "Button", I have a lower border above the standard declaration of TestingStyles: Window ... so that it is all in the same "Project".

In this XAML example, I refer to the graphic file "TaskComplete.png" (which should be added only for trial purposes, add directly to the project ... Even if a simple emoticon for trial purposes). So, create such a simple .png file ... even with Microsoft Paint and drawing a circle with your eyes and a smile. Save the project in the root folder (first go to it, start it first).

Save and recompile the project so that the project knows the new “class” (button) when you start defining the XAML template.

Now, back to the TestingStyles design and move on to the split screen so you can see both the constructor and the XAML markup ... and just replace it with the following ...

 <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 '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> 

This should give you a big step forward to define your own patterns and how the elements begin to link together. When this pattern is started, as any colors, margins, indents, etc. change. To the template, you will immediately see the visual impact of this component on the control.

Have fun and don't bang your head too hard against the wall ...

BTW, as soon as this works, you can take the style element in

 <Window.Resources> </Window.Resources> 

and put it in the Windows Resource Dictionary to make it global for your project, not just this test form.

+11
source

All Articles