田敏
返回博客列表
技术

WPF与Winform

田敏
2024-03-2710 分钟阅读
WPFC#Desktop

1、WPF与Winform

UI开发效率和可维护性差异

可以说,WPF在UI的开发效率和可维护性方面具有压倒性的优势。Windows 窗体使用 GUI 通过“自动生成的程序”构建布局。WPF 通过 XAML 以“基于文本”的方式构建布局。例如,在创建类似的屏幕时,您可以通过复制粘贴来创建两者,但由于 WPF 使用文本复制粘贴,因此工作更容易。此外,当更改现有的屏幕布局等时。z“我以为我改变了按钮的位置,但由于操作不正确,导致控件发生了意外的改变。”

请记住这样的情况。那么,对于Windows Forms来说,需要检查自动生成的程序之间的差异,这可能是一个问题,但是有可能会忽略源代码的哪一行发生了变化,我认为是有的。对于 WPF,所有 UI 信息都是基于文本的,因此差异比 Windows 窗体更清晰,并且更容易发现意外的更改。以上几点极大地影响了UI的开发效率和可维护性。

改进的屏幕绘图性能(GPU)

WPF的另一个优点是它在硬件方面的优越性。

Windows Forms的绘图技术是“GDI”或“GDI+”,其性能取决于CPU。

在WPF中,绘图处理也是使用“Direct3D”在GPU上进行,减轻了CPU的负担。

这些极大地影响了最终用户的应用程序体验。

特别是,开发人员必须意识到最终用户使用的机器规格不如开发人员的机器。

你们中的许多人可能都经历过,由于开发人员机器规格的高性能而在开发过程中没有注意到的不便在使用最终用户的机器规格时变得明显。

WPF 的优点之一是它可以充分利用最终用户的机器规格。

响应式(多种分辨率)兼容

Windows 窗体的一个常见问题是它不能很好地适应当今可用的高分辨率显示器。可能有问题。

原因在于创建UI的过程。

Windows 窗体使用拖放方法将控件放置在“绝对位置”以创建 UI。

WPF是通过在XAML(布局文件)中编写“相对位置”来基于相对位置创建的。

2、WPF特点

3、WPF体系架构

1、三层体系结构

image-20240220204736191

2、基本控件集合类

4、常用布局面板

WPF(Windows Presentation Foundation)中有多种布局控件,每个都适用于不同的场景。以下是一些常用的 WPF 布局控件:

  1. Grid(网格):
    • 允许将 UI 元素按行和列进行组织,非常灵活。
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <!-- UI 元素放置在 Grid 的不同行和列中 -->
</Grid>
  1. StackPanel(堆栈面板):
    • 按照水平或垂直方向堆叠子元素。
<StackPanel>
    <!-- 子元素按顺序垂直堆叠 -->
    <Button Content="Button 1"/>
    <Button Content="Button 2"/>
</StackPanel>
  1. DockPanel(停靠面板):
    • 将子元素停靠在面板的不同位置(顶部、底部、左侧、右侧、中心)。
<DockPanel>
    <Button Content="Top" DockPanel.Dock="Top"/>
    <Button Content="Left" DockPanel.Dock="Left"/>
    <Button Content="Right" DockPanel.Dock="Right"/>
    <Button Content="Bottom" DockPanel.Dock="Bottom"/>
    <Button Content="Center"/>
</DockPanel>
  1. WrapPanel(换行面板):
    • 在空间不足时自动换行,按行或列排列子元素。
<WrapPanel>
    <!-- 子元素在空间不足时自动换行 -->
    <Button Content="Button 1"/>
    <Button Content="Button 2"/>
</WrapPanel>
  1. Canvas(画布):
    • 允许以绝对位置放置子元素。
<Canvas>
    <!-- 子元素以绝对位置放置 -->
    <Button Content="Button 1" Canvas.Left="10" Canvas.Top="20"/>
    <Button Content="Button 2" Canvas.Left="50" Canvas.Top="80"/>
</Canvas>

这些是常见的 WPF 布局控件,你可以根据需要选择适合你应用程序 UI 结构的布局方式。

5、特殊的容器:

1、ScrollViewer:

ScrollViewer 是 WPF(Windows Presentation Foundation)中的一个控件,它提供了滚动功能,允许用户在一个有限的可视区域内查看超过该区域尺寸的内容。ScrollViewer通常用于包装其他控件,例如StackPanelGridTextBlock,以便在内容超过可视区域时,用户可以通过滚动条或鼠标滚轮来查看全部内容。

以下是一个简单的示例,演示了如何使用ScrollViewer

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ScrollViewer Example" Height="300" Width="400">
    <Grid>
        <!-- ScrollViewer wraps a TextBlock -->
        <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <TextBlock TextWrapping="Wrap">
                <!-- Long content to demonstrate scrolling -->
                Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ac justo vitae orci malesuada suscipit. Suspendisse potenti. Curabitur facilisis hendrerit nisl, id fermentum tortor suscipit et. Sed fringilla scelerisque felis, a facilisis ligula ultrices nec. Nulla facilisi. Curabitur in nibh ut urna elementum tincidunt vel ut ante. Phasellus feugiat purus vel urna bibendum, a tincidunt erat scelerisque. Duis sit amet luctus arcu. Sed id quam eu nisl malesuada vehicula nec et quam. Fusce vel tincidunt urna, id euismod erat. Integer id cursus nulla. Duis in leo id dolor tincidunt venenatis. Integer id sodales libero, vitae tincidunt sapien.
            </TextBlock>
        </ScrollViewer>
    </Grid>
</Window>

2、HeaderContentControl

HeaderedContentControl 是 WPF 中的一个控件,它是 ContentControl 的一个扩展,提供了带有标题的内容控制。这个控件常用于具有标题和相关内容的情况,例如在选项卡、面板或其他容器中。

以下是一些关键属性和使用示例:

主要属性:
  1. Header
    • 定义 HeaderedContentControl 的标题,可以是文本、图标或其他 UI 元素。
  2. Content
    • 定义 HeaderedContentControl 中的主要内容,即标题下方的内容。
示例:
<HeaderedContentControl Header="Headered Content Control Example">
    <!-- Content goes here -->
    <StackPanel>
        <TextBlock>Content goes here</TextBlock>
        <Button Content="Click Me" />
    </StackPanel>
</HeaderedContentControl>

在这个例子中,HeaderedContentControlHeader 属性被设置为 "Headered Content Control Example",而 Content 属性包含一个包含文本块和按钮的 StackPanel

HeaderedContentControl 在 UI 中的使用场景通常是希望将标题和相关内容组合在一起,形成一个可折叠或可切换的单元。在某些情况下,你也可以通过继承 HeaderedContentControl 创建自定义的带标题的内容控件。

3、Expander

Expander 是 WPF 中的一个控件,它提供了一个可折叠的区域,用户可以点击标题栏来展开或折叠该区域。Expander 通常用于创建可伸缩的面板,以便在 UI 中节省空间。

以下是一些关键属性和使用示例:

主要属性:
  1. Header

    • 定义 Expander 的标题,用户可以点击这个标题来展开或折叠内容。
  2. IsExpanded

    • 控制 Expander 是否处于展开状态。
示例:
<Expander Header="Expandable Section" IsExpanded="True">
    <!-- Content goes here -->
    <StackPanel>
        <TextBlock>Content goes here</TextBlock>
        <Button Content="Click Me" />
    </StackPanel>
