WPF 核心:触发器、模板与数据绑定

mindtian 发布于 2025-07-08 262 次阅读


AI 摘要

WPF布局看似简单,却暗藏玄机——为何一个按钮能随鼠标悬停瞬间变色?

一、 布局基础

1. StackPanccel:

水平或垂直排列元素,Orientation属性分别:Horizontal(水平) / Vertical(垂直)

2. WrapPancel:

水平排列元素,针对剩余空间不足会进行换行或换列进行排列

3. DockPancel:

根据容器的边界,元素进行Dock.Top、Left、Right、Bottom设置

4. Grid:

类似table表格,可灵活设置并列放置控件元素

5. DataGrid:

<DataGrid ItemsSource="{Binding Users}" AutoGenerateColumns="False" Margin="10">
    <DataGrid.Columns>
        <!-- 第 1 列: ID (文本列) -->
        <DataGridTextColumn Header="ID" Binding="{Binding Id}" IsReadOnly="True" />

        <!-- 第 2 列: 姓名 (文本列) -->
        <DataGridTextColumn Header="姓名" Binding="{Binding Name}" />

        <!-- 第 3 列: 邮箱 (超链接列) -->
        <DataGridHyperlinkColumn Header="邮箱" Binding="{Binding Email}" />

        <!-- 第 4 列: 是否为会员 (复选框列) -->
        <DataGridCheckBoxColumn Header="会员" Binding="{Binding IsMember}" />
        
        <!-- 第 5 列: 操作 (万能的模板列) -->
        <DataGridTemplateColumn Header="操作">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="删除" Click="DeleteButton_Click" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

二、 样式

示例

<Window.Resources>
    <Style x:Key="defaultStyle" TargetType="Button">
        <Setter Property="FontSize" Value="20" />
        <Setter Property="Background" Value="Black" />
        <Setter Property="Foreground" Value="Red" />
        <Setter Property="Width" Value="100" />
        <Setter Property="Height" Value="200"/>
    </Style>
</Window.Resources>
<Grid>
    <Button Style="{StaticResource defaultStyle}" Content="hello"/>
</Grid>

使用:Style="{DynamicResource defaultStyle}"

样式之间可以通过BasedOn继承

三、 触发器

1. Trigger:监测依赖属性的变化,触发器生效

<Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Background" Value="Green" />
        <Setter Property="Foreground" Value="Red" />
        <Setter Property="BorderBrush" Value="Cyan"/>
    </Trigger>
    <Trigger Property="IsMouseOver" Value="False">
        <Setter Property="Background" Value="Red" />
        <Setter Property="Foreground" Value="Green" />
        <Setter Property="BorderBrush" Value="Blue"/>
    </Trigger>
</Style.Triggers>

2. Multrigger:通过多个条件的设置,达到满足条件,触发器生效

<Style.Triggers>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="True"/>
            <Condition Property="IsFocused" Value="True"/>
        </MultiTrigger.Conditions>
        <MultiTrigger.Setters>
            <Setter Property="Foreground" Value="Red" />
        </MultiTrigger.Setters>
    </MultiTrigger>
</Style.Triggers>

3. DataTrigger:通过数据的变化,触发器生效

4. MultiDataTrigger:多个数据条件的触发器

5. EventTrigger:事件触发器,触发了某类事件时,触发器生效

四、 控件模板

1. 直接设置在<Window.Resources>下

<ControlTemplate x:Key="defaultButtonTemplate" TargetType="Button">
    <Border Background="Red" CornerRadius="5">
        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                          VerticalAlignment="{TemplateBinding VerticalAlignment}"/>
    </Border>
</ControlTemplate>

使用:Template="{StaticResource defaultButtonTemplate}"

2. 通过属性设置

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="Button">
            <!-- 关键点:将模板内元素的属性与控件自身属性绑定起来 -->
            <Grid Background="{TemplateBinding Background}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Grid>
            <ControlTemplate.Triggers>
                <!-- 当鼠标悬停时,修改Button的Background属性,Grid会因为TemplateBinding而响应 -->
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Red"/>
                </Trigger>
                <!-- 按下时变色 -->
                <Trigger Property="IsPressed" Value="True">
                    <Setter Property="Background" Value="DarkRed"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Setter.Value>
</Setter>

五、 数据模板

1. 示例一

<DataTemplate x:Key="comTemplate">
    <StackPanel>
        <Border Width="10" Height="10" Background="{Binding Code}"/>
        <TextBlock Text="{Binding Code}" Margin="5, 0"/>
    </StackPanel>
</DataTemplate>

使用:ItemTemplate="{StaticResource comTemplate}"

2. 示例二

<DataTemplate DataType="{x:Type local:Student}">
    <!-- 2. 设计每条数据项的外观 -->
    <!-- 我们用一个带圆角的 Border 把每条信息包起来,让它更美观 -->
    <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Padding="10" Margin="5" Background="#F0F8FF">
        <!-- Grid 是一个强大的布局工具,我们用它来对齐文本 -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <!-- 第一列宽度自适应内容-->
                <ColumnDefinition Width="*"/>
                <!-- 第二列占满剩余空间 -->
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <!-- 三行高度都自适应 -->
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <!-- 标签文本 -->
            <TextBlock Grid.Row="0" Grid.Column="0" Text="姓名:" FontWeight="Bold" />
            <TextBlock Grid.Row="1" Grid.Column="0" Text="年龄:" FontWeight="Bold" />
            <TextBlock Grid.Row="2" Grid.Column="0" Text="性别:" FontWeight="Bold" />
            <!-- 绑定到Student对象的属性 -->
            <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
            <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Age}" />
            <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Sex}" />
        </Grid>
    </Border>
</DataTemplate>

设置this.DataContext = this

引入:Style="{StaticResource MyButtonStyle}"

六、 数据绑定

1. 绑定到元素上

<Slider Width="200" Name="sd"/>
<TextBox Name="txt" Text="{Binding ElementName=sd,Path=Value,Mode=OneTime}"/>

2. 绑定到非元素上

<TextBox Text="{Binding Source={StaticResource tx}, Path=Text}" />

3.

public partial class Window1: INotifyPropertyChanged
{
    public Window1()
    {
        Name = "Hello";
        Task.Run(async () =>
        {
            await Task.Delay(3000);
            Name = "No";
        });
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private string name;
    public string Name
    {
        get { return name; }
        set 
        { 
            name = value;
            OnPropertyChanged("Name");
        }
    }
    protected void OnPropertyChanged(string properName)
    {
        if(PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(properName)); }
    }
}