编码前

在上一篇博客中,写的是一个UserControl的子类,它具有固定的外观(虽然也可以通过样式来进行修改,但受到的限制很大)。如果你想要使用这个控件的逻辑,但是希望在使用的时候可以更改控件的外观,比如希望将预览颜色的矩形放在滑动条的左边(控件中是放在右侧),这个时候你就可以定义一个无外观的控件(但是你可以编写它的默认样式)。在使用这个无外观控件的时候,你可以使用样式和模板来改变控件的外观,而你仍然可以使用这个控件中所定义的属性、事件等控件的功能。

  1.依赖属性、标准属性(属性包装器)、路由事件等和之前定义的ColorPicker是一样的。

  2.如果你让控件呈现默认的样子,你可以在/Themes/Generic.xaml中写一个默认样式模板,并在ColorPicker的构造函数中进行设置。

  3.将控件中设置一些部件,以便在控件使用者可以更容易地重写模板,并且不失控件的功能。

编码

  1. 常见一个类,命名为ColorPicker,继承自Control。因为控件需要做到与用户的交互,所以继承自再上层的类(如FrameworkElement)就不太合适了。
  2. 因为此控件要使用自定义的一个默认样式,所以需要通知系统。方式就是在控件的构造函数中使用DefaultStyleKeyProperty的属性。具体如下:
     public ColorPicker()
    {
    DefaultStyleKey = typeof (ColorPicker);
    }

    这样在使用控件的时候,系统就会去查找他的默认样式

  3. 现在可以写它的的默认样式。
    <1>新建一个Themes的文件夹
    <2>在Themes文件夹下新加一个名为Generic的类。新建成功后将类的内容清空,并将Generic的扩展名改为.xaml
    <3>在空白的xaml文件中加入如下内容
    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomeControl">
    </ResourceDictionary>

    记得要引入你的自定义控件的命名空间
    <4>下面就可以写你所要自定的默认样式了

    <Style TargetType="local:ColorPicker">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:ColorPicker">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Slider x:Name="PART_RedSlider" Minimum="0" Maximum="255"/>
    <Slider x:Name="PART_GreenSlider" Grid.Row="1" Minimum="0" Maximum="255" />
    <Slider x:Name="PART_BlueSlider" Grid.Row="2" Minimum="0" Maximum="255" />
    <Rectangle Grid.Column="1" Grid.RowSpan="3" Width="100" Height="100" Stroke="White" StrokeThickness="2">
    <Rectangle.Fill>
    <SolidColorBrush x:Name="PART_PreviewBrush"/>
    </Rectangle.Fill>
    </Rectangle>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    当使用控件时会自动查找到这个样式。控件显示你所定义的样式效果。

  4. 这个时候自定义控件中只有一个构造函数,现在要定义控件要会使用的属性、路由事件等。内容和上一篇博客中一样,就不写了。
  5. 我们的这个控件需要与模板中的一些元素建立联系。
    <1>首先要重载Control中的OnApplyTemplate函数。
    <2>这里我们使用到Silverlight中的绑定来建立联系,那么怎样取到模板中的对象呢,答案是使用GetTemplateChild函数。
    <3>取到模板中的对象以后就可以进行绑定了,将元素中的某些依赖属性绑定到你想要的控件中的依赖属性。完整的代码如下:
             public override void OnApplyTemplate()
    {
    base.OnApplyTemplate();
    Slider redSlider = GetTemplateChild("PART_RedSlider") as Slider;
    Slider greenSlider = GetTemplateChild("PART_GreenSlider") as Slider;
    Slider blueSlider = GetTemplateChild("PART_BlueSlider") as Slider; BindingSlider(redSlider, "Red");
    BindingSlider(greenSlider, "Green");
    BindingSlider(blueSlider, "Blue"); SolidColorBrush brush = GetTemplateChild("PART_PreviewBrush") as SolidColorBrush;
    if (brush != null)
    {
    Binding binding = new Binding("Color");
    binding.Source = this;
    binding.Mode = BindingMode.TwoWay;
    BindingOperations.SetBinding(brush, SolidColorBrush.ColorProperty, binding);
    }
    } private void BindingSlider(Slider slider, string color)
    {
    if (slider == null)
    return;
    Binding binding=new Binding();
    binding.Source = this;
    binding.Path =new PropertyPath(color);
    binding.Mode=BindingMode.TwoWay;
    slider.SetBinding(Slider.ValueProperty, binding);
    }

    需要注意的地方:1、为了增强控件的容错能力,要有个检验空值的情况。
                       2、由于SolidColorBrush不是继承自FrameworkElement,所以他没有像Slider的SetBinding函数,而要使用BindingOperations来实行绑定(这个问题纠结了好久)。
                          3、还有就是绑定源和绑定目标要区分清楚,马虎的我又纠结了一会儿。
                          4、将将控件预期的FrameworkElement对象(比如此例中的PART_RedSlider和PART_PreviewBrush等)使用TemplatePartAttribute,指定预期元素的名称和类型。

        [TemplatePart(Name = "PART_RedSlider", Type = typeof(Slider)),
    TemplatePart(Name = "PART_GreenSlider", Type = typeof(Slider)),
    TemplatePart(Name = "PART_BlueSlider", Type = typeof(Slider)),
    TemplatePart(Name = "PART_PreviewBrush", Type = typeof(SolidColorBrush))]
    public class ColorPicker :Control
    {
    ......
    }
  6. 现在自定义控件就算是完成了。如下使用即可(记得引入程序集和命名空间啊):
    <control:ColorPicker  Margin="12" Color="Violet" ColorChanged="ColorPicker_OnColorChanged"></control:ColorPicker>

    效果图。并且右侧预览的颜色会随着Slider值得变化而变化。

  7. 如果你想使用重新编写的样式,你可以在你所在的页面资源或是其他地方定义一个新的样式,例如
   <phone:PhoneApplicationPage.Resources>