</Expander>

在这个例子中,ExpanderHeader 属性被设置为 "Expandable Section",并且 IsExpanded 属性被设置为 True,这表示在初始状态下该 Expander 是展开的。StackPanel 中包含了一些内容,这些内容将在用户展开 Expander 后显示。

用户可以通过点击 Expander 的标题来切换展开或折叠状态。如果你希望在初始化时是折叠状态,将 IsExpanded 设置为 False 即可。

6、自定义面板的创建

创建的一般流程:

创建一个自定义面板的一般流程涉及到继承 Panel 类,并重写其中的一些方法以实现你布局逻辑。以下是创建自定义面板的一般流程:

  1. 创建一个类并继承 Panel

    • 创建一个新的类,并让它继承自 Panel 类。
    public class CustomPanel : Panel
    {
        // Your code here
    }
    
  2. 重写 MeasureOverride 方法:

    • MeasureOverride 方法用于测量子元素的大小。在这个方法中,你需要为每个子元素调用 Measure 方法,以计算它们的所需大小。
    protected override Size MeasureOverride(Size availableSize)
    {
        // Measure child elements and calculate desired size
        foreach (UIElement child in InternalChildren)
        {
            child.Measure(availableSize);
        }
    
        // Return total desired size
        return new Size(/* calculated width */, /* calculated height */);
    }
    
  3. 重写 ArrangeOverride 方法:

    • ArrangeOverride 方法用于按照你的布局逻辑排列子元素。在这个方法中,你需要为每个子元素调用 Arrange 方法,以确定它们在面板中的位置。
    protected override Size ArrangeOverride(Size finalSize)
    {
        double x = 0;
        double y = 0;
    
        // Arrange child elements
        foreach (UIElement child in InternalChildren)
        {
            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
            x += child.DesiredSize.Width;
            y += child.DesiredSize.Height;
        }
    
        // Return final used space
        return finalSize;
    }
    
  4. 在 XAML 中使用你的自定义面板:

    • 在 XAML 中使用你的自定义面板,并在其中添加子元素。
    <Window x:Class="YourNamespace.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Custom Panel Example" Height="300" Width="400">
        <Grid>
            <local:CustomPanel>
                <!-- Your child elements go here -->
                <Button Content="Button 1" Width="100" Height="30"/>
                <Button Content="Button 2" Width="100" Height="30"/>
                <Button Content="Button 3" Width="100" Height="30"/>
            </local:CustomPanel>
        </Grid>
    </Window>
    
  5. 根据需求扩展和改进:

    • 根据你的具体需求,可能需要进一步扩展和改进你的自定义面板,例如添加自定义的排列逻辑、支持特定的布局方式等。

通过按照这个流程,你可以创建一个基本的自定义面板,并根据需要进一步定制和改进。

7、wpf资源

8、WIndow顶级容器

在 WPF(Windows Presentation Foundation)中,XAML(Extensible Application Markup Language)用于声明用户界面和应用程序的结构。<Window> 元素是一个顶级容器,用于定义 WPF 应用程序中的窗口。以下是 <Window> 元素的一般结构和属性:

<Window
    x:Class="Namespace.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Window Title" 
    Height="Auto" 
    Width="Auto">
    <!-- Window Content Goes Here -->
</Window>

1. x:Class: 指定窗口类的名称。这个类在代码文件中定义,并且用于实现窗口的后端逻辑。

2. xmlns: 定义默认 XML 命名空间,通常是 http://schemas.microsoft.com/winfx/2006/xaml/presentation,它包含了 WPF 中的基本元素。

3. xmlns:x: 引入 XAML 命名空间,它包含用于 XAML 扩展语法的标记。

4. xmlns:dmc:Ignorable="d": 用于与 Expression Blend(设计工具)相关的命名空间。mc:Ignorable="d" 表示在处理 XAML 时,可以忽略 d 命名空间的属性。

5. Title: 设置窗口的标题,将显示在窗口的标题栏上。

6. HeightWidth: 设置窗口的默认高度和宽度。Auto 表示自动适应内容的大小。

7. Window Content: <Window> 元素内部包含了窗口的内容。这里你可以添加按钮、文本框、菜单等用户界面元素,以及定义布局和样式。

总体而言,<Window> 元素是定义 WPF 应用程序窗口的入口点,其中包含了窗口的基本属性和结构。

9、wpf中的资源集合和层级

在 WPF(Windows Presentation Foundation)中,资源是一种用于在应用程序中共享和重用信息的机制。资源在 WPF 中有两种主要类型:逻辑资源和外部资源。这些资源可以在应用程序中的各个层级中定义和使用,包括全局、页面、窗口和控件级别。

1. 逻辑资源:

逻辑资源是在 XAML 中通过 Window.ResourcesFrameworkElement.Resources 元素定义的资源。这些资源在特定的范围内有效,通常包含在 WindowUserControl 或其他 FrameworkElement 的资源部分中。逻辑资源在定义它们的元素及其子元素中有效。

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 逻辑资源定义在这里 -->
        <SolidColorBrush x:Key="MyBrush" Color="Green"/>
        <Style TargetType="Button">
            <Setter Property="Foreground" Value="Red"/>
        </Style>
    </Window.Resources>

    <!-- 使用逻辑资源 -->
    <Grid>
        <Button Content="Click me" Background="{StaticResource MyBrush}"/>
    </Grid>
</Window>

2. 外部资源:

外部资源是定义在 App.xaml 文件中的资源,这些资源对整个应用程序都是全局可见的。它们可以在任何地方使用,不仅限于特定的 WindowUserControl

<Application x:Class="YourNamespace.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <!-- 外部资源定义在这里 -->
        <Color x:Key="AppColor">Blue</Color>
    </Application.Resources>
</Application>

MainWindow.xaml 中使用外部资源:

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock Text="Hello, WPF!" Foreground="{StaticResource AppColor}"/>
    </Grid>
</Window>

总体而言,资源在 WPF 中的层级关系是从局部到全局,分别是元素级别的逻辑资源、窗口级别的逻辑资源、应用程序级别的逻辑资源以及全局级别的外部资源。这使得在整个应用程序中能够方便地共享和重用资源。

在 WPF 中,资源分为静态资源和动态资源,它们在使用和应用时有一些区别。

1. 静态资源(StaticResource):

  • 特点:

    • 静态资源在 XAML 解析时立即解析和分配。
    • 一旦资源被分配,其值将保持不变。
    • 适用于那些在运行时不会改变的资源。
  • 使用方式:

    • 使用 {StaticResource} 标记引用静态资源。
<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 静态资源 -->
        <SolidColorBrush x:Key="MyBrush" Color="Green"/>
    </Window.Resources>

    <Grid>
        <!-- 使用静态资源 -->
        <Button Content="Click me" Background="{StaticResource MyBrush}"/>
    </Grid>
</Window>

2. 动态资源(DynamicResource):

  • 特点:

    • 动态资源在 XAML 解析时不会立即解析和分配。
    • 资源的值在运行时根据需要进行动态更新。
    • 适用于那些可能在运行时改变的资源。
  • 使用方式:

    • 使用 {DynamicResource} 标记引用动态资源。
