User Controls and Custom Controls in WPF

User Controls and Custom Controls in WPF

Whether in WPF or WinForm, there are user controls (UserControl) and custom controls (CustomControl). These two controls are encapsulation of existing controls to achieve functional reuse. But there are still some differences between the two. This article explains these two controls.

  1. user control
    • Pay attention to the use of composite controls, that is, multiple existing controls form a reusable control group
    • Composed of XAML and background code, tightly bound
    • Template rewriting is not supported
    • Inherited from UserControl
  2. custom control
    • Completely implement a control by yourself, such as inheriting existing controls for function extension and adding new functions
    • Combination of background code and Generic.xaml
    • Support template rewriting when using
    • Inherited from Control

user control

User controls are relatively easy to understand, similar to common WPF forms. It is worth noting that when using binding internally, it needs to be RelativeSourcebound in a way to achieve good encapsulation. A simple case:

  • Define user controls
<UserControl
    x:Class="WpfApp19.UserControl1"
    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:local="clr-namespace:WpfApp19"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <Grid>
        <!--下面绑定都要用RelativeSource作为源-->
        <StackPanel>
            <TextBox
                Width="100"
                BorderThickness="2"
                Text="{Binding value, RelativeSource={RelativeSource AncestorType=UserControl}}" />
            <Button
                Width="100"
                Command="{Binding Command, RelativeSource={RelativeSource AncestorType=UserControl}}"
                Content="Ok" />
        </StackPanel>
    </Grid>
</UserControl>

code behind

//根据需要定义依赖属性 
//所需要绑定的值
 public int value
 {
    
    
     get {
    
     return (int)GetValue(valueProperty); }
     set {
    
     SetValue(valueProperty, value); }
 }
 public static readonly DependencyProperty valueProperty =
     DependencyProperty.Register("value", typeof(int), typeof(UserControl1), new PropertyMetadata(0));

 //所需要绑定的命令
 public ICommand Command
 {
    
    
     get {
    
     return (ICommand)GetValue(CommandProperty); }
     set {
    
     SetValue(CommandProperty, value); }
 }
 public static readonly DependencyProperty CommandProperty =
     DependencyProperty.Register("Command", typeof(ICommand), typeof(UserControl1), new PropertyMetadata(default(ICommand)));


 //所需要绑定的命令参数
 public object CommandParemeter
 {
    
    
     get {
    
     return (object)GetValue(CommandParemeterProperty); }
     set {
    
     SetValue(CommandParemeterProperty, value); }
 }
 public static readonly DependencyProperty CommandParemeterProperty =
     DependencyProperty.Register("CommandParemeter", typeof(object), typeof(UserControl1), new PropertyMetadata(0));
  • use user controls
<Window x:Class="WpfApp19.MainWindow"
        ...
        xmlns:local="clr-namespace:WpfApp19"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <local:UserControl1 value="{Binding }" Command="{Binding }"/>
    </Grid>
</Window>

custom control

CustomControl1.csAfter clicking to add a custom control, a file and a directory will be added , and there is a file Themesunder the directory , which is the style of the custom control. Generic.xamlThe style we often generate for certain controls 编辑模板- 创建副本operations is actually Generic.xamlthe style defined in . In addition, sometimes we may encounter a situation, that is, the same software runs under different Windows versions, and the performance may be different, and even some systems cannot run, which is different from the default Theme under different systems. In fact, when the wpf control cannot find a custom style, it will obtain the style from the system. The search order is to first find the assembly where it is located. If the assembly defines a feature, then it will check the attribute value. If the attribute is yes, it means ThemeInfothere ThemeInfoDictionaryLocationis Noneno A specific theme resource, the value SourceAssemblymeans that the specific resource is defined inside the assembly, and the value ExternalAssemblymeans that it is outside. If it is still not found, the program will get it in its own themes/generic.xaml, and generic.xamlwhat it gets in is actually related to the default style of the system.

System themes corresponding to different xaml

image-20230208144759832

button case

img

C# file

