none
咨询一个WPF自定义模板的按钮,求思路 RRS feed

  • 问题

  • 如上图,所显示的是一个按钮,类似radioButton来实现单选功能,试问如果用WPF来实现的话,有什么思路吗。。

    最好还要有从右向左移动的动画= =

    2015年12月25日 7:49

答案

  • 如果只是单个按钮的话,你这种需求应该是重写ToggleButton的。

    具体思路就是:

    1.自定义控件,继承ToggleButton,添加你想要的额外属性;

    2.参考ToggleButton的默认模板,了解该控件的结构;

    3.修改模板,添加状态,添加动画。

    我给自定义的TreeView写过一个ToggleButton模板,为了实现左侧小箭头的旋转效果,你可以参考一下:

        <PathGeometry x:Key="PathGeometry" Figures="M5.75,10.125 L5.75,25.25 L16.8125,17.813541 z " />
        <SolidColorBrush x:Key="PressedBrush" Color="#66000000" />
        <SolidColorBrush x:Key="MouseOverBrush" Color="#33000000" />
        <SolidColorBrush x:Key="NormalBrush" Color="#00000000" />
        <!-- region AnimatedExpander -->
        <ControlTemplate x:Key="AnimateExpanderToggleButtonTemplate" TargetType="{x:Type ToggleButton}">
            <Border x:Name="Root">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)"
                                                               Storyboard.TargetName="Root">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource NormalBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="MouseOver">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)"
                                                               Storyboard.TargetName="Root">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource MouseOverBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Pressed">
                            <Storyboard>
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background)"
                                                               Storyboard.TargetName="Root">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PressedBrush}" />
                                </ObjectAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Disabled" />
                    </VisualStateGroup>
                    <VisualStateGroup x:Name="CheckStates">
                        <VisualStateGroup.Transitions>
                            <VisualTransition GeneratedDuration="0:0:0.3" />
                        </VisualStateGroup.Transitions>
                        <VisualState x:Name="Checked">
                            <Storyboard>
                                <DoubleAnimationUsingKeyFrames
                                    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"
                                    Storyboard.TargetName="ExpandPath">
                                    <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="90" />
                                </DoubleAnimationUsingKeyFrames>
                            </Storyboard>
                        </VisualState>
                        <VisualState x:Name="Unchecked" />
                        <VisualState x:Name="Indeterminate" />
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
                <Grid x:Name="LayoutRoot">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <ContentPresenter x:Name="HeaderPresenter" Grid.Column="1" />
                    <Border Grid.Column="0" Width="20" Height="20" BorderThickness="0" Padding="5">
                        <Path x:Name="ExpandPath"
                              StrokeThickness="0"
                              Data="{StaticResource PathGeometry}"
                              Stretch="Uniform"
                              Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
                            <Path.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform />
                                    <SkewTransform />
                                    <RotateTransform />
                                    <TranslateTransform />
                                </TransformGroup>
                            </Path.RenderTransform>
                        </Path>
                    </Border>
                </Grid>
            </Border>
        </ControlTemplate>

    因为是为特定情况写的模板,许多地方被写死(硬编码),所以不很灵活;而且由于需求简单,因此也没有新建控件,直接应用到ToggleButton就可以了,使用起来像这样:

        <ToggleButton Template="{StaticResource LTreeViewItemToggleButtonTemplate}"/>

    窗口中会绘制一个小三角形,点击会在两种状态中切换,并且有旋转动画。

    红框是我画的。

    注意:

    上述示例中,我没有新建控件,所以没有涉及到默认样式和模板的问题,如果你需要自定义一个控件,则需要考虑;

    如果使用了默认样式和模板,那么在使用你的自定义控件时就不需要指定使用的模板了;

    新建自定义控件的直接用处是,添加更多的依赖属性,以供模板调用(TemplateBinding);

    状态是关键特性之一,它能理清结构并简化代码;

    多使用Blend,用它能够方便的编辑控件的模板(管理状态、构建动画、调节属性)。

    查看控件默认模板的方法有很多,我在帖子 Treeview控件,子节点的双击事件 中提到过,你可以看看。

    希望这个例子能给你一些帮助。


    • 已编辑 Lymim 2015年12月25日 9:43 补充
    • 已标记为答案 小菜包砸 2016年1月8日 7:40
    2015年12月25日 9:31