<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 动态资源 -->
        <SolidColorBrush x:Key="MyDynamicBrush" Color="Blue"/>
    </Window.Resources>

    <Grid>
        <!-- 使用动态资源 -->
        <Button Content="Click me" Background="{DynamicResource MyDynamicBrush}"/>
    </Grid>
</Window>

注意事项:

  • 静态资源适用于那些在运行时不会改变的情况,因为它在 XAML 解析时就会被解析和分配。
  • 动态资源适用于可能在运行时改变的情况,因为它允许在运行时动态更新资源的值。
  • 在大多数情况下,使用静态资源是更好的选择,因为它会在应用程序启动时提供更好的性能。

根据具体的使用场景,选择静态资源或动态资源能够更好地满足应用程序的需求。

10、wpf中的样式

在 WPF(Windows Presentation Foundation)中,样式(Style)是一种定义控件外观和行为的机制。样式可用于统一整个应用程序或特定部分的外观,提高可维护性和一致性。下面详细介绍一下 WPF 中的样式:

1. 样式的定义:

在 XAML 中,你可以使用 <Style> 元素定义样式。样式可以包含各种视觉属性和触发器,以便在不同的状态下应用不同的样式。

<Window x:Class="YourNamespace.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <!-- 定义一个样式 -->
        <Style TargetType="Button">
            <Setter Property="Background" Value="LightBlue"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Margin" Value="5"/>
            <!-- 触发器 -->
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="LightSkyBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Grid>
        <!-- 使用样式 -->
        <Button Content="Click me"/>
    </Grid>
</Window>

2. 样式的应用:

样式可以应用于特定类型的控件,通过 TargetType 属性指定。上述例子中的样式被应用于 Button 控件。

3. 样式的继承:

你可以通过 BasedOn 属性实现样式的继承。这使得你可以基于现有样式创建新样式,而无需重复定义所有属性。

<Window.Resources>
    <!-- 基础样式 -->
    <Style x:Key="BaseButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="LightBlue"/>
        <!-- 其他属性... -->
    </Style>

    <!-- 派生样式 -->
    <Style x:Key="SpecialButtonStyle" TargetType="Button" BasedOn="{StaticResource BaseButtonStyle}">
        <Setter Property="FontSize" Value="18"/>
        <!-- 其他属性... -->
    </Style>
</Window.Resources>

4. 触发器:

样式中的触发器(Trigger)允许你根据控件的状态来改变样式。在上述例子中,IsMouseOver 触发器在鼠标悬停在按钮上时改变按钮的背景颜色。

在WPF中,有几种不同类型的触发器,包括简单触发器(Trigger)、数据触发器(DataTrigger)、事件触发器(EventTrigger)等。这些触发器用于在特定条件下执行相应的操作,例如修改控件的属性、执行动画等。

1. 简单触发器(Trigger):

简单触发器用于在特定属性达到指定值时触发操作。以下是一个简单触发器的例子,当ButtonIsMouseOver属性为True时,修改其背景颜色:

<Button Content="Hover me">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="LightBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>
2. 数据触发器(DataTrigger):

数据触发器用于在特定数据条件下触发操作。以下是一个数据触发器的例子,当Slider的值大于等于50时,修改TextBlock的颜色:

<Slider x:Name="slider" Minimum="0" Maximum="100"/>
<TextBlock Text="Value is less than 50">
    <TextBlock.Style>
        <Style TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Value, ElementName=slider}" Value="50">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>
3. 事件触发器(EventTrigger):

事件触发器用于在特定事件发生时触发操作。以下是一个事件触发器的例子,当Button被点击时,执行动画:

<Button Content="Click me">
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <!-- 在这里定义动画等操作 -->
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>

这些触发器使得在WPF应用程序中能够以响应用户交互或数据变化的方式来改变UI元素的外观和行为。您可以根据具体需求选择使用哪种触发器。

5. 动态资源:

可以使用动态资源(DynamicResource)来实现样式的动态更新。这允许在运行时改变样式的属性。

<Window.Resources>
    <Style x:Key="DynamicButtonStyle" TargetType="Button">
        <Setter Property="Background" Value="{DynamicResource MyDynamicBrush}"/>
    </Style>
</Window.Resources>

6. 样式在控件中的应用:

可以通过 Style 属性将样式应用于单个控件。

<Button Content="Click me" Style="{StaticResource BaseButtonStyle}"/>

11、wpf中的控件模板

在WPF中,控件模板(Control Template)是一种定义控件外观和行为的机制。它允许您自定义控件的外观,而无需修改控件的行为。控件模板通常包含在控件的样式中,并通过ControlTemplate元素定义。

以下是控件模板的关键概念和用法:

  1. 控件模板的定义: 控件模板定义了控件在视觉上的外观结构。它包含一组 XAML 元素,用于描述控件的布局、样式和可视元素。

    <ControlTemplate TargetType="Button">
        <!-- 控件的视觉结构定义在这里 -->
    </ControlTemplate>
    

    上面的示例定义了一个 Button 的控件模板。

  2. 样式与控件模板: 控件模板通常与样式(Style)一起使用,以便将其应用于控件。样式定义了控件的外观和行为,而控件模板定义了视觉结构。

    <Style TargetType="Button">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <!-- 控件的视觉结构定义在这里 -->
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    在样式中,Template 属性用于指定控件模板。

  3. 模板绑定: 控件模板中可以使用模板绑定(Template Binding)来绑定到控件的属性。这允许控件模板中的某些部分与控件实例的属性相关联。

    <ControlTemplate TargetType="Button">
        <Border Background="{TemplateBinding Background}">
            <ContentPresenter Content="{TemplateBinding Content}" />
        </Border>
    </ControlTemplate>
    

    在上述示例中,BackgroundContent 被使用了模板绑定。

  4. 部分控件模板: 控件模板可以包含控件的部分定义。例如,一个 Button 控件的模板可能包含按钮的外框、文本、图标等。

    <ControlTemplate TargetType="Button">
        <Border>
            <TextBlock Text="{TemplateBinding Content}" />
            <!-- 其他控件部分的定义 -->
        </Border>
    </ControlTemplate>
    

    这允许您更灵活地定制控件的不同部分。

  5. 触发器与状态: 控件模板中可以包含触发器(Trigger)和视觉状态(VisualState),以便在不同的条件下应用不同的样式或行为。

    <ControlTemplate TargetType="Button">
        <ControlTemplate.Triggers>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background" Value="DarkGray"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    

    在上述示例中,当按钮被按下时,IsPressed 触发器会修改按钮的背景颜色。

通过使用控件模板,您可以全面地自定义控件的外观,而无需修改其行为。这为创建具有独特外观的自定义控件提供了强大的工具。

1.TemplateBinding用法

TemplateBinding 是一种在控件模板中使用的绑定方式,它用于在模板内部绑定到控件的属性。它允许在控件模板中使用来自控件本身的属性值,而无需硬编码这些值。

用法示例:

假设有一个自定义按钮控件,其中包含一个Border和一个TextBlock,你想让这个按钮的TextBlockText属性与按钮自身的Content属性保持同步。这时就可以使用 TemplateBinding

<!-- 自定义按钮控件模板 -->
<ControlTemplate TargetType="Button">
    <Border>
        <TextBlock Text="{TemplateBinding Content}" />
    </Border>
</ControlTemplate>

