WPF基础内容记录

发布于:2025-07-25 ⋅ 阅读:(13) ⋅ 点赞:(0)

目录

简介

一、样式基础

二、控件模板

三、数据模板

四、WPF绑定相关

1.绑定

2. 命令(ICommand)

3. 通知更改


简介

Windows Presentation Foundation (WPF) 是 Microsoft 提供的一个图形子系统,用于开发具有丰富用户界面的桌面应用程序。它通过提供强大的功能和灵活的设计,成为了开发 Windows 应用程序的首选框架之一。WPF 支持高度可定制化的 UI 设计,功能包括但不限于数据绑定、样式、控件模板、命令以及通知更改等,这些都帮助开发者轻松实现灵活而美观的用户界面。

在这篇文章中,我们将深入探讨一些 WPF 的基础概念,帮助初学者了解如何使用这些功能来构建应用程序。我们将涵盖以下几个重要的方面:

  1. 基础样式 (Styles):如何使用样式统一控件的外观。
  2. 控件模板 (Control Templates):如何自定义控件的结构和外观。
  3. 数据模板 (Data Templates):如何控制数据的展示方式。
  4. 控件绑定 (Data Binding):如何绑定控件的属性与数据源,保持 UI 与数据的同步。
  5. 命令 (Commands):如何使用命令模式来处理用户的操作。
  6. 通知更改 (INotifyPropertyChanged):如何通知 UI 属性发生了变化,进行实时更新。

一、样式基础

目的:给某些具有相同样式的控件,设置一个可以统一使用的样式。

1. 一个WPF的页面在普通设计实现的过程是:

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <StackPanel>
            <Button Content="button1"
                    FontSize="18"
                    Foreground="White"
                    Background="Blue"></Button>
            <Button Content="button2"
                    FontSize="18"
                    Foreground="White"
                    Background="Blue"></Button>
            <Button Content="button3"
                    FontSize="18"
                    Foreground="White"
                    Background="Blue"></Button>
        </StackPanel>
    </Grid>
</Window>

2. 这三个按钮除了内容不同,其他属性都是相同的,那么就可以设计一个统一的样式,使得这三个按钮只需要应用同一个样式,而不需要针对于单个按钮进行设计。

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>
        <Style TargetType="Button">
            <Setter Property="FontSize"
                    Value="18" />
            <Setter Property="Foreground"
                    Value="White" />
            <Setter Property="Background"
                    Value="Blue" />
            <Setter Property="Content"
                    Value="Button1" />
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button></Button>
            <Button></Button>
            <Button></Button>
        </StackPanel>
    </Grid>
</Window>

3. 可以看到,在Button的属性列表中,并没有设计样式,但是页面上,样式已经显示出来了。因为没有给这个样式设置固定的名称,默认作用域是全局的。

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>
        <Style x:Key="ButtonStyle" TargetType="Button">
            <Setter Property="FontSize"
                    Value="18" />
            <Setter Property="Foreground"
                    Value="White" />
            <Setter Property="Background"
                    Value="Blue" />
            <Setter Property="Content"
                    Value="Button1" />
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Style="{StaticResource ButtonStyle}"></Button>
            <Button Style="{StaticResource ButtonStyle}"></Button>
            <Button Style="{StaticResource ButtonStyle}"></Button>
        </StackPanel>
    </Grid>
</Window>

4. 同样,样式集也可以有继承关系,BaseON。

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>
        <Style x:Key="BaseButtonStyle"
               TargetType="Button">
            <Setter Property="FontSize"
                    Value="18" />
            <Setter Property="Foreground"
                    Value="White" />
            <Setter Property="Background"
                    Value="Blue" />
        </Style>
        <Style x:Key="ButtonStyle"
               TargetType="Button"
               BasedOn="{StaticResource BaseButtonStyle}">
            <Setter Property="Content"
                    Value="Button1" />
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Content="button1"
                    Style="{StaticResource ButtonStyle}"></Button>
            <Button Content="button2"
                    Style="{StaticResource ButtonStyle}"></Button>
            <Button Content="button3"
                    Style="{StaticResource ButtonStyle}"></Button>
        </StackPanel>
    </Grid>
</Window>

元素的属性值优先级是最高的,无论在样式表中如何设计,在元素的属性中设置值之后,元素的属性值优先显示.

二、控件模板

文本大纲==》选择Button并右键==》编辑模板==》编辑副本==》确定

Button中能显示很多内容的主要原因是有ContentPresenter

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Window.Resources>
        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
        <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
        <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
        <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
        <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
        <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
        <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
        <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
        <Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Padding" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Button}">
                        <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                            <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsDefaulted" Value="true">
                                <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="true">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
                                <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Style="{DynamicResource ButtonStyle1}">
                <StackPanel Orientation="Horizontal">
                    <Button Content="1"></Button>
                    <Button Content="2"></Button>
                    <Button Content="3"></Button>
                    <Button Content="4"></Button>
                </StackPanel>
            </Button>
        </StackPanel>
    </Grid>
</Window>

