WPF 使用自定义控件(custom control),资源字典(ResourceDictionary),用户控件(user control),及之间的对比

2023-11-07

最近使用WPF比较多,过来记录一下,对比一下

个人理解usercontrol比较适用于组合控件(比如你想要实现的控件是由多个控件组成的),customCcontrol主要是用来单独重绘控件,如button,datagrid,lable等。customCcontrol修改控件外观的方专式是可属以访问控件本身template的里的控件,然后可以对其修改样式和增加逻辑。

而资源字典更多是通过”Seyle”标签来封装资源。在WPF中我们可以使用Style来设置控件的某些属性值,并使该设置影响到指定范围内的所有该类控件或影响指定的某一控件。

一、创建自定义控件

1、控件后台交互类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfCustomControlLibrary1
{
   
    [TemplatePart(Name = CustomControl1.ElementDateTimeTextBox, Type = typeof(TextBlock))]
    [TemplatePart(Name = CustomControl1.ElementContentTextBox, Type = typeof(TextBlock))]
    public class CustomControl1 : Control
    {
        public CustomControl1()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
            ContentMsg = "Hello World"; 
        }
        private const string ElementDateTimeTextBox = "PART_DateTimeTextBox";
        private const string ElementContentTextBox = "PART_ContentTextBox";
        private const string Elementbutton = "PART_Button"; 
        TextBlock contentMsgTB = null;
        Button button = null;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
             
            contentMsgTB = GetTemplateChild(ElementContentTextBox) as TextBlock;
            button =  GetTemplateChild(Elementbutton) as Button;
            button.Click += Button_Click;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ContentMsg = "李四";
        }

        /// <summary>
                /// Registers a dependency property as backing store for the Content property
                /// </summary>
        public static readonly DependencyProperty ContentMsgProperty =
      DependencyProperty.Register("ContentMsg", typeof(object), typeof(CustomControl1),
      new FrameworkPropertyMetadata(null ));

        /// <summary>
                /// Gets or sets the Content.
                /// </summary>
                /// <value>The Content.</value>
        public object ContentMsg
        {
            get { return (object)GetValue(ContentMsgProperty); }
            set { SetValue(ContentMsgProperty, value); }
        }  
    }
}


2、样式Generic.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfCustomControlLibrary1">
    <Style TargetType="{x:Type local:CustomControl1}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:CustomControl1}">
                    <StackPanel Orientation="Horizontal"> 
                        <TextBlock x:Name="PART_ContentTextBox" Text="{TemplateBinding ContentMsg}"/>
                        <StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Button  Margin="10"  
                                         Grid.Column="0" Grid.Row="0"  Content="1" x:Name="PART_Button"/>
                                <Button  Margin="10" Grid.Column="0" Grid.Row="1"   Content="2" x:Name="PART_Button2"/>
                                <Button  Margin="10"  Grid.Column="0" Grid.Row="2"   Content="3" x:Name="PART_Button3"/> 
                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Button  Margin="10"  Grid.Column="1" Grid.Row="0"   Content="4" x:Name="PART_Button4"/>
                                <Button Margin="10"   Grid.Column="1" Grid.Row="1"   Content="5" x:Name="PART_Button5"/>
                                <Button Margin="10"   Grid.Column="1" Grid.Row="2"   Content="6" x:Name="PART_Button6"/>

                            </StackPanel>
                            <StackPanel Orientation="Horizontal">
                                <Button   Margin="10" Grid.Column="2" Grid.Row="0"   Content="7" x:Name="PART_Button7"/>
                                <Button   Margin="10" Grid.Column="2" Grid.Row="1"   Content="8" x:Name="PART_Button8"/>
                                <Button  Margin="10"  Grid.Column="2" Grid.Row="2"   Content="9" x:Name="PART_Button9"/>

                            </StackPanel>
                        </StackPanel>
                      </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

3、应用

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        xmlns:custom="clr-namespace:WpfCustomControlLibrary1;assembly=WpfCustomControlLibrary1"
        Title="MainWindow" Height="450" Width="800">
 
    <StackPanel>
        <custom:CustomControl1 ContentMsg="{Binding DateTimeMsg,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  
                               x:Name="mycustom"  HorizontalAlignment="Left" Margin="55,30,0,0" VerticalAlignment="Top"></custom:CustomControl1>
        <Button Content="dsafsdf" Click="Button_Click" Height="40" Width="100" Margin="10"></Button>
    </StackPanel>
</Window>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfCustomControlLibrary1
{
  
    public class MainViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _DateTimeMsg;
        public string DateTimeMsg
        {
            get
            {
                return _DateTimeMsg;
            }
            set
            {
                _DateTimeMsg = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("DateTimeMsg"));
                }
            }
        }
        private string _ContentMsg;
        public string ContentMsg
        {
            get
            {
                return _ContentMsg;
            }
            set
            {
                _ContentMsg = value;
                if (this.PropertyChanged != null)
                {
                    this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ContentMsg"));
                }
            }
        }
        
    }
}

4、页面访问

可通过依赖属性  MessageBox.Show(this.mycustom.ContentMsg.ToString());

二、资源字典

