Controles de usuario y controles personalizados en WPF

Controles de usuario y controles personalizados en WPF

Ya sea en WPF o WinForm, existen controles de usuario (UserControl) y controles personalizados (CustomControl).Estos dos controles son la encapsulación de los controles existentes para lograr la reutilización funcional. Pero todavía hay algunas diferencias entre los dos.Este artículo explica estos dos controles.

  1. control de usuario
    • Preste atención al uso de controles compuestos, es decir, múltiples controles existentes forman un grupo de control reutilizable
    • Compuesto de XAML y código de fondo, estrechamente vinculado
    • No se admite la reescritura de plantillas
    • Heredado de UserControl
  2. control personalizado
    • Implemente completamente un control usted mismo, como heredar controles existentes para la extensión de funciones y agregar nuevas funciones
    • Combinación de código de fondo y Generic.xaml
    • Admite la reescritura de plantillas cuando se usa
    • Heredado de Control

control de usuario

Los controles de usuario son relativamente fáciles de entender, similares a los formularios comunes de WPF. Vale la pena señalar que cuando se usa el enlace internamente, debe vincularse RelativeSourcede manera que se logre una buena encapsulación. Un caso sencillo:

  • Definir controles de usuario
<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>

código detrás

//根据需要定义依赖属性 
//所需要绑定的值
 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));
  • usar controles de usuario
<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>

control personalizado

Después de hacer clic para agregar un control personalizado, se agregarán un CustomControl1.csarchivo y un directorio, y hay un archivo Themesdebajo del directorio , que es el estilo del control personalizado. Generic.xamlEl estilo que solemos generar para ciertos controles 编辑模板- 创建副本operaciones es en realidad Generic.xamlel estilo definido en . Además, a veces podemos encontrarnos con una situación, es decir, el mismo software se ejecuta en diferentes versiones de Windows, y el rendimiento puede ser diferente, e incluso algunos sistemas no pueden ejecutarse, lo cual es diferente del tema predeterminado en diferentes sistemas. De hecho, cuando el control wpf no puede encontrar un estilo personalizado, obtendrá el estilo del sistema. El orden de búsqueda es encontrar primero el ensamblaje donde se encuentra. Si el ensamblaje define una característica, luego verificará el valor del atributo. .Si el atributo es sí, significa que no ThemeInfohay un recurso de tema específico, el valor significa que el recurso específico está definido dentro del ensamblaje y el valor ThemeInfoDictionaryLocationsignifica que está fuera.Si aún no se encuentra, el programa obtendrá en su propio , que en realidad está relacionado con el estilo predeterminado del sistema.NoneSourceAssemblyExternalAssemblythemes/generic.xamlgeneric.xaml

Temas del sistema correspondientes a diferentes xaml

imagen-20230208144759832

caso de botón

imagen

archivo C#

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

Archivo Generic.xaml en la carpeta Temas

Tenga en cuenta que no puede haber chino en este archivo, ni comentarios

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

Usar controles personalizados

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

Puntos de conocimiento de uso común en controles personalizados

  1. Propiedades de TemplatePart

En los controles personalizados, algunos controles deben tener un nombre para facilitar la llamada, como obtener el botón especificado en el método OnApplyTemplate() reescrito. Esto requiere que el usuario no pueda modificar el nombre en la plantilla cuando usa el control.

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

Así que usa atributos delante de la clase para marcar

[TemplatePart(Name = "UpButton", Type = typeof(RepeatButton))]
[TemplatePart(Name = "DownButton", Type = typeof(RepeatButton))]
public class Numeric : Control{
    
    }
  1. Definición y llamada de estado visual.

Los estados visuales se pueden definir en controles personalizados para presentar efectos en diferentes estados.

Defina el estado visual en xml, y los estados visuales en diferentes grupos son mutuamente excluyentes

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

Cambio de estado visual correspondiente en C#

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

Al mismo tiempo, puede usar atributos para marcar en la clase de fondo

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

De hecho, puede usar Trigger para lograr esta función

Descarga del código fuente del estudio de caso

Comparta varios casos de controles de usuario y controles personalizados, el efecto es el siguiente:

imagen

Descarga del código fuente

Supongo que te gusta

Origin blog.csdn.net/weixin_44064908/article/details/128938443
Recomendado
Clasificación