在这个例子中,{TemplateBinding Content} 表示 TextBlockText 属性将绑定到按钮模板的 Content 属性。这意味着,无论你使用该按钮时设置了什么内容,TextBlock 的文本都会与按钮的内容保持一致。

特点和注意事项:
  1. 绑定到控件本身属性: TemplateBinding 允许在控件模板中轻松地将模板内的元素属性绑定到控件本身的属性,这样在使用自定义控件时,模板内的元素会根据控件的属性值进行更新。

  2. 限制: TemplateBinding 有一些限制。它只能用于模板内,且仅限于控件模板的定义中。不能用于通用的数据绑定上下文中。

  3. 简化模板: 使用 TemplateBinding 可以简化模板的编写,特别是当你希望在模板内部使用控件本身的属性时。

  4. 适用控件: 通常用于自定义控件的模板定义中。

<MyCustomButton Content="Click me" />

在上述例子中,MyCustomButton 使用了上述的控件模板,而由于使用了 TemplateBinding Content,按钮的文本将与按钮的 Content 属性一致。

2.ContentPresenter 用法

ContentPresenter 是一个用于显示控件内容的重要控件。它通常在控件模板中使用,负责呈现控件的内容部分。

在WPF中,许多控件(例如ButtonCheckBoxContentControl等)都具有Content属性,用于容纳其他控件或内容。ContentPresenter 就是用来在控件模板中展示这些内容的。

以下是 ContentPresenter 的主要作用和特点:

  1. 显示控件的内容: 当控件的内容(Content)发生变化时,ContentPresenter 负责显示这些内容。

  2. 控件模板中的占位符: 在控件模板中,ContentPresenter 通常充当一个占位符,用于插入控件的实际内容。

  3. 适应不同类型的内容: ContentPresenter 能够适应不同类型的内容,包括单个元素、多个元素、文本等。

  4. 内容的样式和模板: ContentPresenter 也可以包含在控件模板内的样式中,以定制内容的呈现方式。

以下是一个简单的例子,演示了 ContentPresenter 在控件模板中的使用:

<Style TargetType="Button">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Background="LightBlue">
                    <!-- ContentPresenter 用于显示按钮的内容 -->
                    <ContentPresenter/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

在上述例子中,ContentPresenter 被嵌套在一个 Border 元素内,用于显示按钮的内容。这使得按钮的内容可以在 LightBlue 背景的边界内呈现。

总的来说,ContentPresenter 是控件模板中用于呈现控件内容的关键元素,它使得内容的显示与控件的外观分离,提供了更大的灵活性。

12、数据模板

数据模板(DataTemplate)是 WPF 中用于定义数据对象在界面上的展示方式的一种机制。它通常用于 ItemsControl(例如 ListBox、ListView、DataGrid 等)中,用于定义每个数据项的外观。

基本结构:

一个简单的数据模板定义如下:

<DataTemplate DataType="{x:Type YourDataType}">
    <!-- 内容,用于呈现数据对象 -->
</DataTemplate>

其中:

  • DataType 属性指定了这个数据模板适用的数据类型。
  • 模板的内容定义了数据对象在界面上的呈现方式。

示例:

以下是一个更详细的例子,演示如何使用数据模板呈现一个包含姓名和年龄信息的 Person 类对象:

<!-- 定义Person类 -->
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

<!-- XAML中的数据模板 -->
<DataTemplate DataType="{x:Type local:Person}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
        <TextBlock Text="{Binding Age}"/>
    </StackPanel>
</DataTemplate>

在这个例子中:

  • Person 类有两个属性:NameAge
  • 数据模板使用 DataTemplate 标签,并通过 DataType 属性指定了这个模板适用于 Person 类型的数据。
  • DataTemplate 中,我们使用了一个 StackPanel 包含两个 TextBlock 控件,分别绑定到 NameAge 属性。

使用方式:

数据模板在使用时通常与ItemsControl搭配使用,例如:

<ListBox ItemsSource="{Binding PeopleList}" ItemTemplate="{StaticResource YourPersonDataTemplate}"/>

其中 PeopleList 是一个包含多个 Person 对象的集合,而 YourPersonDataTemplate 是前面定义的数据模板。这样,每个 Person 对象都会以定义的方式显示在 ListBox 中。

13、依赖属性

依赖属性(Dependency Property)是WPF中一种特殊类型的属性,它提供了一种机制,使属性能够自动支持数据绑定、样式、动画以及其他一些高级特性。依赖属性的特点是可以继承、支持动画、提供了值的有效性检查等。

以下是依赖属性的一些关键特点:

  1. 值继承: 依赖属性的值可以从其父元素继承,这意味着子元素可以继承父元素的属性值。

  2. 支持动画: 依赖属性支持动画,可以通过动画系统实现平滑的属性值变化效果。

  3. 数据绑定: 依赖属性可以通过数据绑定机制与其他属性、数据源等进行绑定,实现数据的双向同步。

  4. 默认值和有效性检查: 依赖属性可以具有默认值,并且可以通过提供属性元数据来定义有效性检查等规则。

  5. 通知机制: 当依赖属性的值发生变化时,可以触发通知机制,使其它相关的操作能够感知到这个变化。

以下是一个简单的依赖属性的定义示例:

public class CustomControl : Control
{
    // 定义依赖属性
    public static readonly DependencyProperty CustomTextProperty =
        DependencyProperty.Register("CustomText", typeof(string), typeof(CustomControl));

    // 封装依赖属性
    public string CustomText
    {
        get { return (string)GetValue(CustomTextProperty); }
        set { SetValue(CustomTextProperty, value); }
    }
}

在这个例子中:

  • CustomText 是一个依赖属性,它表示自定义控件的文本内容。
  • 通过 DependencyProperty.Register 方法注册了 CustomTextProperty,并指定了属性名称、属性类型以及拥有这个属性的类类型。

在XAML中,可以使用这个依赖属性如下:

<local:CustomControl CustomText="Hello, WPF!" />

FrameworkPropertyMetadata

FrameworkPropertyMetadata 是 WPF 中的一个类,用于定义依赖属性的元数据。依赖属性的元数据包含有关属性的信息,例如默认值、属性改变的回调函数等。通过 FrameworkPropertyMetadata,你可以对依赖属性的行为进行更详细和精细的控制。

以下是一些 FrameworkPropertyMetadata 的主要属性和用法:

  1. DefaultValue: 定义依赖属性的默认值。
new FrameworkPropertyMetadata("Default Text")
  1. BindsTwoWayByDefault: 定义属性是否默认支持双向数据绑定。
new FrameworkPropertyMetadata("Default Text", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
  1. PropertyChangedCallback: 在属性值发生变化时执行的回调方法。
new FrameworkPropertyMetadata("Default Text", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCustomTextChanged)
  1. CoerceValueCallback: 提供一个回调方法,用于在属性值更改时强制执行特定的值。
new FrameworkPropertyMetadata("Default Text", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCustomTextChanged, CoerceCustomText)

这是一个具体的例子:

public class CustomControl : Control
{
    public static readonly DependencyProperty CustomTextProperty =
        DependencyProperty.Register("CustomText", typeof(string), typeof(CustomControl),
            new FrameworkPropertyMetadata("Default Text", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCustomTextChanged, CoerceCustomText));

    public string CustomText
    {
        get { return (string)GetValue(CustomTextProperty); }
        set { SetValue(CustomTextProperty, value); }
    }

    private static void OnCustomTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // 在属性值变化时执行的逻辑
    }

    private static object CoerceCustomText(DependencyObject d, object baseValue)
    {
        // 强制执行属性值的逻辑
        return baseValue;
    }
}