<Style x:Key="style" TargetType="control:ColorPicker">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:ColorPicker">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse Stroke="Red" VerticalAlignment="Top" StrokeThickness="2" Width="100" Height="200">
<Ellipse.Fill>
<SolidColorBrush x:Name="PART_PreviewBrush"/>
</Ellipse.Fill>
</Ellipse>
<StackPanel Grid.Column="1" >
<Slider Height="200" Minimum="0" Maximum="255" Orientation="Vertical" x:Name="PART_RedSlider"/>
<TextBlock TextAlignment="Center" Text="Red" Foreground="Red"/>
</StackPanel>
<StackPanel Grid.Column="2">
<Slider Height="200" Minimum="0" Maximum="255" Orientation="Vertical" x:Name="PART_GreenSlider"/>
<TextBlock TextAlignment="Center" Text="Green" Foreground="Green"/>
</StackPanel>
<StackPanel Grid.Column="3">
<Slider Height="200" Minimum="0" Maximum="255" Orientation="Vertical" x:Name="PART_BlueSlider"/>
<TextBlock TextAlignment="Center" Text="Blue" Foreground="Blue"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>

这里重新设计了控件的样式,只要在控件的时候引用的可以了:

<control:ColorPicker Style="{StaticResource style}"  Margin="12" Color="Violet" ColorChanged="ColorPicker_OnColorChanged"></control:ColorPicker>

此时重现的效果图就是这个样子:

关于样式和模板,请参考MSDN即可。

编码后

  1. 自定义控件的时候将你所要使用到与逻辑部分有关的模板里的空间进行命名,类似这里的Part_RedSlider,为了控件使用者能够方便正常的重载样式,将这些做成部件。
    做成部件后,控件使用者必须在他自己的样式中使用这个部件,才使得控件功能的完整
  2. 还可以在自定义控件中加入状态,根据不同的状态有不同的显示,在状态的转换中可以加入一些动画来增强效果,下一篇将会涉及到此类内容。
  3. 关于使用ControlTemplate来定义现有控件外观以及使用ControlTemplate来创建新的控件,可以查看MSDN上的相关详细的介绍。

