WPF基础内容记录
目录
简介
一、样式基础
二、控件模板
三、数据模板
四、WPF绑定相关
1.绑定
2. 命令(ICommand)
3. 通知更改
简介
Windows Presentation Foundation (WPF) 是 Microsoft 提供的一个图形子系统,用于开发具有丰富用户界面的桌面应用程序。它通过提供强大的功能和灵活的设计,成为了开发 Windows 应用程序的首选框架之一。WPF 支持高度可定制化的 UI 设计,功能包括但不限于数据绑定、样式、控件模板、命令以及通知更改等,这些都帮助开发者轻松实现灵活而美观的用户界面。
在这篇文章中,我们将深入探讨一些 WPF 的基础概念,帮助初学者了解如何使用这些功能来构建应用程序。我们将涵盖以下几个重要的方面:
- 基础样式 (Styles):如何使用样式统一控件的外观。
- 控件模板 (Control Templates):如何自定义控件的结构和外观。
- 数据模板 (Data Templates):如何控制数据的展示方式。
- 控件绑定 (Data Binding):如何绑定控件的属性与数据源,保持 UI 与数据的同步。
- 命令 (Commands):如何使用命令模式来处理用户的操作。
- 通知更改 (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);}}
}