在这个例子中,FrameworkPropertyMetadata 用于定义 CustomTextProperty 的一些元数据,包括默认值、支持双向数据绑定、属性值变化回调和强制执行回调。这样,就能够更加灵活地控制依赖属性的行为。

类型转换器

类型转换器在WPF中有着重要的作用,主要用于在XAML中处理不同类型之间的转换。以下是一些场景和作用:

  1. 数据绑定: 在数据绑定过程中,将不同类型的数据转换为目标属性的类型。例如,将字符串表示的数字转换为 int,或者将枚举值的字符串表示转换为实际的枚举类型。

    <TextBlock Text="{Binding Age, StringFormat=N0}" />
    <Button IsEnabled="{Binding IsButtonEnabled}" />
    
  2. 资源引用: 在XAML中引用颜色、图像等资源时,类型转换器用于将字符串表示的资源引用转换为实际的 BrushImageSource 对象。

    <Rectangle Fill="Red" />
    <Image Source="myImage.png" />
    
  3. 样式和模板: 在定义样式和模板时,可能需要将字符串表示的值转换为目标类型,以正确应用样式。

    <Style TargetType="Button">
        <Setter Property="Background" Value="Red" />
    </Style>
    
  4. ValueConverter: 当需要自定义的转换逻辑时,可以实现 IValueConverter 接口,创建自定义的类型转换器。这在处理复杂的数据绑定转换逻辑时非常有用。

    <TextBlock Text="{Binding Age, Converter={StaticResource AgeToCategoryConverter}}" />
    

总体而言,类型转换器在XAML中提供了一种通用的机制,用于简化不同类型之间的转换过程。这种机制在数据绑定、样式定义、资源引用等方面都能够提高代码的可读性和灵活性。

使用方法

在WPF中,类型转换通常发生在XAML中,其中你可以使用内置的类型转换器或自定义的转换器。以下是一些常见的方式:

使用内置的类型转换器

  1. StringFormat: 通过 StringFormat 可以将值格式化为特定的字符串。

    <TextBlock Text="{Binding SomeNumber, StringFormat=N2}" />
    

    上述代码将 SomeNumber 属性的值格式化为包含两位小数的数字字符串。

  2. 直接赋值: 对于一些简单的转换,WPF会自动调用内置的类型转换器。

    <Rectangle Width="100" Height="50" Fill="Red" />
    

    在这里,WidthHeight 属性期望 double 类型,而 Fill 属性期望 Brush 类型,WPF会自动进行类型转换。

使用自定义的类型转换器

如果需要处理更复杂的转换逻辑,你可以实现 IValueConverter 接口,创建自定义的转换器。例如,创建一个将数字转换为颜色的转换器:

public class NumberToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is double number)
        {
            // 通过某种逻辑将数字转换为颜色
            if (number > 0)
                return Brushes.Green;
            else
                return Brushes.Red;
        }

        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

然后在XAML中使用:

<Window.Resources>
    <local:NumberToColorConverter x:Key="NumberToColorConverter" />
</Window.Resources>

<TextBlock Text="{Binding SomeNumber, Converter={StaticResource NumberToColorConverter}}" />

上述代码将 SomeNumber 属性的值通过 NumberToColorConverter 转换为颜色。

总体而言,WPF提供了灵活的机制,可以通过内置的类型转换器或者自定义的转换器来处理不同类型之间的转换。选择合适的方式取决于具体的场景和需求。

依赖属性的继承

使用场景:

依赖属性的继承在以下场景中可能会很有用:

  1. 自定义控件: 当你创建自定义控件并希望子类能够继承一些特定的依赖属性时,依赖属性的继承就变得重要。子类可以从父类继承某些属性,而无需重新定义这些属性。

  2. 样式和模板: 在创建样式或模板时,你可能希望子元素能够继承父元素上定义的某些属性,这样就能够在整个元素树中保持一致的外观和行为。

  3. 基类中定义的属性: 当你在基类中定义了一些通用的属性,并希望这些属性能够被派生类所继承时,依赖属性的继承可以派上用场。

定义:

依赖属性的继承是通过 RegisterAttached 方法的 ownerType 参数来实现的。当你在子类中注册一个依赖属性时,可以通过将 ownerType 设置为子类的类型,以便让该属性在子类中可用。

public static readonly DependencyProperty MyPropertyInChildClassProperty =
    DependencyProperty.RegisterAttached("MyPropertyInChildClass", typeof(int), typeof(ChildClass), new PropertyMetadata(0));

实例:

考虑一个基类 BaseControl 和一个派生类 DerivedControl 的情况,它们都包含一个名为 CustomProperty 的依赖属性。在这种情况下,DerivedControl 将继承 CustomProperty

public class BaseControl : DependencyObject
{
    public static readonly DependencyProperty CustomPropertyProperty =
        DependencyProperty.Register("CustomProperty", typeof(string), typeof(BaseControl), new PropertyMetadata(""));
    
    public string CustomProperty
    {
        get { return (string)GetValue(CustomPropertyProperty); }
        set { SetValue(CustomPropertyProperty, value); }
    }
}

public class DerivedControl : BaseControl
{
    // DerivedControl 继承了 CustomProperty 依赖属性
}

在这个例子中,DerivedControl 类不需要重新定义 CustomProperty 依赖属性,因为它已经继承了自 BaseControl。在使用 DerivedControl 时,你可以直接使用 CustomProperty,并且可以访问基类中定义的属性。

附加依赖属性

使用场景:

附加依赖属性通常用于将属性附加到不同类型的元素上,而不仅仅是通过继承的方式。以下是一些使用场景:

  1. 附加行为: 附加依赖属性常用于向控件添加特定的行为。例如,可以使用附加依赖属性为某个元素添加点击事件的处理逻辑。

  2. 样式和模板: 附加依赖属性可以用于在样式或模板中附加额外的信息,而无需修改实际的元素类型。

  3. 布局: 在某些情况下,你可能需要为元素指定一些布局相关的属性,而这些属性不属于元素本身的标准属性,这时可以使用附加依赖属性。

定义:

定义附加依赖属性通常包括以下几个步骤:

  1. 使用 RegisterAttached 方法注册附加依赖属性。
  2. 提供 GetSet 方法,用于获取和设置附加属性的值。
public class MyAttachedProperties : DependencyObject
{
    public static readonly DependencyProperty MyProperty =
        DependencyProperty.RegisterAttached("MyProperty", typeof(string), typeof(MyAttachedProperties), new PropertyMetadata(""));

    public static string GetMyProperty(DependencyObject obj)
    {
        return (string)obj.GetValue(MyProperty);
    }

    public static void SetMyProperty(DependencyObject obj, string value)
    {
        obj.SetValue(MyProperty, value);
    }
}

实例:

使用附加依赖属性的示例:

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp"
        Title="MainWindow" Height="350" Width="525">
    
    <Grid local:MyAttachedProperties.MyProperty="This is an attached property value">
        <!-- Other UI elements in the Grid -->
    </Grid>
</Window>

14、数据源的几种绑定方式