Windows phone 自定义控件(无外观控件)——ColorPicker的更多相关文章

  1. WPF教程十二:了解自定义控件的基础和自定义无外观控件

    这一篇本来想先写风格主题,主题切换.自定义配套的样式.但是最近加班.搬家.新租的房子打扫卫生,我家宝宝6月中旬要出生协调各种的事情,导致了最近精神状态不是很好,又没有看到我比较喜欢的主题风格去模仿的, ...

  2. 【WPF学习】第六十五章 创建无外观控件

    用户控件的目标是提供增补控件模板的设计表面,提供一种定义控件的快速方法,代价是失去了将来的灵活性.如果喜欢用户控件的功能,但需要修改使其可视化外观,使用这种方法就有问题了.例如,设想希望使用相同的颜色 ...

  3. Windows phone自定义控件(无外观控件)——FlipPanel

    编码前 无外观自定义控件的定义在上一篇中已经有了,至于这一篇的自定义控件,比之前多加入了状态的变化,就像默认的Button具有Pressed.Normal等状态.在状态转变的同时可以加上一些动画,可以 ...

  4. 背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理

    [源码下载] 背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理 作者:webabcd 介绍背水一战 Windows 10 之 控件(自定义控件) ...

  5. C# 自定义控件VS用户控件

    1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Con ...

  6. 《深入理解Windows Phone 8.1 UI控件编程》基于最新的Runtime框架

    <深入理解Windows Phone 8.1 UI控件编程>本书基于最新的Windows Phone 8.1 Runtime SDK编写,全面深入地论述了最酷的UI编程技术:实现复杂炫酷的 ...

  7. 重新想象 Windows 8 Store Apps (15) - 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState, VisualStateManager

    原文:重新想象 Windows 8 Store Apps (15) - 控件 UI: 字体继承, Style, ControlTemplate, SystemResource, VisualState ...

  8. 重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试

    原文:重新想象 Windows 8 Store Apps (16) - 控件基础: 依赖属性, 附加属性, 控件的继承关系, 路由事件和命中测试 [源码下载] 重新想象 Windows 8 Store ...

  9. 重新想象 Windows 8 Store Apps (3) - 控件之内容控件: ToolTip, Frame, AppBar, ContentControl, ContentPresenter; 容器控件: Border, Viewbox, Popup

    原文:重新想象 Windows 8 Store Apps (3) - 控件之内容控件: ToolTip, Frame, AppBar, ContentControl, ContentPresenter ...

随机推荐

  1. 关于String.valueOf()和.toString的问题

    以下是String.valueOf()的源代码 public static String valueOf(Object obj) {     return (obj == null) ? " ...

  2. python selenium-2 定位元素

    元素 方法 示例 id find_element_by_id('su') driver.get("http://www.baidu.com")driver.find_element ...

  3. maven package 命令报:-source1.3 中不支持注释错误

    在使用maven 打包或者编译时报:-source1.3 中不支持注释错误解决方案如下: <build>  <plugins>   <plugin>    < ...

  4. Ubuntu登录系统失败的解决方案

    问题一: 只能用guest用户登录下,如何切换成普通用户登录 解决: 重启,同时按Esc建,直至进入到恢复模式下: 选择第一项,进入: 找到ro...那一行,把ro之后的删除,并把ro修改为rw si ...

  5. 向Nexus仓库推送/使用各种组件

    1.Nuget仓库 使用NuGetPackageExplorer打包制作自己的nupkg https://github.com/NuGetPackageExplorer/NuGetPackageExp ...

  6. SpringBoot入门篇--热部署

    在项目的开发过程中我们难免会涉及到对代码的修改,有bug我们也需要对代码进行修改,这时候我们就需要重启服务器.但是,我们知道的是这个过程是相当的痛苦的,重启服务器涉及到了所有资源的重新加载,过程那是相 ...

  7. CNN入门笔记

    在之前的学习中,没有认真了解卷积神经网络,由于一些原因需要使用CNN来做图像分类,开始学习了卷积神经网络,参考了一些资料并做了这份记录 为什么要用卷积神经网络 在图像处理中,往往把图像表示为像素的向量 ...

  8. (16/24) webpack打包后的调试方法

    在程序开发中,调试程序是最频繁的,那使用了webpack后,所有的代码都打包到了一起,这给调试带来了困难,但是webpack在设计时就已经考虑好了这点,它支持生产Source Maps来方便我们的调试 ...

  9. 0_Simple__simpleMPI

    MPI 的简单使用 ▶ 源代码.主机根结点生成随机数组,发布副本到各结点(例子用孩子使用了一个结点),分别使用 GPU 求平方根并求和,然后根结点使用 MPI 回收各节点的计算结果,规约求和后除以数组 ...

  10. windows10系统右键添加cmd命令

    https://blog.csdn.net/Mr_BEelzebub/article/details/78776104 首先,在桌面新建一个文本文档. Windows Registry Editor ...