三、数据模板

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <DataGrid Name="list"
                  AutoGenerateColumns="False"
                  CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Code}"
                                    Header="Code" />
                <DataGridTextColumn Binding="{Binding Name}"
                                    Header="Name" />

                <DataGridTemplateColumn Header="操作">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <!--<StackPanel Orientation="Horizontal">
                                <Button Content="删除" />
                                <Button Content="复制" />
                                <Button Content="保存" />
                            </StackPanel>-->
                            <StackPanel Orientation="Horizontal">
                                <Border Width="10"
                                        Height="10"
                                        Background="{Binding Code}" />
                                <TextBlock Margin="10,0"
                                           Text="{Binding Name}" />
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
using System.Windows;

namespace Style_base
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            List<Color> test = new List<Color>();
            test.Add(new Color() { Code = "#FFB6C1", Name = "浅粉红" });
            test.Add(new Color() { Code = "#FFC0CB", Name = "粉红" });
            test.Add(new Color() { Code = "#DC143C", Name = "深红(猩红)" });
            test.Add(new Color() { Code = "#FFF0F5", Name = "淡紫红" });
            list.ItemsSource = test;
        }
    }
    public class Color
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }
}

四、WPF绑定相关

1.绑定

绑定控件上的值以及绑定属性值.

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow"
        Height="450"
        Width="800">
    <Grid>
        <StackPanel>
            <Slider Name="slider"
                    Margin="5" />
            <TextBox Text="{Binding ElementName=slider,Path=Value,Mode=Default}"
                     Margin="5" />
            <TextBox Text="{Binding ElementName=slider,Path=Value,Mode=OneTime}"
                     Margin="5" />
            <TextBox Text="{Binding ElementName=slider,Path=Value,Mode=OneWay}"
                     Margin="5" />
            <TextBox Text="{Binding ElementName=slider,Path=Value,Mode=OneWayToSource}"
                     Margin="5" />
            <TextBox Text="{Binding ElementName=slider,Path=Value,Mode=TwoWay}"
                     Margin="5" />
            <TextBox Text="{Binding Name}"
                     Height="30"
                     Margin="5" />
        </StackPanel>
    </Grid>
</Window>
using System.Reflection.Metadata.Ecma335;
using System.Windows;

namespace Style_base
{
    public partial class MainWindow : Window
    {        
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new Test() { Name = "张三" };
        }
    }
    public class Test
    {
        public string Name { get; set; }
    }
}

2. 命令(ICommand)

<Window x:Class="Style_base.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"
        xmlns:local="clr-namespace:Style_base"
        mc:Ignorable="d"
        Title="MainWindow" 
        Height="450"
        Width="800">
    <Grid>
        <Button Content="Click_Me" Height="46" Width="120" Command="{Binding ShowCommand}" />
    </Grid>
</Window>
using System.Reflection.Metadata.Ecma335;
using System.Windows;

namespace Style_base
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
        }
    }
}

 继承ICommand类,实现自己的Command类。

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Style_base
{
    public class MyCommand : ICommand
    {
        Action executeAction;
        public MyCommand(Action action)
        {
            executeAction = action;
        }
        public event EventHandler? CanExecuteChanged;

        public bool CanExecute(object? parameter)
        {
            return true;
        }

        public void Execute(object? parameter)
        {
            executeAction();
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Style_base
{
    public class MainViewModel
    {
        public MainViewModel()
        {
            ShowCommand = new MyCommand(Show);
        }
        public MyCommand ShowCommand { get; set; }
        public void Show()
        {
            MessageBox.Show("点击按钮命令执行成功");
        }
    }
}

3. 通知更改

在2的MainViewModel中简单修改

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Style_base
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            Name = "Hello WPF!";
            ShowCommand = new MyCommand(Show);
        }
        public MyCommand ShowCommand { get; set; }
        private string name;
        public string Name 
        {
            get { return name; } 
            set { name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } 
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public void Show()
        {
            Name = "点击按钮命令执行成功";
            MessageBox.Show(Name);
        }
    }
}

即可实现

封装成方法

新建一个基类View ModelBase

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Style_base
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在MainViewModel中使用

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Style_base
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Name = "Hello WPF!";
            ShowCommand = new MyCommand(Show);
        }
        public MyCommand ShowCommand { get; set; }
        private string name;
        public string Name 
        {
            get { return name; } 
            set { name = value; OnPropertyChanged("Name"); } 
        }
        
        public void Show()
        {
            Name = "点击按钮命令执行成功";
            MessageBox.Show(Name);
        }
    }
}

让方法自动识别是哪个属性发生了改变

对ViewModelBase进行改变

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Style_base
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
        public void OnPropertyChanged([CallerMemberName]string propertyName ="")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

在MainViewModel中使用

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Style_base
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            Name = "Hello WPF!";
            ShowCommand = new MyCommand(Show);
        }
        public MyCommand ShowCommand { get; set; }
        private string name;
        public string Name 
        {
            get { return name; } 
            set { name = value; OnPropertyChanged(); } 
        }
        
        public void Show()
        {
            Name = "点击按钮命令执行成功";
            MessageBox.Show(Name);
        }
    }
}


网站公告

今日签到

点亮在社区的每一天
去签到