`

【WPF】创建基于模板的WPF控件

阅读更多
WPF可以创建两种控件,它们的名字也很容易让人混淆:用户控件(User Control)和定制控件(Customer Control),之所以如此命名,是因为用户控件更面向控件的“使用者”,以方面他们利用现成的控件组合成新的控件,而客户控件,更便于定制化(Customization),方便创建有别于现有控件的定制控件。

定制控件提供了行为和表现完全分离的开发模式,具有很高的灵活性,当然,也更难一些。这里我们通过创建个简单的搜索控件来看看如何开发定制控件:



首先我们创建一个WPF应用,在同一个solution里,再添加一个用户WPF控件库。

系统会自动在控件库里创建一个UserControl1.XAML,这个文件可以直接删除。在WPF控件库里添加一个新的项目,注意:应该选择定制控件而不是用户控件,如图:



现在程序结构看起来应该像这样子:


定制控件的模板会为我们建立FilterTextBox.cs和Generic.xaml文件。

前者内容如下:
public class FilterTextBox : Control{ 
   static FilterTextBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FilterTextBox), 
           new FrameworkPropertyMetadata(typeof(FilterTextBox)));
    }}


generic.xaml是定制控件的外观表现,默认在themes目录下
<Style TargetType="{x:Type local:FilterTextBox}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
        <Border Background="{TemplateBinding Background}" 
               BorderBrush="{TemplateBinding 
BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>


现在generic.xaml的border还是空的。

接下来,我们先为控件创建其行为。首先我们添加一个叫"Text"的依赖属性,这是用户输入的搜索文本。这儿我们创建了个Callback,这样属性改变时就会被调用。注意不要在CLR属性的getter和setter添加任何代码,因为在运行时,WPF会忽略这些属性而直接调用GetValue和SetValue.但是在xaml里使用属性时,你仍需要CLR属性。
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text",
                                        typeof(String),
                                        typeof(FilterTextBox),
                                        new UIPropertyMetadata(null,
                                        new PropertyChangedCallback(OnTextChanged),
                                        new CoerceValueCallback(OnCoerceText)));

        private static object OnCoerceText(DependencyObject o, Object value)
        {
            FilterTextBox filterTextBox = o as FilterTextBox;
            if (filterTextBox != null)
                return filterTextBox.OnCoerceText((String)value);
            else
                return value;
        }

        private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            FilterTextBox filterTextBox = o as FilterTextBox;
            if (filterTextBox != null)
                filterTextBox.OnTextChanged((String)e.OldValue, (String)e.NewValue);
        }

        protected virtual String OnCoerceText(String value)
        {
            return value;
        }

        protected virtual void OnTextChanged(String oldValue, String newValue)
        {
        }

        public String Text
        {
            // IMPORTANT: To maintain parity between setting a property in XAML   
            // and procedural code, do not touch the getter and setter inside   
            // this dependency property!   
            get
            {
                return (String)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }   


接着我们还要暴露出一些事件,这样当文本被修改时,控件的用户就会被通知到,这儿为控件添加一个"TextChangeEvent"。
        public static readonly RoutedEvent TextChangedEvent = EventManager.RegisterRoutedEvent("TextChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FilterTextBox));

        public event RoutedEventHandler TextChanged
        {
            add { AddHandler(TextChangedEvent, value); }
            remove { RemoveHandler(TextChangedEvent, value); }
        }


事件在OnTextChange方法里被触发:
        protected virtual void OnTextChanged(String oldValue, String newValue)
        {
            this.RaiseEvent(new RoutedEventArgs(FilterTextBox.TextChangedEvent, this));
        }


到这里,关于控件行为的代码已经基本完成了,我们继续前进,为我们的控件创建一个外观。我们添加一个DockPanel,并在其中加入一个文本框和一个按钮。按钮Dock在右边。然后我们把文本框的Text属性和我们控件的Text属性绑定起来。同时设置UpdateSourceTrigge为TextChanged,这样每次用户在文本框输入点什么东西,就会触发我们写的TextChanged事件。注意,文本框没有border,因为这儿不需要2个Border。
                <ControlTemplate TargetType="{x:Type local:FilterTextBox}">
                    <Border
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="3">
                        <DockPanel
                            LastChildFill="True"
                            Margin="1">
                            <Button
                                x:Name="PART_ClearFilterButton"
                                Content="X"
                                Width="20"
                                ToolTip="Clear Filter"
                                DockPanel.Dock="Right" />
                            <TextBox
                                x:Name="PART_FilterTextBox"
                                Text="{Binding Path=Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,RelativeSource={RelativeSource TemplatedParent}}"
                                BorderBrush="{x:Null}"
                                BorderThickness="0"
                                VerticalAlignment="Center" />
                        </DockPanel>
                    </Border>
                </ControlTemplate>


特别要注意的是这些控件的名称。他们都以"PART_"开头,这是WPF的标准方法用来表示那些需要被替换的控件,当我们要修改控件的模板的时候。如果有人为你的控件编写模板,你需要验证所用的部件的类型是控件所必需的。可以通过TemplatePart Attribute,并添加部件的名称和类型。
[TemplatePart(Name = "PART_FilterTextBox", Type = typeof(TextBox))]
[TemplatePart(Name = "PART_ClearFilterButton", Type = typeof(Button))]
public class FilterTextBox : Control{
    ...
}


我们还设想只有在文本框里有文本的时候,一个“清空”的按钮才显示出来。我们创建一个DataTriger来实现这个想法:
<ControlTemplate TargetType="{x:Type local:FilterTextBox}">
  <Border>
    ...
  </Border>  
 <ControlTemplate.Triggers>
    <DataTrigger Binding="{Binding Path=Text.Length, ElementName=PART_FilterTextBox}" Value="0">
      <Setter TargetName="PART_ClearFilterButton" Property="Visibility" Value="Collapsed" />
    </DataTrigger>
  </ControlTemplate.Triggers>
</ControlTemplate>


现在要处理的是当用户点击"清空"按钮时候的动作,可以通过重写OnApplyTemplate来实现。通过控件名调用GetTemplateChild可以得到控件的引用。当用户点击“清空”按钮时,我们想删除文本框里的所有文本。得到文本框的引用和上面的方法相同。
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate(); 
            Button clearFilterButton = base.GetTemplateChild("PART_ClearFilterButton") as Button;
            if (clearFilterButton != null)
            {
                clearFilterButton.Click += new RoutedEventHandler(ClearFilterButton_Click);
            }
        }

        private void ClearFilterButton_Click(Object sender, RoutedEventArgs e)
        {
            TextBox textBox = base.GetTemplateChild("PART_FilterTextBox") as TextBox;
            if (textBox != null)
            {
                textBox.Text = String.Empty;
            }
        }


现在可以在WPF应用里跑一下了!

参考:
http://pavanpodila.spaces.live.com/blog/cns!9C9E888164859398!135.entry
  • 大小: 17 KB
  • 大小: 68.6 KB
  • 大小: 17.4 KB
分享到:
评论

相关推荐

    WPF系统控件模板查看器

    WPF系统控件模板查看器,可以wpf控件样式文件,对学习wpf的样式很有帮助

    WPF自定义模板控件

    在提供的文件名`WindowsFormsApplication1`中,虽然没有具体的文件内容,但通常这是一个与WPF项目相关的Windows Forms应用程序项目,这表明可能存在一个交互或转换的场景,比如从WPF控件到Windows Forms的集成,或者...

    WPF标准控件模板查看程序(含代码)

    要查看或编辑WPF控件的标准模板,可以使用一些工具,例如“WPF标准控件模板查看程序”。这个程序提供了直观的方式来查看和理解每个控件模板的结构,这对于学习和理解WPF控件的呈现机制非常有帮助。 **四、使用代码...

    wpf控件模板工具

    wpf控件模板,标注控件模板工具,反编译微软windows各种风格

    WPF控件默认模板查看工具

    WPF控件默认模板查看工具是一款非常实用的辅助软件,它专为开发者设计,帮助他们深入理解并修改WPF控件的视觉表现。这款工具能够显示各种WPF控件的默认模板,使开发者可以方便地查看、学习和自定义这些模板,从而...

    WPF控件模板可视化树查看器

    本教程将深入探讨“WPF控件模板可视化树查看器”这一工具,它能帮助我们理解并探索控件模板的组成。 首先,我们来理解什么是控件模板。在WPF中,每个控件都有一个默认的模板,这个模板定义了控件的视觉表现。例如,...

    Wpf Mvvm 动态创建控件

    本篇将深入探讨如何在WPF MVVM模式下动态创建控件以及如何获取选中或设置的值。 1. **动态创建控件基础**: 在WPF中,动态创建控件通常涉及到`System.Windows.Controls`命名空间中的控件类,如TextBox、CheckBox、...

    ControlTemplateBrowser.rar wpf控件标准模板

    在Windows Presentation Foundation(WPF)中,`ControlTemplate`是一个非常关键的概念,它允许开发者自定义UI...通过这个工具,开发者可以提升对WPF控件定制能力的理解,从而创造出更加独特且用户体验优秀的应用程序。

    wpf控件及模板

    通过学习和实践使用WPF控件和模板,开发者可以创建出美观且交互性强的用户界面。模板的灵活性使得WPF应用程序可以拥有独特的视觉风格,同时保持代码的可维护性和重用性。在实际开发中,熟练掌握WPF的模板机制对于...

    WPF控件模板浏览器

    可查看wpf控件模板的源xaml,方便重写控件样式。。。。。。。。。。。。。。。。。。。。。。。。。。

    WPF 自定义控件库 常用控件封装&常用控件样式重写

    3. 委托样式(Style Based on): 可以创建基于其他样式的样式,通过设置`BasedOn`属性,继承并修改已有样式。 4. 数据触发器(Data Triggers):数据触发器允许根据数据绑定值的变化改变控件的样式,增强了界面的响应性...

    WPF日期控件模板重写

    这需要创建一个新的控件,可能基于`Slider`或者其他可滚动组件,并实现相应的事件处理逻辑。 6. **集成到项目中**:最后,将自定义的模板应用到项目中的`DatePicker`实例,可以通过设置控件的`Template`属性为新的...

    WPF 超炫界面模板

    WPF的设计目标是让开发者能够创建出美观、交互性强的应用程序,提升用户体验。 在"ModernUI.1.0.4.win8极简化界面设计"中,ModernUI(也称为MUI)是一个基于WPF的库,它为开发者提供了一套现代化、简洁的用户界面...

    WPF 查看系统控件的模板(VeiwControlTemplate.rar )

    WPF控件可以有默认的模板,也可以通过资源字典在应用程序或控件级别定义新的模板。模板查找遵循一定的规则,如控件首先查找自身定义的模板,然后向上遍历其父控件,直到找到模板或达到根元素。 5. **模板触发器...

    wpf控件模板查看器

    可以查询常用的wpf控件的模板,学习和编程的好工具

    wpf控件库重写美化了各控件,如按钮、表格等

    综上所述,WPF控件库的重写和美化是一个涉及多方面技术的工作,包括但不限于控件继承、样式和模板定制、数据绑定、动画、响应式设计以及可访问性。这样的工作不仅可以提高应用的视觉吸引力,还能增强用户体验,是...

    C#WPF版Halcon模板匹配+切图工具助手

    wpf调用HSmartWindowControlWPF控件,配合模板匹配算法,实现halcon匹配助手相当的功能,支持训练并自动调整参数以及保存模型等功能。切图工具以模板匹配工具助手框架进行开发,可以对标记分组,支持固定ROI切图或...

    WPF自定义时间控件,可选择日期、时间

    - 创建一个新的WPF控件通常从创建一个UserControl开始,这是WPF中的一个容器类,可以包含多个其他UI元素。 - 在`WPF_DateTimePicker\UserControls`目录下的文件可能包含了自定义DateTimePicker控件的源代码。一般...

    WPF自定义控件-旋钮

    总结起来,创建一个WPF自定义旋钮控件涉及到了对WPF基础的深入理解,包括模板、依赖属性、事件处理、动画和布局。这个过程不仅可以提升开发者的技术能力,还能提供一个独特而富有交互性的用户界面元素。通过学习和...

    自制WPF Chart控件

    在某些情况下,内置的WPF图表控件可能无法满足特定的需求,这时我们就需要创建自定义的Chart控件。 标题中的“自制WPF Chart控件”指的是开发者为满足特定需求而创建的自定义图表组件。这通常涉及到对数据可视化和...

Global site tag (gtag.js) - Google Analytics