WPF 使用自定义控件(custom control),资源字典(ResourceDictionary),用户控件(user control),及之间的对比
最近使用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 WPFtest
{
/// <summary>
/// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
///
/// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:WPFtest"
///
///
/// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:WPFtest;assembly=WPFtest"
///
/// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
/// 并重新生成以避免编译错误:
///
/// 在解决方案资源管理器中右击目标项目,然后依次单击
/// “添加引用”->“项目”->[浏览查找并选择此项目]
///
///
/// 步骤 2)
/// 继续操作并在 XAML 文件中使用控件。
///
/// <MyNamespace:FirstCustomControl/>
///
/// </summary>
[TemplatePart(Name = FirstCustomControl.ElementDateTimeTextBox, Type = typeof(TextBlock))]
[TemplatePart(Name = FirstCustomControl.ElementContentTextBox, Type = typeof(TextBlock))]
public class FirstCustomControl : Control
{
private const string ElementDateTimeTextBox = "PART_DateTimeTextBox";
private const string ElementContentTextBox = "PART_ContentTextBox";
TextBlock dateTimeTB = null;
TextBlock contentMsgTB = null;
public FirstCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FirstCustomControl), new FrameworkPropertyMetadata(typeof(FirstCustomControl)));
ContentMsg = "Hello World";
DateTimeMsg = "DateTime";
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
dateTimeTB = GetTemplateChild(ElementDateTimeTextBox) as TextBlock;
contentMsgTB = GetTemplateChild(ElementContentTextBox) as TextBlock;
}
/// <summary>
/// Registers a dependency property as backing store for the Content property
/// </summary>
public static readonly DependencyProperty ContentMsgProperty =
DependencyProperty.Register("ContentMsg", typeof(object), typeof(FirstCustomControl),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
/// <summary>
/// Gets or sets the Content.
/// </summary>
/// <value>The Content.</value>
public object ContentMsg
{
get { return (object)GetValue(ContentMsgProperty); }
set { SetValue(ContentMsgProperty, value); }
}
public static readonly DependencyProperty DateTimeMsgProperty =
DependencyProperty.Register("DateTimeMsg", typeof(object), typeof(FirstCustomControl),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public object DateTimeMsg
{
get
{
return (object)GetValue(DateTimeMsgProperty);
}
set
{
SetValue(DateTimeMsgProperty, value);
}
}
}
}
2、样式Generic.xaml
<Style TargetType="{x:Type local:FirstCustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FirstCustomControl}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="PART_DateTimeTextBox" Text="{TemplateBinding DateTimeMsg}"/>
<TextBlock x:Name="PART_ContentTextBox" Text="{TemplateBinding ContentMsg}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
3、应用
<local:FirstCustomControl x:Name="mycustom" HorizontalAlignment="Left" Margin="55,30,0,0" VerticalAlignment="Top"/>
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>