编码前

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

  FlipPanel控件的功能介绍:它具有两个状态,Normal和Flipped。当Normal状态时,控件显示正面的内容;当为Flipped状态时,控件显示反面的内容。除此之外,控件还有一个按钮,用来两个状态的跳转,并且也会随着状态的变化而有显示上的不同。

编码:

  1. 自定义一个继承于Control的FlipPanel的类,像上一篇类似,在构造函数中指示将使用它的默认样式:

    public class FlipPanel :Control
    {
    public FlipPanel()
    {
    DefaultStyleKey = typeof (FlipPanel);
    }
    。。。。。。
    }
  2. 根据需要定义一组依赖属性以及公开的属性封装器:
    <1>用来显示控件当前状态的属性:IsFlipped
    <2>正、方面的内容的属性:FrontContent、BackContent
    <3>在内容显示的时候设置边框光滑度的属性:CornerRadius
    public static readonly DependencyProperty IsFlipedProperty = DependencyProperty.Register(
    "IsFliped", typeof (bool), typeof (FlipPanel), new PropertyMetadata(default(bool))); public bool IsFliped
    {
    get { return (bool) GetValue(IsFlipedProperty); }
    set { SetValue(IsFlipedProperty, value); }
    } public static readonly DependencyProperty FrontContentProperty = DependencyProperty.Register(
    "FrontContent", typeof (object), typeof (FlipPanel), new PropertyMetadata(default(object))); public object FrontContent
    {
    get { return (object) GetValue(FrontContentProperty); }
    set { SetValue(FrontContentProperty, value); }
    } public static readonly DependencyProperty BackContentProperty = DependencyProperty.Register(
    "BackContent", typeof (object), typeof (FlipPanel), new PropertyMetadata(default(object))); public object BackContent
    {
    get { return (object) GetValue(BackContentProperty); }
    set { SetValue(BackContentProperty, value); }
    } public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
    "CornerRadius", typeof (CornerRadius), typeof (FlipPanel), new PropertyMetadata(default(CornerRadius))); public CornerRadius CornerRadius
    {
    get { return (CornerRadius) GetValue(CornerRadiusProperty); }
    set { SetValue(CornerRadiusProperty, value); }
    }
  3. 上一篇的方法类似,在Themes/Generic.xaml中来定义自定义控件的默认样式:
    <Style TargetType="local:FlipPanel">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:FlipPanel">
    <Grid>
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <!--This is the front content.-->
    <Border x:Name="FrontContent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}"
    Background="{TemplateBinding Background}">
    <ContentPresenter Content="{TemplateBinding FrontContent}"/>
    </Border>
    <!--This is the back content.-->
    <Border x:Name="BackContent" Opacity="0" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
    CornerRadius="{TemplateBinding CornerRadius}">
    <ContentPresenter Content="{TemplateBinding BackContent}"/>
    </Border>
    <!--This is the flip button.-->
    <ToggleButton Grid.Row="1" x:Name="FlipButton" RenderTransformOrigin="0.5,0.5" Margin="0,10,0,0" Height="30" Width="30">
    <ToggleButton.Template>
    <ControlTemplate>
    <Grid>
    <Ellipse Stroke="Red" Fill="DarkGray"/>
    <Path Data="M1,1.5 L4.5,5 8,1.5" Stroke="Red" HorizontalAlignment="Center" VerticalAlignment="Center" StrokeThickness="2"/>
    </Grid>
    </ControlTemplate>
    </ToggleButton.Template>
    <ToggleButton.RenderTransform>
    <RotateTransform x:Name="FlipButtonTransform" Angle="-90" />
    </ToggleButton.RenderTransform>
    </ToggleButton> <VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="ViewStates">
    <VisualState x:Name="Normal">
    <Storyboard>
    <DoubleAnimation Storyboard.TargetName="BackContent" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1"/>
    <DoubleAnimation Storyboard.TargetName="FrontContent" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>
    <DoubleAnimation Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="Angle" To="-90" Duration="0:0:1"/>
    </Storyboard>
    </VisualState>
    <VisualState x:Name="Flipped">
    <Storyboard>
    <DoubleAnimation Storyboard.TargetName="BackContent" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>
    <DoubleAnimation Storyboard.TargetName="FlipButtonTransform" Storyboard.TargetProperty="Angle" To="90" Duration="0:0:1"/>
    <DoubleAnimation Storyboard.TargetName="FrontContent" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1"/>
    </Storyboard>
    </VisualState>
    </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    1.上面的XAML与之前的定义控件外观差不多,有两个Border,一个ToggleButton组成。其中Border中显示的ContentPresenter中的内容绑定到了控件中内容的属性上,及FrontContent和BackContent。并且多了一个关于状态的设置。
          2.这里如同上面介绍的,控件具有两个状态,这两个状态属于对立的状态(及同时只能处在其中一个状态中),他们是在一个名为ViewStates的状态分组中。
          3.在转化到不同状态时,使用了动画,用来让转化过程平滑一点。
          4.建议为FlipPanel控件类应用TemplatePart特性,包括可视化状态TemplateVisualState:

     [TemplatePart(Name = "FlipButton",Type = typeof(ToggleButton)),
    TemplateVisualState(Name = "Normal",GroupName = "ViewStates"),
    TemplateVisualState(Name = "Flipped",GroupName = "VisualStates")]
  4. 模板中的那个ToggleButton按钮用来转换状态,所以对于它,应该处理它的点击事件。依旧是在重写的OnApplyTemplate函数中来获取控件,并注册它的Click事件。
             public override void OnApplyTemplate()
    {
    base.OnApplyTemplate();
    ToggleButton flipButton = GetTemplateChild("FlipButton") as ToggleButton;
    if(flipButton!=null)
    flipButton.Click += flipButton_Click;
    } void flipButton_Click(object sender, RoutedEventArgs e)
    {
    this.IsFliped = !this.IsFliped;
    }

    此刻点击FlipButton按钮就可以改变控件的状态了,也就是IsFlipped的值。

  5. 现在状态是有了(写在XAML中),单击FlipButton按钮也会改变控件的状态值。但是我们怎样让IsFipped的状态值关联到样式模板中的状态呢?答案是使用VisualStateManager类来控制状态的转变,用的的是他的其中的函数GotoState。
             private void OnChangedState(bool useTransitions)
    {
    if (IsFliped)
    VisualStateManager.GoToState(this, "Flipped", useTransitions);
    else
    VisualStateManager.GoToState(this, "Normal", useTransitions);
    }

        GoToState中第一个参数为发生状态变化的控件,第二个参数就是控件所要到达的状态,第三个参数是否使用状态过渡
    并且在OnApplyTemplate函数和flipButton_Click函数中进行调用

             public override void OnApplyTemplate()
    {
    base.OnApplyTemplate();
    ToggleButton flipButton = GetTemplateChild("FlipButton") as ToggleButton;
    if(flipButton!=null)
    flipButton.Click += flipButton_Click;
    OnChangedState(false);
    } void flipButton_Click(object sender, RoutedEventArgs e)
    {
    this.IsFliped = !this.IsFliped;
    OnChangedState(true);
    }
  6. 简单的带状态的自定义控件已经定义好了,之后就可以直接使用了。例:
    <control:FlipPanel  BorderBrush="PowderBlue" BorderThickness="2" CornerRadius="10" Margin="12">
    <control:FlipPanel.FrontContent>
    <StackPanel Margin="6">
    <Button Background="Purple" Margin="3" Content="FrontContent1"/>
    <Button Background="Red" Margin="3" Content="FrontContent2"/>
    <Button Background="Blue" Margin="3" Content="FrontContent3"/>
    <Button Background="GreenYellow" Margin="3" Content="FrontContent4"/>
    </StackPanel>
    </control:FlipPanel.FrontContent>
    <control:FlipPanel.BackContent>
    <Grid Margin="12">
    <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
    <RowDefinition/>
    </Grid.RowDefinitions>
    <TextBlock FontSize="20" Margin="3" HorizontalAlignment="Center" Foreground="Peru">This is the FlipPanel's back.</TextBlock>
    <Button Grid.Row="2" Margin="3" Content="Back" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
    </control:FlipPanel.BackContent>
    </control:FlipPanel>

    Normal状态下截图为

    当点击下方的按钮以后,控件状态变为Flipped:

编码后

  1. 当然你可以使用Style在使用的时候自定义一个样式,从而改变他的默认样式,达到自己想要的布局。
  2. 自定义无外观的控件时,最重要的是想着,要这个控件实现什么样的功能和逻辑。并抽出功能和逻辑中会使用到的控件作为模板部件,以便让控件使用者重写样式方便的同时不会丢掉控件的功能。
  3. 设计好样式的同时,要根据需要来设计控件不同状态之间的转换,可以通过动画来进行状态转换时的效果以及处于状态中时,控件的显示效果。

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

  1. Windows phone 自定义控件(无外观控件)——ColorPicker

    编码前 在上一篇博客中,写的是一个UserControl的子类,它具有固定的外观(虽然也可以通过样式来进行修改,但受到的限制很大).如果你想要使用这个控件的逻辑,但是希望在使用的时候可以更改控件的外观 ...

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

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

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

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

  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. LINK : fatal error LNK1158: 无法运行“rc.exe”解决办法 and Visual Studio 2017 下载安装

    LINK : fatal error LNK1158: 无法运行“rc.exe” 首先下载软件包:https://pan.baidu.com/s/1L1N1sikXUaZZd-9nmZnwjA 第一个 ...

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

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

  3. CSS 标签实例二

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  4. 在Linux 系统 Latex安装 使用入门教程

    来源: http://blog.chinaunix.net/u/25605/showart_2100398.html 入门介绍好文:TeX.LaTeX.TeXLive 小结 笔记详情:http://v ...

  5. Windows 8的用户模式Shim Engine小探及利用

    转载: https://bbs.pediy.com/thread-175483.htm Windows Shim Engine,即Windows 兼容性模式实现引擎,在exe文件的属性对话框中有一个兼 ...

  6. python + docker, 实现天气数据 从FTP获取以及持久化(五)-- 利用 Docker 容器化 Python 程序

    背景 不知不觉中,我们已经完成了所有的编程工作.接下来,我们需要把 Python 程序 做 容器化 (Docker)部署. 思考 考虑到项目的实际情况,“持久化天气”的功能将会是一个独立的功能模块发布 ...

  7. MiniDump产生工具

    1:分析程序异常等等信息,在入口处初始化即可 //生成Dump文件信息 OS:Windows #pragma once #include <windows.h> #include < ...

  8. storm的可靠性

    消息确认机制: 在数据发送的过程中可能会数据丢失导致没能接收到,spout有个超时时间(默认是30S),如果30S过去了还是没有接收到数据,也认为是处理失败. 运行结果都是处理成功 参考代码Storm ...

  9. diskspd的使用

    参数翻译 可测试目标: file_path 文件abc.file #<physical drive number> #1为第一块物理磁盘[谨慎,别拿系统盘测试,一般用于准备投入的数据磁盘测 ...

  10. Xshell批量导入IP地址

    我的xshell被覆盖了~~~结果原来的host没了,很郁闷要一个一个添加,网上找了很长时间在Xshell中批量添加IP的文章,结果都不行. 最后找到了https://blog.netsarang.c ...