当在 WPF 中进行数据绑定时,我们需要指定数据的来源,以下是四种常见的指定数据绑定源的方式:

  1. 指定 Source:

    • 使用 Source 属性直接指定数据绑定的源对象。这可以是 XAML 中定义的资源、类的实例等。
    <Window.Resources>
        <local:Person x:Key="MyPerson" Name="John" Age="25" />
    </Window.Resources>
    
    <TextBlock Text="{Binding Path=Name, Source={StaticResource MyPerson}}" />
    
  2. 指定 ElementName:

    • 通过 ElementName 属性指定数据绑定的目标元素的名称。数据绑定将使用该元素的数据上下文。
    <TextBox x:Name="myTextBox" Text="Hello, World!" />
    <TextBlock Text="{Binding Text, ElementName=myTextBox}" />
    
  3. 指定 DataContext:

    • 使用 DataContext 属性设置数据绑定的源。可以在控件上直接设置,或者通过容器元素继承给子元素。
    <Grid DataContext="{Binding ViewModel}">
        <TextBlock Text="{Binding Property}" />
    </Grid>
    
  4. 使用 Resources:

    • 将数据源定义为资源,并在需要进行数据绑定的控件中引用该资源。
    <Window.Resources>
        <local:MyDataSource x:Key="MyDataSource" />
    </Window.Resources>
    
    <TextBlock Text="{Binding Path=Property, Source={StaticResource MyDataSource}}" />
    
  5. 指定 RelativeSource:

    • 使用 RelativeSource 属性指定数据绑定的相对源,例如指定绑定到父元素、祖先元素等。
    <StackPanel>
        <TextBlock Text="{Binding Property}" />
        <TextBlock Text="{Binding DataContext.OtherProperty, RelativeSource={RelativeSource AncestorType=Window}}" />
    </StackPanel>
    

这些方式的选择取决于项目的结构和需求。在确定数据绑定源时,可以根据实际情况选择最合适的方式。

image-20240311224742539

15、生命周期事件

1、Application生命周期

WPF 应用程序的生命周期包括多个重要事件和阶段,其中一些最常见的是:

  1. StartUp: 当应用程序启动时,此事件会被触发。你可以在 App.xaml.cs 文件中重写 OnStartup 方法来处理启动时的逻辑。

  2. Exit: 当应用程序即将退出时,此事件会被触发。你可以在 App.xaml.cs 文件中重写 OnExit 方法来处理退出时的逻辑。

  3. SessionEnding: 当用户注销或关闭计算机时,此事件会被触发。你可以在 App.xaml.cs 文件中重写 OnSessionEnding 方法来处理这种情况下的逻辑。

  4. Activated: 当应用程序从非活动状态切换到活动状态时,此事件会被触发。例如,应用程序从最小化状态恢复到正常状态时会触发该事件。

  5. Deactivated: 当应用程序从活动状态切换到非活动状态时,此事件会被触发。例如,当用户将应用程序最小化或切换到其他应用程序时会触发该事件。

  6. DispatcherUnhandledException: 当应用程序中未处理的异常被派发器捕获时,此事件会被触发。你可以在 App.xaml.cs 文件中重写 OnDispatcherUnhandledException 方法来处理未处理的异常。

2、Window生命周期

Window 的生命周期事件包括:

  1. SourceInitialized: 当窗口的源初始化完成时触发。在此事件中,你可以执行与窗口源相关的操作,例如设置窗口的样式或者其他初始化操作。

  2. ContentRendered: 当窗口的内容渲染完成时触发。在此事件中,你可以执行与窗口内容渲染相关的操作,例如根据窗口内容的大小调整窗口的布局或者执行其他初始化操作。

  3. Activated: 当窗口从非活动状态转为活动状态时触发。在此事件中,你可以执行与窗口活动状态相关的操作,例如更新界面内容、处理用户输入或者执行其他需要在窗口活动时进行的任务。

  4. Deactivated: 当窗口从活动状态转为非活动状态时触发。在此事件中,你可以执行与窗口非活动状态相关的操作,例如暂停界面更新、保存用户数据或者执行其他需要在窗口非活动时进行的任务。

  5. Closing: 当窗口即将关闭时触发。在此事件中,你可以执行一些清理操作,例如释放资源、保存用户数据或者执行其他必要的关闭前操作。

  6. Closed: 当窗口已经完全关闭并且不再可见时触发。在此事件中,你可以执行一些最终的清理操作,例如释放资源或者执行其他与窗口关闭相关的任务。

这些生命周期事件提供了对窗口各个阶段的完整控制,使你能够在适当的时机执行必要的操作,从而实现更加灵活和可靠的窗口应用程序。

3、其他元素

除了 Application 和 Window,其他元素(如 UserControl、Page、ContentControl 等)也具有自己的生命周期事件,其中包括:

  1. Initialized: 当元素的初始化过程完成时触发。在此事件中,你可以执行与元素初始化相关的操作,例如设置默认值、注册事件处理程序或者执行其他初始化任务。

  2. Loaded: 当元素被加载到可视化树中并准备好进行布局和渲染时触发。在此事件中,你可以执行与元素加载后相关的操作,例如更新界面内容、注册事件处理程序或者执行其他需要在元素加载后进行的任务。

  3. Unloaded: 当元素从可视化树中移除时触发。在此事件中,你可以执行与元素卸载相关的操作,例如释放资源、取消事件注册或者执行其他需要在元素卸载时进行的任务。

这些生命周期事件使得你能够更好地控制元素的行为,从而实现更加灵活和可靠的界面交互和应用程序逻辑

窗口打开------>关闭触发顺序

MainWindow_Initialized

MainWindow_SourceInitialized

MainWindow_Activated

MainWindow_Loaded

MainWindow_ContentRendered

MainWindow_Closing

MainWindow_Deactivated

MainWindow_Closed

16、键盘事件

在 WPF 中,键盘事件可用于捕获用户在键盘上按下、输入和释放的动作。以下是 WPF 中常用的键盘事件:

  1. KeyDown: 当用户按下键盘上的任意键时触发。通常用于捕获键盘按键的按下动作,以执行相应的操作。

  2. TextInput: 当键盘输入字符时触发。与 KeyDown 不同,TextInput 事件提供了字符而不是按键的信息。因此,TextInput 事件更适合用于处理文本输入。

  3. KeyUp: 当用户释放键盘上的任意键时触发。通常用于捕获键盘按键的释放动作,以执行相应的操作。

  4. PreviewKeyDown: 与 KeyDown 类似,但 PreviewKeyDown 是隧道事件,会在元素本身接收到按键事件之前触发。通常用于在按键事件到达元素之前拦截并处理按键。

  5. PreviewTextInput: 与 TextInput 类似,但 PreviewTextInput 是隧道事件,会在元素本身接收到文本输入事件之前触发。通常用于在文本输入事件到达元素之前拦截并处理输入。

  6. PreviewKeyUp: 与 KeyUp 类似,但 PreviewKeyUp 是隧道事件,会在元素本身接收到释放按键事件之前触发。通常用于在释放按键事件到达元素之前拦截并处理释放动作。

通过处理这些键盘事件,你可以实现各种键盘交互功能,例如捕获特定键的按下、处理文本输入、拦截按键事件等。

17、WPF动画

1、动画的基本属性