1、创建资源字典

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:local="clr-namespace:WPFtest"
                    x:Class="WPFtest.DicEvent" >
    <local:RotationManager x:Key="p"></local:RotationManager>
    <local:NumberConverter x:Key="NumberConverter"></local:NumberConverter> 
       <Style TargetType="Button" x:Key="ButtonImage">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                   
                    <Grid Background="{TemplateBinding Background}">
                        <StackPanel HorizontalAlignment="Left">
                            <Button Name="my" Click="liClick_Click" Background="Red" Content="点我啊"></Button>
                        </StackPanel>
                        <StackPanel  HorizontalAlignment="Right">
                            <TextBox Name="texbox2" Text="{Binding Source={StaticResource p},Path=Angle,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"  Height="50" Width="100"></TextBox>
                            <!--<TextBox Name="texbox2" Text="{Binding RelativeSource={RelativeSource  RotationManager.Angle}}"  Height="50" Width="100"></TextBox>-->
                            <!--<TextBox Name="texbox2" Text="{Binding Source=local:RotationManager.Angle,Path=value, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"  Height="50" Width="100"></TextBox>-->
                            <!--<TextBox Name="texbox1" Text="{TemplateBinding local:RotationManager.Angle}"  Height="50" Width="100"></TextBox>-->
                            <!--<Label Name="texbox1" Content="{TemplateBinding local:RotationManager.Angle,Converter={StaticResource ResourceKey=NumberConverter}}"  Height="50" Width="100"></Label>-->
                        </StackPanel>
                        <ContentPresenter></ContentPresenter>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

2、资源字典后台处理类

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
namespace WPFtest
{
    public partial class DicEvent : ResourceDictionary
    {
        public void liClick_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(((sender as Button).TemplatedParent).GetValue(RotationManager.AngleProperty).ToString());
            ((sender as Button).TemplatedParent).SetValue(RotationManager.AngleProperty, "1111111");
            MessageBox.Show(((sender as Button).TemplatedParent).GetValue(RotationManager.AngleProperty).ToString());

            //省去处理,如果显示,表明调用成功。
            //System.Windows.Forms.MessageBox.Show("点击了资源字典的控件!");

        }
        public void my_MouseEnter(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.MessageBox.Show("你成功dd了!");

        }

    }

    public class myattach : DependencyObject
    {
        public static string GetGrade(DependencyObject obj)
        {
            return (string)obj.GetValue(GradeProperty);
        }

        public static void SetGrade(DependencyObject obj, string value)
        {
            obj.SetValue(GradeProperty, value);
        }

        public static readonly DependencyProperty GradeProperty =
            DependencyProperty.RegisterAttached("attach", typeof(string), typeof(myattach), new UIPropertyMetadata("isattach"));

    }

   

}


3、数值转换器

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;

namespace WPFtest
{
    [ValueConversion(typeof(int), typeof(string))]
    public class NumberConverter : IValueConverter
    {
        //源属性传给目标属性时,调用此方法ConvertBack
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int c = System.Convert.ToInt32(parameter);

            if (value == null)
                throw new ArgumentNullException("value can not be null");

            int index = System.Convert.ToInt32(value);
            if (index == 0)
                return "Blue";
            else if (index == 1)
                return "Red";
            else
                return "Green";
        }

        //目标属性传给源属性时,调用此方法ConvertBack
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    } 
}

三、用户控件

1、创建控件模板

<UserControl x:Class="XXX.自定义控件.MyNumericUpDown"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XXX.自定义控件"
             mc:Ignorable="d" 
             d:DesignHeight="30" d:DesignWidth="120">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="40*"/>
            <ColumnDefinition Width="40*"/>
        </Grid.ColumnDefinitions>

        <TextBox Name="TextBox_Num" Grid.Column="0" Text="1" FontSize="20" TextAlignment="Center" MinWidth="40" VerticalContentAlignment="Center"/>
        <Button Name="Button_Add" Grid.Column="1" Content="加" Click="Button_Add_Click" Background="Aqua"/>
        <Button Name="Button_Sub" Grid.Column="2" Content="减" Click="Button_Sub_Click" Background="Aqua"/>
        
    </Grid>
</UserControl>

2、编写后台代码

/// <summary>
    /// MyNumericUpDown.xaml 的交互逻辑
    /// </summary>
    public partial class MyNumericUpDown : UserControl
    {

        /// <summary>
        /// 当前值
        /// </summary>
        public int Num
        {
            get
            {
                int value = 0;
                this.Dispatcher.Invoke(new Action(() =>
                    value = Convert.ToInt32(this.TextBox_Num.Text.Trim())
                ));
                return value;
            }
            set
            {
                this.Dispatcher.Invoke(new Action(() =>
                {
                    this.TextBox_Num.Text = value.ToString();
                }));                
            }
        }

        public MyNumericUpDown()
        {
            InitializeComponent();
        }
        
        private void Button_Add_Click(object sender, RoutedEventArgs e)
        {
            int num = int.Parse(this.TextBox_Num.Text.Trim());
            if (num > 0)
            {
                this.TextBox_Num.Text = (num + 1).ToString();
            }
        }

        private void Button_Sub_Click(object sender, RoutedEventArgs e)
        {
            int num = int.Parse(this.TextBox_Num.Text.Trim());
            if (num > 0)
            {
                if ((num - 1) == 0)
                    return;
                this.TextBox_Num.Text = (num - 1).ToString();
            }
        }
    }

三、使用控件

xmlns:z="clr-namespace:XXX.自定义控件"  //Windows标签中声明命名空间
<z:MyNumericUpDown x:Name="MyNumericUpDown_PageNum" Width="120" Height="30"></z:MyNumericUpDown>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WPF 使用自定义控件(custom control),资源字典(ResourceDictionary),用户控件(user control),及之间的对比 的相关文章

随机推荐