编码前

  无外观自定义控件的定义在上一篇中已经有了,至于这一篇的自定义控件,比之前多加入了状态的变化,就像默认的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. SpringMVC 实现返回一段数据 & 实现自动发送json格式数据 - AJAX

    实现返回一段数据 - AJAX 当页面通过AJAX来访问Controller时,期望得到的不是一个页面而是一段数据,此时可以使用如下方法,直接向相应中写入数据: /** * 直接向响应中写出数据,通常 ...

  2. 汽车收费 C++ PTA

    7-1 汽车收费(10 分) 现在要开发一个系统,管理对多种汽车的收费工作. 给出下面的一个基类框架 class Vehicle { protected: string NO;//编号 public: ...

  3. 垃圾收集器之:G1收集器

    G1垃圾收集器是一种工作在堆内不同分区上的并发收集器.分区既可以归属于老年代,也可以归属新生代,同一个代的分区不需要保持连续.为老年代设计分区的初衷是我们发现并发后台线程在回收老年代中没有引用的对象时 ...

  4. 装饰模式 (Decoratory)

    动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活. 装饰模式就是利用 SetComponent 来对对象进行包装的,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个 ...

  5. BP神经网络的公式推导

    如果感觉自己看不懂,那就看看我博客的梯度下降法,博文最后的感知机也算最简单的BP神经网络吧,用的也是反馈(w,b):典型梯度下降法 BP网络的结构 BP网络的结构如下图所示,分为输入层(Input), ...

  6. uva-539-枚举

    题意: 给你一个无向图,找最长路. 俩份代码,感觉map[][]简单易懂啊 #include<stdio.h> #include<iostream> #include<s ...

  7. Git 代码版本还原方法

    因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,Git 代码版本还原方法 在使用 Git 管理自己的代码和资料时,难免会遇到意料 ...

  8. vsphere使用笔记

    一,安装虚拟机,需要使用vsphere client上传镜像,提示错误:   vsphere client 上传文件:Failed to log into NFC Server   , 解决办法:用浏 ...

  9. leetcode27

    public class Solution { public int RemoveElement(int[] nums, int val) { var len = nums.Length; ; ; i ...

  10. 使用示例带你提前了解 Java 9 中的新特性

    使用示例带你提前了解 Java 9 中的新特性 转载来源:https://juejin.im/post/58c5e402128fe100603cc194 英文出处:https://www.journa ...