在 WPF 中使用动画时,可以通过定义以下基本属性来控制动画的行为和效果:

  1. 目标属性(Target Property)

    • 这是您希望动画影响的属性,可以是 UI 元素的任何可动画属性,例如宽度、高度、位置、颜色、透明度等。

    示例:

    <DoubleAnimation Storyboard.TargetProperty="Width" ... />
    
  2. 目标对象(Target Object)

    • 这是包含目标属性的对象,通常是 UI 元素。

    示例:

    <DoubleAnimation Storyboard.TargetName="myRectangle" ... />
    
  3. 动画类型(Animation Type)

    • 您可以选择不同类型的动画,以适应您想要实现的效果。常见的动画类型包括 DoubleAnimationColorAnimationThicknessAnimation 等。

    示例:

    <DoubleAnimation ... />
    
  4. 动画持续时间(Duration)

    • 这是指动画应该持续的时间长度。

    示例:

    <DoubleAnimation Duration="0:0:2" ... />
    
  5. 动画重复行为(Repeat Behavior)

    • 您可以定义动画的重复行为,包括循环次数和方向。

    示例:

    <DoubleAnimation RepeatBehavior="3x" ... />
    
  6. 缓动函数(Easing Function)

    • 可选的缓动函数可用于定义动画的变化速度。常见的缓动函数包括线性(Linear)、二次(Quadratic)、三次(Cubic)、弹簧(Spring)等。

    示例:

    <DoubleAnimation>
        <DoubleAnimation.EasingFunction>
            <QuadraticEase EasingMode="EaseInOut" />
        </DoubleAnimation.EasingFunction>
    </DoubleAnimation>
    
  7. 自动逆转(Auto Reverse)

    • 指定动画是否应在完成后自动反转回其初始状态。

    示例:

    <DoubleAnimation AutoReverse="True" ... />
    
  8. 动画完成事件(Completed Event)

    • 提供一个事件,用于在动画完成时触发相应的操作。

    示例:

    <DoubleAnimation Completed="Animation_Completed" ... />
    
  9. 开始动画(Begin Animation)

    • 通过 StoryboardBegin() 方法来触发动画的开始。

    示例:

    myStoryboard.Begin();
    

通过组合和调整这些属性,您可以创建各种各样的动画效果,从简单的线性变化到复杂的缓动效果,以及适用于各种 UI 元素的动态交互。

2、动画的生命周期

在WPF中,动画的生命周期包括以下几个重要事件:

  1. Completed: 当动画完成时触发。可以在这个事件处理程序中执行一些后续操作,比如显示消息或者执行其他动作。

  2. CurrentGlobalSpeedInvalidated: 当动画的速度发生变化时触发。这可能是由于动画的加速或减速,或者是由于动画的暂停和恢复等操作。

  3. CurrentStateInvalidated: 当动画的状态发生变化时触发。动画的状态包括是否正在运行、是否暂停等。

  4. CurrentTimeInvalidated: 当动画的时间线发生变化时触发。这是基于动画的时间轴,通常以每秒60帧的速度进行更新。

  5. RemoveRequested: 当动画正被移除时触发。这可以是由于动画完成、手动停止动画或者移除动画对象等操作。

下面是一个简单的示例代码,演示了如何使用这些事件来管理动画的生命周期:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace AnimationLifecycleExample
{
    public partial class MainWindow : Window
    {
        private DoubleAnimation animation;

        public MainWindow()
        {
            InitializeComponent();
            InitializeAnimation();
        }

        private void InitializeAnimation()
        {
            animation = new DoubleAnimation();
            animation.From = 100;
            animation.To = 200;
            animation.Duration = TimeSpan.FromSeconds(2);

            // 添加事件处理程序
            animation.Completed += AnimationCompleted;
            animation.CurrentGlobalSpeedInvalidated += AnimationSpeedChanged;
            animation.CurrentStateInvalidated += AnimationStateChanged;
            animation.CurrentTimeInvalidated += AnimationTimeChanged;
            animation.RemoveRequested += AnimationRemoved;
        }

        private void AnimationCompleted(object sender, EventArgs e)
        {
            MessageBox.Show("Animation completed!");
        }

        private void AnimationSpeedChanged(object sender, EventArgs e)
        {
            // 处理动画速度变化的逻辑
        }

        private void AnimationStateChanged(object sender, EventArgs e)
        {
            // 处理动画状态变化的逻辑
        }

        private void AnimationTimeChanged(object sender, EventArgs e)
        {
            // 处理动画时间线变化的逻辑
        }

        private void AnimationRemoved(object sender, EventArgs e)
        {
            // 处理动画被移除的逻辑
        }

        private void StartAnimationButton_Click(object sender, RoutedEventArgs e)
        {
            rectangle.BeginAnimation(Rectangle.WidthProperty, animation);
        }

        private void StopAnimationButton_Click(object sender, RoutedEventArgs e)
        {
            rectangle.BeginAnimation(Rectangle.WidthProperty, null);
            rectangle.Width = 100; // 将目标属性值重置为动画开始前的值
        }
    }
}

使用了各个事件处理程序来管理动画的生命周期。例如,在 AnimationCompleted 方法中,我们在动画完成时显示一个消息框。

3、故事板

故事板(Storyboard)是 WPF 中用于定义和管理动画的重要概念之一。它允许您在 XAML 或代码中定义一个或多个动画,并指定它们的目标对象、属性以及动画效果。故事板通常用于创建视觉上的效果,如淡入淡出、移动、缩放等。

下面是故事板的一些关键特点和用法:

  1. 定义动画: 您可以在故事板中定义一个或多个动画,例如双精度动画(DoubleAnimation)、颜色动画(ColorAnimation)等,以及它们的关键帧动画(KeyFrameAnimation)。

  2. 指定目标对象和属性: 您可以将动画应用于特定的目标对象和属性。这可以通过在动画标记中使用 Storyboard.TargetNameStoryboard.TargetProperty 属性来实现。

  3. 控制动画的开始、暂停和停止: 您可以通过调用故事板的 Begin()Pause()Resume()Stop() 方法来控制动画的播放状态。

  4. 重复和自动反转: 您可以通过设置 Storyboard.RepeatBehaviorStoryboard.AutoReverse 属性来控制动画的重复次数和是否自动反转播放。

  5. 事件触发: 故事板支持事件触发,您可以在动画完成时触发自定义的事件或执行其他操作。

  6. 资源共享: 故事板可以作为资源在整个应用程序中共享和重复使用,从而提高代码的复用性和可维护性。

通过使用故事板,您可以轻松地创建各种视觉效果和动画,使您的 WPF 应用程序更加生动和吸引人。

重叠动画

重叠动画是指在同一个元素上同时执行多个动画,这些动画可能影响元素的不同属性或者方向。下面是一个重叠动画的示例代码,演示了如何在一个元素上同时执行多个动画:

<Window x:Class="OverlapAnimation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Storyboard x:Key="ButtonAnimation1">
            <DoubleAnimation Storyboard.TargetName="myButton" Storyboard.TargetProperty="Width"
                             From="100" To="200" Duration="0:0:2" />
        </Storyboard>
        
        <Storyboard x:Key="ButtonAnimation2">
            <DoubleAnimation Storyboard.TargetName="myButton" Storyboard.TargetProperty="Height"
                             From="50" To="100" Duration="0:0:2" />
        </Storyboard>
    </Window.Resources>
    <Grid>
        <Button x:Name="myButton" Content="Click me" Click="Button_Click" Width="100" Height="50"/>
    </Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Media.Animation;

