【问题标题】:XAML - Simple animation for simple custom controlXAML - 简单自定义控件的简单动画
【发布时间】:2013-04-08 23:21:05
【问题描述】:

我正在使用 C# 开发一个 Windows 应用商店应用程序,遇到了一些我无法想象的困难,但我不知道该怎么做。

在我的MainPage.xaml 上,我创建了一个用户控件:一个水平方向的StackPanel,里面只有一个Image 和一个TextBlock。像这样:

<!-- language: lang-xml -->
<StackPanel Width="300" Height="100" Orientation="Horizontal" Margin="0,0,0,20" Tapped="LoremIpsum_Tapped">
    <Image Source="/Assets/pic.jpg" Margin="20"/>
    <TextBlock FontFamily="Segoe UI" FontSize="30" VerticalAlignment="Center">
        Lorem Ipsum
    </TextBlock>
</StackPanel>

看起来像这样:

我将它用作一种自定义类型的按钮来访问某些子页面。现在的问题是,没有动画。我想拥有ListItems 在 SplitView 中的典型动画:按下时收缩,释放时或指针退出控件的虚拟边界时恢复正常。我找不到与那些 ListItems (Common.StandardStyles.xamlStandard130ItemTemplate) 关联的动画声明/定义。但我发现 (here) 有预定义的动画:PointerDownThemeAnimationPointerUpThemeAnimation。但是后来我花了很长时间才弄清楚如何将它们应用于控件。您可能认为this site specifically about those pointer theme animations(关于 C#)中提到的示例应该有所帮助,但它会导致 sample for HTML/Javascript animations。但我找到了解决方案hereherehere

所以我在控件资源中需要Storyboards,一个名称以便控件可以作为目标,并在代码隐藏中使用事件处理程序。

应用在我的 XAML 上,它变成了这样:

<!-- language: lang-xml -->
<StackPanel x:Name="LoremIpsum" Width="300" Height="100" Orientation="Horizontal" Margin="0,0,0,20" Tapped="LoremIpsum_Tapped" PointerPressed="LoremIpsum_AnimDown" PointerReleased="LoremIpsum_AnimUp" PointerExited="LoremIpsum_AnimUp">
    <Image Source="/Assets/pic.jpg" Margin="20"/>
    <TextBlock FontFamily="Segoe UI" FontSize="30" VerticalAlignment="Center">
        Lorem Ipsum
    </TextBlock>
    <StackPanel.Resources>
        <Storyboard x:Name="pointerDownStoryboard">
            <PointerDownThemeAnimation TargetName="LoremIpsum" />
        </Storyboard>
        <Storyboard x:Name="pointerUpStoryboard">
            <PointerUpThemeAnimation TargetName="LoremIpsum" />
        </Storyboard>
    </StackPanel.Resources>
</StackPanel>

加上代码隐藏中的其他事件处理程序:

<!-- language: lang-cs -->
private void Latest_AnimDown(object sender, PointerRoutedEventArgs e)
{
    pointerDownStoryboard.Begin();
}
private void Latest_AnimUp(object sender, PointerRoutedEventArgs e)
{
    pointerUpStoryboard.Begin();
}

这行得通。但是......由于我有很多这样的用户控件,我当然不想为每个控件添加所有这些。如前所述,Standard130ItemTemplate 没有帮助。所以我想到了自定义控件。我希望我可以定义一个MyStackPanel,它只不过是一个StackPanel + StoryBoards,并且定位到 x:Name 可以工作,也许我可以将事件处理程序放在 LayoutAwarePage 代码隐藏中,所以所有其他人都继承它。

我开始寻找如何做到这一点,并找到了如何创建自定义控件的示例:XAML user and custom controls sample

Generic.xml 中有自定义控件:

<!-- language: lang-xml -->
xmlns:local="using:UserAndCustomControls">
<Style TargetType="local:BasicCustomControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:BasicCustomControl">
                <Border
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

还有一个代码文件,但不是代码隐藏:

<!-- language: lang-cs -->
namespace UserAndCustomControls
{
    public sealed class BasicCustomControl : Control
    {
        public BasicCustomControl()
        {
            this.DefaultStyleKey = typeof(BasicCustomControl);
        }
    }
}

但是样本中没有任何关于动画的内容。所以我尝试了这个示例,尝试将StoryBoards 添加到边框或 ControlTemplate,将事件处理程序添加到自行创建的 Generic.xaml.cs 代码隐藏中。没有任何效果。

然后我找到了this,但不确定如何以及为什么将事件处理程序放入BasicCustomControl 类。反正我试过了:

Generic.xaml:

<!-- language: lang-xml -->
xmlns:local="using:UserAndCustomControls">
<Style TargetType="local:BasicCustomControl">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:BasicCustomControl">
                <Border x:Name="Border"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                    <Border.Resources>
                        <Storyboard x:Name="pointerDownStoryboard">
                            <PointerDownThemeAnimation TargetName="Border" />
                        </Storyboard>
                        <Storyboard x:Name="pointerUpStoryboard">
                            <PointerUpThemeAnimation TargetName="Border" />
                        </Storyboard>
                    </Border.Resources>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

BasicCustomControl.cs:

<!-- language: lang-cs -->
protected override void OnPointerPressed(PointerRoutedEventArgs e)
{
    ((Storyboard)this.Resources["PointerDownThemeAnimation"]).Begin(this);
}

那没用。 Begin() 方法不带参数并且没有参数构建成功,但是单击控件时出现 System.Runtime.InteropServices.COMException。

然后我在 SO 上找到了这个:How to add XAML storyboard animation to a full blown WPF Custom Control in an XBAP?

这似乎是一个有趣的解决方案,但我不明白。这里还有一些自定义控件的 VisualState 内容的描述,但我不知道如何将其应用于我的需求:Quickstart: control templates

现在我在这方面花了相当多的时间,我只是想对于这个简单的事情 - 一个简单的,甚至是自定义控件的预定义动画 - 必须有一个简单的解决方案。我希望我只是忽略了一些东西,它并没有那么复杂。

总结问题:

  1. Standard130ItemTemplate 的动画在哪里定义并“附加”到模板?
  2. 是否有一个我可以“继承”的控件以我想要的方式运行(只是三个事件的上/下动画)?
  3. 有没有一种方法可以使用 XAML + 代码创建我可以继承的那种控件?
  4. 有没有办法只使用 XAML 来做到这一点?这比前一种方式更可取吗?
  5. 有这方面的简单示例或教程吗?
  6. 是否有任何我可以下载和使用的自定义控件集合?我找不到任何东西。

【问题讨论】:

    标签: c# xaml windows-store-apps


    【解决方案1】:

    创建自定义样式。在此示例中,我将图像源绑定到内容字段。所以我只需要将我的图像路径放在该字段中(例如“Assets/Image.png”)。如果第一次添加这样的自定义动画更容易弄清楚,那就太好了。

        <Style x:Key="ImageButtonStyle" TargetType="ButtonBase">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ButtonBase">
                    <Grid x:Name="RootGrid" Background="Transparent">
                        <Image x:Name="ImageLabel" Source="{TemplateBinding Content}"/>
                        <Rectangle
                                x:Name="FocusVisualWhite"
                                IsHitTestVisible="False"
                                Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
                                StrokeEndLineCap="Square"
                                StrokeDashArray="1,1"
                                Opacity="0"
                                StrokeDashOffset="1.5"/>
                        <Rectangle
                                x:Name="FocusVisualBlack"
                                IsHitTestVisible="False"
                                Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
                                StrokeEndLineCap="Square"
                                StrokeDashArray="1,1"
                                Opacity="0"
                                StrokeDashOffset="0.5"/>
    
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <PointerDownThemeAnimation TargetName="ImageLabel" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusStates">
                                <VisualState x:Name="Focused">
                                    <Storyboard>
                                        <DoubleAnimation
                                                Storyboard.TargetName="FocusVisualWhite"
                                                Storyboard.TargetProperty="Opacity"
                                                To="1"
                                                Duration="0"/>
                                        <DoubleAnimation
                                                Storyboard.TargetName="FocusVisualBlack"
                                                Storyboard.TargetProperty="Opacity"
                                                To="1"
                                                Duration="0"/>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Unfocused" />
                                <VisualState x:Name="PointerFocused" />
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="CheckStates">
                                <VisualState x:Name="Checked">
                                    <Storyboard>
                                        <DoubleAnimation Duration="0" To="0" Storyboard.TargetName="OutlineGlyph" Storyboard.TargetProperty="Opacity"/>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundGlyph" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemForegroundThemeBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BackgroundCheckedGlyph" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Content" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource AppBarItemPressedForegroundThemeBrush}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Unchecked"/>
                                <VisualState x:Name="Indeterminate"/>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-07
      • 1970-01-01
      • 1970-01-01
      • 2011-10-02
      • 1970-01-01
      • 2014-07-23
      • 1970-01-01
      相关资源
      最近更新 更多