public class Switch : ToggleButton
{
    
    
    static Switch()
    {
    
    
        //通过重写Metadata,控件就会通过程序集themes文件夹下的generic.xaml来寻找系统默认样式
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Switch), new FrameworkPropertyMetadata(typeof(Switch)));
    }
}

Generic.xaml file under the Themes folder

Note that there can be no Chinese in this file, nor can comments

<Style TargetType="{x:Type local:Switch}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:Switch}">
                <Grid>
                    <Border
                        Name="dropdown"
                        Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                        Margin="-23"
                        CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                        Visibility="Collapsed">
                        <Border.Background>
                            <RadialGradientBrush>
                                <GradientStop Offset="1" Color="Transparent" />
                                <GradientStop Offset="0.7" Color="#5500D787" />
                                <GradientStop Offset="0.59" Color="Transparent" />
                            </RadialGradientBrush>
                        </Border.Background>
                    </Border>
                    <Border
                        Name="bor"
                        Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                        Background="Gray"
                        BorderBrush="DarkGreen"
                        BorderThickness="5"
                        CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}">
                        <Border
                            Name="bor1"
                            Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
                            Margin="2"
                            Background="#FF00C88C"
                            CornerRadius="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}" />
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation
                                        Storyboard.TargetName="bor1"
                                        Storyboard.TargetProperty="Background.Color"
                                        To="White"
                                        Duration="0:0:0.5" />
                                    <ColorAnimation
                                        Storyboard.TargetName="bor"
                                        Storyboard.TargetProperty="BorderBrush.Color"
                                        To="#FF32FAC8"
                                        Duration="0:0:0.5" />
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="dropdown" Storyboard.TargetProperty="Visibil
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Visible</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="bor1" Storyboard.TargetProperty="Background.Color" />
                                    <ColorAnimation Storyboard.TargetName="bor" Storyboard.TargetProperty="BorderBrush.Color" />
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="dropdown" Storyboard.TargetProperty="Visibil
                                        <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
                                            <DiscreteObjectKeyFrame.Value>
                                                <Visibility>Collapsed</Visibility>
                                            </DiscreteObjectKeyFrame.Value>
                                        </DiscreteObjectKeyFrame>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Use custom controls

<Grid>
    <local:Switch Width="100" Height="100" />
</Grid>

Commonly used knowledge points in custom controls

  1. TemplatePart properties

In custom controls, some controls need to have a name for easy calling, such as getting the specified button in the rewritten OnApplyTemplate() method. This requires that the user cannot modify the name in the template when using the control.

//应用该控件时调用
public override void OnApplyTemplate()
{
    
    
    UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
    DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
}

So use attributes in front of the class to mark

[TemplatePart(Name = "UpButton", Type = typeof(RepeatButton))]
[TemplatePart(Name = "DownButton", Type = typeof(RepeatButton))]
public class Numeric : Control{
    
    }
  1. Definition and call of visual state

Visual states can be defined in custom controls to present effects in different states.

Define the visual state in xml, and the visual states under different groups are mutually exclusive

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup Name="FocusStates">
        <VisualState Name="Focused">
            <Storyboard>
                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual" 
                           Storyboard.TargetProperty="Visibility" Duration="0">
                    <DiscreteObjectKeyFrame KeyTime="0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
            </Storyboard>
        </VisualState>
        <VisualState Name="Unfocused"/>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Corresponding visual state switching in C#

private void UpdateStates(bool useTransitions)
{
    
    
    if (IsFocused)
    {
    
    
        VisualStateManager.GoToState(this, "Focused", false);
    }
    else
    {
    
    
        VisualStateManager.GoToState(this, "Unfocused", false);
    }
}

At the same time, you can use attributes to mark in the background class

[TemplateVisualState(Name = "Focused", GroupName = "FocusedStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusedStates")]
public class Numeric : Control{
    
    }

In fact, you can use Trigger to achieve this function

Case study source code download

Share several cases of user controls and custom controls, the effect is as follows:

img

Source code download

Guess you like

Origin blog.csdn.net/weixin_44064908/article/details/128938443