namespace OverlapAnimation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 获取故事板
            Storyboard storyboard1 = (Storyboard)FindResource("ButtonAnimation1");
            Storyboard storyboard2 = (Storyboard)FindResource("ButtonAnimation2");
            
            // 开始动画
            storyboard1.Begin();
            storyboard2.Begin();
        }
    }
}

在这个示例中,当按钮被点击时,会触发 Button_Click 事件处理程序,在该处理程序中获取名为 ButtonAnimation1ButtonAnimation2 的两个故事板,并调用 Begin() 方法开始执行两个动画效果。这样,按钮的宽度和高度会同时发生变化。

同步的动画

同步的动画是指多个动画在同一时间内开始执行,或者受到相同的时间线控制,从而呈现出同步的效果。下面是一个示例代码,演示了如何创建同步的动画效果:

<Window x:Class="SyncAnimation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Storyboard x:Key="ButtonAnimation">
            <DoubleAnimation Storyboard.TargetName="myButton1" Storyboard.TargetProperty="Width"
                             From="100" To="200" Duration="0:0:2" />
            <DoubleAnimation Storyboard.TargetName="myButton2" Storyboard.TargetProperty="Width"
                             From="100" To="200" Duration="0:0:2" />
        </Storyboard>
    </Window.Resources>
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button x:Name="myButton1" Content="Button 1" Click="Button_Click"/>
            <Button x:Name="myButton2" Content="Button 2" Click="Button_Click"/>
        </StackPanel>
    </Grid>
</Window>
using System;
using System.Windows;
using System.Windows.Media.Animation;

namespace SyncAnimation
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // 获取故事板
            Storyboard storyboard = (Storyboard)FindResource("ButtonAnimation");

            // 开始动画
            storyboard.Begin();
        }
    }
}

在这个示例中,当任何一个按钮被点击时,都会触发 Button_Click 事件处理程序。在处理程序中,我们获取了名为 ButtonAnimation 的故事板,并调用 Begin() 方法开始执行动画。由于故事板中包含了两个按钮的动画定义,因此两个按钮的宽度会在同一时间内从 100 变化到 200,呈现出同步的动画效果。

3、简单线性动画之缓动动画

缓动动画(Easing Animation)是一种常用的动画技术,它可以让动画在开始和结束时速度变化,从而让动画更加流畅和自然。WPF中提供了几种预定义的缓动函数,如Linear、Quadratic、Cubic等,也可以自定义缓动函数。

下面是一个简单的线性动画的示例,其中包含了缓动动画效果:

<Window x:Class="EasingAnimation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <Storyboard x:Key="ButtonAnimation">
            <DoubleAnimation Storyboard.TargetName="myButton" Storyboard.TargetProperty="Width"
                             From="100" To="200" Duration="0:0:2" 
                             EasingFunction="{StaticResource QuadraticEase}"/>
        </Storyboard>
        
        <QuadraticEase x:Key="QuadraticEase" EasingMode="EaseInOut"/>
    </Window.Resources>
    <Grid>
        <Button x:Name="myButton" Content="Animate" Click="Button_Click"/>
    </Grid>
</Window>

在这个示例中,我们定义了一个线性动画,将按钮的宽度从100变化到200,持续2秒钟。动画的缓动函数通过 EasingFunction 属性指定为 QuadraticEase,即二次缓动函数。该缓动函数具有 EaseInOut 模式,表示动画在开始和结束时速度变化较慢,中间时速度变化较快,呈现出一种平滑的变化效果。

在代码中,我们还需要为 QuadraticEase 定义一个键值,以便在动画中引用。

QuadraticEase 是一种缓动函数,用于在动画过程中调整动画值的变化速度,以产生更加自然的动画效果。在这个例子中,QuadraticEase 被定义为一个资源,因此可以在其他地方引用它。

EasingModeQuadraticEase 的属性,用于指定缓动函数的模式。它有以下几种取值:

  • EaseIn:动画开始时加速,末尾时减速。
  • EaseOut:动画开始时减速,末尾时加速。
  • EaseInOut:动画开始和末尾时都有缓慢的加速和减速效果。

在这个例子中,EasingMode 被设置为 EaseInOut,表示缓动函数会在动画的起始和结束阶段都有加速和减速的效果,以产生更加平滑的动画过渡。

下面是一些不同类型的缓动函数的示例:

  1. LinearEase(线性缓动函数)
<LinearEase x:Key="LinearEase"/>
  1. QuadraticEase(二次缓动函数)
<QuadraticEase x:Key="QuadraticEase" EasingMode="EaseInOut"/>
  1. CubicEase(三次缓动函数)
<CubicEase x:Key="CubicEase" EasingMode="EaseInOut"/>
  1. QuarticEase(四次缓动函数)
<QuarticEase x:Key="QuarticEase" EasingMode="EaseInOut"/>
  1. QuinticEase(五次缓动函数)
<QuinticEase x:Key="QuinticEase" EasingMode="EaseInOut"/>

这些缓动函数都可以通过指定 EasingMode 属性来控制动画的加速和减速效果。

4、关键帧动画

关键帧动画(KeyFrame Animation)是一种在动画序列中指定关键帧的技术,其中每个关键帧都定义了一个动画属性的特定值。WPF 中提供了多种类型的关键帧,包括 LinearDoubleKeyFrameDiscreteDoubleKeyFrameSplineDoubleKeyFrameEasingDoubleKeyFrame

  1. LinearDoubleKeyFrame(线性关键帧)
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width">
    <LinearDoubleKeyFrame Value="100" KeyTime="0:0:0"/>
    <LinearDoubleKeyFrame Value="200" KeyTime="0:0:2"/>
</DoubleAnimationUsingKeyFrames>

线性关键帧产生匀速的动画效果,从一个值到另一个值的变化是均匀的。

  1. DiscreteDoubleKeyFrame(离散关键帧)
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width">
    <DiscreteDoubleKeyFrame Value="100" KeyTime="0:0:0"/>
    <DiscreteDoubleKeyFrame Value="200" KeyTime="0:0:2"/>
</DoubleAnimationUsingKeyFrames>

离散关键帧产生离散的动画效果,从一个值突然跳转到另一个值,没有过渡效果。

  1. SplineDoubleKeyFrame(样条关键帧)
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width">
    <SplineDoubleKeyFrame KeyTime="0:0:0" Value="100" KeySpline="0.6,0.0 0.9,0.4"/>
    <SplineDoubleKeyFrame KeyTime="0:0:2" Value="200" KeySpline="0.0,0.0 0.2,1.0"/>
</DoubleAnimationUsingKeyFrames>

样条关键帧可以使用自定义的贝塞尔曲线定义动画的变化速度。

  1. EasingDoubleKeyFrame(缓动关键帧)
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Width">
    <EasingDoubleKeyFrame KeyTime="0:0:0" Value="100" EasingFunction="{StaticResource QuadraticEase}"/>
    <EasingDoubleKeyFrame KeyTime="0:0:2" Value="200" EasingFunction="{StaticResource QuadraticEase}"/>
</DoubleAnimationUsingKeyFrames>

缓动关键帧使用指定的缓动函数来定义动画的变化速度。

版权协议:MIT返回列表