WPF 入门笔记 - 01 - 入门基础以及常用布局
本篇为学习博客园大佬圣殿骑士的《WPF基础到企业应用系列》以及部分DotNet菜园的《WPF入门教程系列》所作笔记,对应圣殿骑士《WPF基础到企业应用系列》第 1 - 6 章之间内容,包括 WPF 项目结构、程序的启动和关闭、程序的生命周期、继承关系以及常见的布局控件及其应用。先上链接:
WPF核心程序集
创建一个WPF后,项目引用中会默认引用PresentationCore、PresentationFramework、WindowsBase三个WPF核心程序集:

PresentationCore:定义了WPF中基本的UI元素和呈现系统的核心功能,包括布局、渲染、输入、事件等。它包含了许多基本的类和接口,如UIElement、Visual、DispatcherObject、Freezable等。PresentationFramework:这是WPF中的应用程序框架,提供了一些高级UI控件和应用程序级别的功能,如Windows、Pages、Navigation、Application、Window等。此外,PresentationFramework还定义了WPF的命名空间、样式和主题等。WindowsBase:它包含了一些基础类和接口,用于支持PresentationCore和PresentationFramework,如DependencyObject、DependencyProperty、RoutedEvent、FrameworkElement等。
文件结构
默认生成的文件结构如图:

在 App.xaml 中,可以指定项目运行时启动的窗体,默认是主窗体:MainWindow, 此外还可以还可以定义需要的系统资源以及引入程序集等操作 - App.xaml:

在MainWindow.xaml中设计主窗体样式:修改标题为:XAMLWithScript,而后添加一个Button按钮,并进行一些对按钮做一些简单的”初始化“ - MainWindow.xaml:

当前XAML样式呈现的是一个标题为XAMLWithScript,有一个内容为OnClick的Button按钮:

样式中定义的事件在当前页面的后台页面MainWindow.xaml.cs中:

WPF 和 Winform 案例
Application
WPF 和 传统的 WinForm 类似, WPF 同样需要一个 Application 来统领一些全局的行为和操作,并且每个 Domain (应用程序域)中只能有一个 Application 实例存在。和 WinForm 不同的是 WPF Application 默认由两部分组成 : App.xaml 和 App.xaml.cs,这有点类似于 Delphi Form(我对此只是了解,并没有接触过 Delphi ),将定义和行为代码相分离。当然,这个和 WebForm 也比较类似。XAML 从严格意义上说并不是一个纯粹的 XML 格式文件,它更像是一种 DSL(Domain Specific Language,领域特定语言),它的所有定义都直接映射成某些代码,只是具体的翻译工作交给了编译器完成而已。WPF 应用程序由 System.Windows.Application 类来进行管理。
WPF 程序启动项
之前章节有说过WPF程序的启动项默认是通过StartupUri来确定打开哪个窗体,我的理解是它和Winform一样也是通过入口函数来控制打开哪个窗体,只不是默认情况下需要通过StartupUri间接来确定具体打开谁:

新建一个类文件WPFStartupItem.cs重新定义程序的入口,在项目属性中将启动对象修改为自定义的类:
using System;
using System.Windows;
namespace WpfApp2
{
class WPFStartupItem : Application
{
[STAThread]
static void Main()
{
// Method 1 : 创建 Application 对象作为程序入口
Application app = new Application();
Window window = new Window(); // 空窗体 仅做说明
app.Run(window);
// Method 2 : 指定 Application 对象的 MainWindow 属性,调用无参数的 Run 方法
Window window1 = new Window();
app.MainWindow = window1;
window1.Show(); // 必须调用 Show 方法,否则无法显示窗体
app.Run();
// Method 3 : 通过 URL 的方式启动
app.StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
app.Run();
}
}
}
WPFStartupItem是一个WPF应用程序的启动代码示例,演示了三种不同的方式来启动应用程序。
[STAThread]是一个线程特性(thread attribute),用于指定应用程序的主线程类型。在Windows应用程序中,特别是涉及到图形用户界面(GUI)的应用程序中,使用了单线程单元 (STA) 模型。STA模型要求应用程序的主线程(也称为消息循环线程)是单线程的,并且使用了单线程单元的COM组件和功能。
在.NET应用程序中,默认情况下,主线程被标记为多线程单元 (MTA) 模型。但是,对于大多数GUI应用程序,特别是WPF和WinForms应用程序,必须将主线程标记为STA模型,以确保与COM组件和其他GUI相关的功能的兼容性。
因此,为了确保应用程序的主线程被标记为STA模型,需要在主线程的入口方法(例如
Main()方法)前添加[STAThread]特性。在给定的示例中,
[STAThread]特性被应用于Main()方法,用于指定主线程的模型为STA模型,以确保与GUI和COM组件的兼容性。
Application类是WPF应用程序的核心类之一,它继承自System.Windows.Application。它提供了管理和控制WPF应用程序的功能。
Application类的主要职责包括:
- 提供应用程序的入口点:
Application类定义了一个静态的Main()方法,作为应用程序的入口点。在Main()方法中,可以创建一个Application对象,并调用Run()方法来启动应用程序。- 管理应用程序的生命周期:
Application类负责处理应用程序的启动、关闭和退出过程。它提供了事件和方法,用于在应用程序的不同生命周期阶段执行相应的操作,如Startup事件用于处理应用程序启动时的逻辑,Exit事件用于处理应用程序退出时的逻辑。- 管理应用程序的资源:
Application类允许您定义和访问应用程序级别的资源,如样式、模板、资源字典等。这些资源可以在整个应用程序中共享和重用。- 处理全局异常:
Application类提供了一个DispatcherUnhandledException事件,用于捕获和处理应用程序中未处理的异常。您可以订阅该事件,并在发生异常时执行自定义的异常处理逻辑。- 管理应用程序的窗口和导航:
Application类提供了管理应用程序窗口和页面导航的功能。您可以使用MainWindow属性设置应用程序的主窗口,使用NavigationService属性进行页面之间的导航。

WPF 程序关闭
WPF程序一般通过调用Shutdown()方法关闭程序,当然也可以通过Close()或者Application.Exit()实现。默认WPF项目中,Shutdown()方法是隐式发生的,可以通过在App.xaml中显示调用:

| ShutdownMode 参数 | 作用 |
|---|---|
| OnLastWindowClose(默认值) | 最后一个窗体关闭或调用 Application 对象的 Shutdown() 方法时,应用程序关闭。 |
| OnMainWindowClose | 启动窗体关闭或调用 Application 对象的 Shutdown() 方法时,应用程序关闭。(和 C# 的 Windows 应用程序的关闭模式比较类似) |
| OnExplicitShutdown | 只有在调用 Application 对象的 Shutdown() 方法时,应用程序才会关闭。 |
同样你也可以在代码文件(App.xaml.cs)中进行更改,但必须注意这个设置写在app.Run() 方法之前 ,如下代码:
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
app.Run(win);
Application 对象事件

窗体 Window
窗体均继承自System.Windows.Window基类,前文说过在WPF中,一个窗体通常被分成XAML UI文件和后台.cs代码文件,最早的XAMLWithScript:
// MainWindow.xaml
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="XAMLWithScript" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Button Name="button1" Height="23" Width="75" Click="button1_Click">OnClick</Button>
</Grid>
</Window>
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp2
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
事件等代码逻辑内容是写在后台代码文件中的,也可以通过x:Code内部XAML类型在XAML生产环境中放置代码:
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="XAMLWithScript" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Button Name="button1" Height="23" Width="75" Click="button1_Click">OnClick</Button>
<x:Code>
<![CDATA[void button1_Click(object sender,System.Windows.RoutedEventArgs e)
{
button1.Content="Hello there.";
}
]]>
</x:Code>
</Grid>
</Window>
记得把后台.cs文件中关于点击事件注释掉,否则会造成冲突:

上述XAML代码在点击按钮后,按钮文本会变成Hello there.:

窗体生命周期

第一次打开窗口时,只有当引发 Activated 后才会引发 Loaded 和 ContentRendered 事件。 记住这一点,在引发 ContentRendered 时,实际上就可认为窗口已打开。
WPF 窗体的详细的属性、方法、事件请参考 MSDN,有很多的属性、方法、事件与Windows应用程序中System.Windows.Forms.Form类颇为相似,其中常用的一些属性、方法、事件有:
窗体边框模式(WindowStyle 属性)和是否允许更改窗体大小(ResizeMode 属性)。
窗体启动位置(WindowStartupLocation 属性)和启动状态(WindowState 属性)等。
窗体标题(Title 属性)及图标 。
是否显示在任务栏(ShowInTaskbar)
始终在最前(TopMost 属性)
Dispatcher 多线程
微软在WPF引入了Dispatcher,不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以包含多个线程,其中有一个是主线程,其余的是子线程。在WPF或WinForm应用程序中,主线程负责接收输入、处理事件、绘制屏幕等工作,为了使主线程及时响应,防止假死,在开发过程中对一些耗时的操作、消耗资源比较多的操作,都会去创建一个或多个子线程去完成操作,比如大数据量的循环操作、后台下载。这样一来,由于UI界面是主线程创建的,所以子线程不能直接更新由主线程维护的UI界面。
Dispatcher的作用是用于管理线程工作项队列,类似于Win32中的消息队列,Dispatcher的内部函数,仍然调用了传统的创建窗口类,创建窗口,建立消息泵等操作。Dispatcher本身是一个单例模式,构造函数私有,暴露了一个静态的CurrentDispatcher方法用于获得当前线程的Dispatcher。对于线程来说,它对Dispatcher是一无所知的,Dispatcher内部维护了一个静态的 List<Dispatcher> _dispatchers, 每当使用CurrentDispatcher方法时,它会在这个_dispatchers中遍历,如果没有找到,则创建一个新的Dispatcher对象,加入到_dispatchers中去。Dispatcher内部维护了一个Thread的属性,创建Dispatcher时会把当前线程赋值给这个 Thread的属性,下次遍历查找的时候就使用这个字段来匹配是否在_dispatchers中已经保存了当前线程的Dispatcher。
继承关系

- System.Object 类:基类。
- System.Windows.Threading.DispatcherObject 类:从图中看WPF 中的使用到的大部分控件与其他类大多是继承 DispatcherObject 类,它提供了用于处理并发和线程的基本构造。
- System.Windows.DependencyObject类:对WPF中的依赖项属性承载支持与 附加属性承载支持,表示参与 依赖项属性 系统的对象。
- System.Windows.Media.Visual类:为 WPF 中的呈现提供支持,其中包括命中测试、坐标转换和边界框计算等。
- System.Windows.UIElement 类:UIElement 是 WPF 核心级实现的基类,该类是 Windows Presentation Foundation (WPF) 中具有可视外观并可以处理基本输入的大多数对象的基类。
- System.Windows.FrameworkElement类:为 Windows Presentation Foundation (WPF) 元素提供 WPF 框架级属性集、事件集和方法集。此类表示附带的 WPF 框架级实现,它是基于由UIElement定义的 WPF 核心级 API 构建的。
- System.Windows.Controls.Control 类:表示 用户界面 (UI) 元素的基类,这些元素使用 ControlTemplate 来定义其外观。
- System.Windows.Controls.ContentControl类:表示没有任何类型的内容表示单个控件。
WPF的绝大部分的控件,还包括窗口本身都是继承自ContentControl的:

System.Windows.Controls.ItemsControl 类:表示可用于提供项目的集合的控件。

System.Windows.Controls.Panel类:为所有 Panel 元素提供基类。 使用 Panel 元素定位和排列在 Windows Presentation Foundation (WPF) 应用程序的子对象。
System.Windows.Sharps.Sharp类:为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。
走进Dispatcher
WPF 线程分配系统提供一个Dispatcher 属性、VerifyAccess 和CheckAccess方法来操作线程。线程分配系统位于所有WPF类中基类,大部分WPF元素都派生于此类,如下图的Dispatcher类:

与Dispatcher调度对象想对应的就是DispatcherObject,在WPF中绝大部分控件都继承自 DispatcherObject,甚至包括 Application。这些继承自 DispatcherObject 的对象具有线程关联特征,也就意味着只有创建这些对象实例,且包含了Dispatcher的线程 (通常指默认UI线程)才能直接对其进行更新操作。

我们声明一个文本Label并尝试在程序运行过程中更新其显示内容:
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="XAMLWithScript" Height="450" Width="800" WindowStartupLocation="CenterScreen" Loaded="Window_Loaded">
<Grid>
<Button Name="button1" Height="23" Width="75" Click="button1_Click">OnClick</Button>
<Label Name="lbl_Hello">Hello World!</Label>
</Grid>
</Window>
后台代码:
using System;
using System.Threading;
using System.Windows;
namespace WpfApp2
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Thread thread = new Thread(ModifyUI);
thread.Start();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
button1.Content = "Hello there.";
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void ModifyUI()
{
// 模拟一些工作
Thread.Sleep(TimeSpan.FromSeconds(5));
lbl_Hello.Content = "Hello,Dispatcher";
}
}
}
在程序运行五秒后就会报错,System.InvalidOperationException:“调用线程无法访问此对象,因为另一个线程拥有该对象。”

这和Winform跨线程更新UI是类似的,我们一般会使用委托完成对线程UI的更新。在WPF中,按照DispatcherObject的限制原则,我们改用 Window.Dispatcher.Invoke() 即可顺利完成这个更新操作。

如果在其他工程或者类中,我们可以用Application.Current.Dispatcher.Invoke方法来完成同样的操作,它们都指向UI Thread Dispatcher这个唯一的对象。Dispatcher 同时还支持BeginInvoke异步调用,如下代码:
private void btnHello_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(() =>
{
Thread.Sleep(TimeSpan.FromSeconds(5));
this.lblHello.Content = DateTime.Now.ToString();
}));
}).Start();
}
布局
虽然UI很重要,但不能为了UI而UI!
WPF 的布局控件都在System.Windows.Controls.Panel这个基类下面,使用Panel元素在WPF应用程序中放置和排列子对象。

一个Panel的呈现是测量和排列Children子元素、然后在屏幕上绘制它们的过程。所以在布局的过程中会经过一系列的计算,那么Children越多,执行的计算次数就越多。如果不需要较为复杂的 Panel(如Grid和自定义复杂的 Panel),则可以使用构造相对简单的布局(如 Canvas、UniformGrid等),这种布局可带来更好的性能。如果有可能,我们应尽量避免不必要地调用UpdateLayout方法。
每当Panel内的子元素改变其位置时,布局系统就可能触发一个新的处理过程。对此,了解哪些事件会调用布局系统就很重要,因为不必要的调用可能导致应用程序性能变差。
换句话说,布局是一个递归系统,实现在屏幕上对元素进行大小调整、定位和绘制,然后进行呈现。具体如下图,要实现控件 0 的布局, 那么先要实现 0 的子控件 01,02... 的布局, 要实现 01 的布局, 那么得实现 01 的子控件 001,002... 的布局, 如此循环直到子控件的布局完成后, 再完成父控件的布局, 最后递归回去直到递归结束, 这样整个布局过程就完成了。

布局系统为Panel中的每个子控件完成两个处理过程:测量处理过程(Measure)和排列处理过程(Arrange)。每个子Panel均提供自己的 MeasureOverride 和ArrangeOverride方法,以实现自己特定的布局行为。
Canvas
Canvas是最基本的面板,只是一个存储控件的容器,它不会自动调整内部元素的排列及大小。不指定元素位置,元素将默认显示在画布的左上方。它仅支持用显式坐标定位控件,它也允许指定相对任何角的坐标,而不仅仅是左上角。可以使用Left、Top、Right、 Bottom附加属性在Canvas中定位控件。通过设置Left和Right属性的值表示元素最靠近的那条边,应该与Canvas左边缘或右边缘保持一个固定的距离,设置Top和Bottom的值也是类似的意思。实质上,你在选择每个控件停靠的角时,附加属性的值是作为外边距使用的。
Canvas的主要用途是用来画图。Canvas 默认不会自动裁减超过自身范围的内容,即溢出的内容会显示在Canvas外面,这是因为默认 ClipToBounds="False";我们可以通过设置ClipToBounds="True来裁剪多出的内容。
接下来我们来看两个实例,通过xaml和C#实现相同视觉效果:

xaml样式:
<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Canvas Margin="0,0,0,0"
Background="White">
<Rectangle Canvas.Left="210"
Canvas.Top="101"
Width="250"
Height="200"
Fill="Blue"
Stroke="Azure" />
<Ellipse Canvas.Left="65"
Canvas.Top="45"
Width="250"
Height="100"
Panel.ZIndex="1"
Fill="Red"
Stroke="Green" />
<Button Name="btnByCode" Click="btnByCode_Click">后台代码实现</Button>
</Canvas>
</Window>
C#代码实现:
Canvas canv = new Canvas();
//把canv添加为窗体的子控件
Content = canv;
canv.Margin = new Thickness(0, 0, 0, 0);
canv.Background = new SolidColorBrush(Colors.White);
//Rectangle
Rectangle r = new Rectangle();
r.Fill = new SolidColorBrush(Colors.Red);
r.Stroke = new SolidColorBrush(Colors.Red);
r.Width = 200;
r.Height = 140;
r.SetValue(Canvas.LeftProperty, (double)200);
r.SetValue(Canvas.TopProperty, (double)120);
canv.Children.Add(r);
//Ellipse
Ellipse el = new Ellipse();
el.Fill = new SolidColorBrush(Colors.Blue);
el.Stroke = new SolidColorBrush(Colors.Blue);
el.Width = 240;
el.Height = 80;
el.SetValue(Canvas.ZIndexProperty, 1);
el.SetValue(Canvas.LeftProperty, (double)100);
el.SetValue(Canvas.TopProperty, (double)80);
canv.Children.Add(el);
Canvas内的子控件不能使用两个以上的Canvas附加属性,如果同时设置Canvas.Left和Canvas.Right属性,那么后者将会被忽略
StackPanel
堆栈面板,水平或垂直放置元素。通过设置面板的Orientation属性设置了两种排列方式:横排(Horizontal 默认的)和竖排(Vertical)。纵向的StackPanel默认每个元素宽度与面板一样宽,反之横向亦然。如果包含的元素超过了面板空间,它只会截断多出的内容。 元素的Margin属性用于使元素之间产生一定得间隔,当元素空间大于其内容的空间时,剩余空间将由HorizontalAlignment和VerticalAlignment属性来决定如何分配。
同样看实例:

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<StackPanel Margin="0,0,0,0"
Background="White"
Orientation="Horizontal">
<Button Content="竖排第一个按钮" />
<Button Content="竖排第二个按钮" />
<Button Content="竖排第三个按钮" />
<Button Content="竖排第四个按钮" />
</StackPanel>
</Window>
StackPanel sp = new StackPanel();
sp.Orientation = Orientation.Vertical;
//把sp添加为窗体的子控件
this.Content = sp;
sp.Margin = new Thickness(0, 0, 0, 0);
sp.Background = new SolidColorBrush(Colors.White);
sp.Orientation = Orientation.Horizontal;
//Button1
Button b1 = new Button();
b1.Content = "竖排第一个按钮";
sp.Children.Add(b1);
//Button2
Button b2 = new Button();
b2.Content = "竖排第二个按钮";
sp.Children.Add(b2);
//Button3
Button b3 = new Button();
b3.Content = "竖排第三个按钮";
sp.Children.Add(b3);
//Button4
Button b4 = new Button();
b4.Content = "竖排第四个按钮";
sp.Children.Add(b4);
WrapPanel
可换行的行中放置元素,在水平方向上从左向右放置元素,换行后也是从左向右。在垂直方向上,从上到下放置元素,在切换列后也是从上到下。WrapPanel 也提供了Orientation属性设置排列方式,这跟上面的StackPanel基本相似。不同的是WrapPanel会根据内容自动换行。
ItemHeight - 所有子元素都一致的高度。每个子元素填充高度的方式取决于它的VerticalAlignment属性、Height属性等。任何比ItemHeight高的元素都将被截断。
ItemWidth - 所有子元素都一致的宽度。每个子元素填充高度的方式取决于它的VerticalAlignment属性、Width属性等。任何比ItemWidth高的元素都将被截断。
实例:

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<WrapPanel Margin="0,0,0,0" Background="White">
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
<Rectangle Width="60" Height="60" Margin="10,10,10,10" Fill="lightgreen" />
</WrapPanel>
</Window>
WrapPanel wp = new WrapPanel();
//把wp添加为窗体的子控件
Content = wp;
wp.Margin = new Thickness(0, 0, 0, 0);
wp.Background = new SolidColorBrush(Colors.White);
//遍历增加Rectangles
Rectangle r;
for (int i = 0; i <= 10; i++)
{
r = new Rectangle();
r.Fill = new SolidColorBrush(Colors.LightGreen);
r.Margin = new Thickness(10, 10, 10, 10);
r.Width = 60;
r.Height = 60;
wp.Children.Add(r);
}
DockPanel
停靠面板,根据容器的整个边界调整元素,DockPanel定义一个区域,在此区域中,您可以使子元素通过描点的形式排列,这些对象位于Children属性中。停靠面板其实就是在WinForm类似于Dock属性的元 素。DockPanel会对每个子元素进行排序,并停靠在面板的一侧,多个停靠在同侧的元素则按顺序排序。
如果将LastChildFill属性设置为 true(默认设置),那么无论对DockPanel的最后一个子元素设置的其他任何停靠值如何,该子元素都将始终填满剩余的空间。若要将子元素停靠在另一个方向,必须将LastChildFill属性设置为 false,还必须为最后一个子元素指定显式停靠方向。
默认情况下,面板元素并不接收焦点。要强制使面板元素接收焦点,请将Focusable属性设置为 true。

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<DockPanel Width="Auto" Height="Auto" LastChildFill="False">
<Button Content="1" DockPanel.Dock="Top" />
<Button Width="40" Content="4" DockPanel.Dock="Left" />
<Button Width="40" Content="2" DockPanel.Dock="Right" />
<Button Content="3" DockPanel.Dock="Bottom" />
</DockPanel>
</Window>
DockPanel dockPanel = new DockPanel();
dockPanel.Width = double.NaN; // Auto
dockPanel.Height = double.NaN; // Auto
dockPanel.LastChildFill = false;
Button button1 = new Button();
button1.Content = "1";
DockPanel.SetDock(button1, Dock.Top);
Button button2 = new Button();
button2.Width = 40;
button2.Content = "4";
DockPanel.SetDock(button2, Dock.Left);
Button button3 = new Button();
button3.Width = 40;
button3.Content = "2";
DockPanel.SetDock(button3, Dock.Right);
Button button4 = new Button();
button4.Content = "3";
DockPanel.SetDock(button4, Dock.Bottom);
dockPanel.Children.Add(button1);
dockPanel.Children.Add(button2);
dockPanel.Children.Add(button3);
dockPanel.Children.Add(button4);
this.Content = dockPanel;
Grid
表格布局,在行列表格中排列元素,它的子控件被放在一个一个实现定义好的小格子里面,整齐配列。
Grid和其他各个Panel比较起来,功能最多也最为复杂。要使用Grid,首先要向RowDefinitions和ColumnDefinitions属性中添加一定数量的RowDefinitions和 ColumnDefinitions元素,从而定义行数和列数。而放置在Grid面板中的控件元素都必须显示采用附加属性语法定义其放置所在的行和列,它们都是以0为基准的整型值,如果没有显式设置任何行或列,Grid将会隐式地将控件加入在第0行第0列。
由于Grid的组成并非简单的添加属性标记来区分行列,这也使得用户在实际应用中可以具体到某一单 元格中,所以布局起来就很精细了。
Grid的列宽与行高可采用固定、自动、按比例三种方式定义
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="300" />
</Grid.ColumnDefinitions>
</Grid>
<Grid.RowDefinitions> 元素来定义行,其中包含的多个 <RowDefinition> 元素来定义每行的高度。
- 第一行的高度设置为 "Auto",表示该行的高度会根据其内容自动调整。
- 第二行的高度也设置为 "Auto",表示该行的高度会根据其内容自动调整。
- 第三行的高度设置为 "*",表示该行的高度将填充 Grid 的剩余可用空间。
- 第四行的高度设置为 "40",表示该行的高度固定为 40 个设备无关单位(Device Independent Units)。
<Grid.ColumnDefinitions> 元素来定义列,其中包含多个 <ColumnDefinition> 元素来定义每列的宽度。
- 第一列的宽度设置为 "Auto",表示该列的宽度会根据其内容自动调整。
- 第二列的宽度设置为 "300",表示该列的宽度固定为 300 个设备无关单位。
上述xaml内容定义了一个四行两列的Grid布局,第一行和第二行的高度根据其内容自动调整,第三行填充剩余的可用空间,第四行的高度固定为 40。第一列的宽度根据其内容自动调整,第二列的宽度固定为 300。
Grid宽高的几种方式:
- 固定大小(Fixed Size): 可以使用具体数值(如像素、设备无关单位等)来指定行高度和列宽度,例如
RowDefinition.Height="100"和ColumnDefinition.Width="200"。这样可以使行和列具有固定的大小。 - 自动调整(Auto Sizing): 可以使用
Auto关键字来指定行高度和列宽度,例如RowDefinition.Height="Auto"和ColumnDefinition.Width="Auto"。这样会根据行或列的内容自动调整大小,以适应内容的需求。 - 剩余空间填充(Star Sizing): 可以使用
*关键字来指定行高度和列宽度,例如RowDefinition.Height="*"和ColumnDefinition.Width="*"。这样会使行或列占据剩余可用空间的比例。如果多个行或列都设置为*,它们将平均分配剩余空间。
跨越多行多列
在 Grid 布局中,可以通过合并单元格的方式实现跨越多行和多列的布局效果。这可以通过使用 Grid.RowSpan 和 Grid.ColumnSpan 属性来实现。
Grid.RowSpan属性用于指定一个元素跨越的行数,可以设置为大于 1 的整数值。Grid.ColumnSpan属性用于指定一个元素跨越的列数,同样可以设置为大于 1 的整数值。
以下是一个示例,展示了一个元素跨越两行三列的布局:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- 跨越两行三列的元素 -->
<TextBlock Text="Spanning across multiple rows and columns"
Grid.Row="0" Grid.RowSpan="2"
Grid.Column="0" Grid.ColumnSpan="3" />
<!-- 其他元素 -->
<TextBlock Text="Row 2, Column 3" Grid.Row="2" Grid.Column="3" />
<TextBlock Text="Row 1, Column 2" Grid.Row="1" Grid.Column="2" />
</Grid>
在上述示例中,TextBlock 元素通过设置 Grid.RowSpan="2" 和 Grid.ColumnSpan="3" 属性跨越了两行三列。它位于第一行的第一列,并跨越了第一行、第二行和前三列。其他元素则根据指定的行和列进行定位。
通过合并单元格的方式,可以创建更复杂的跨越多行多列的布局效果,以满足特定的布局需求。
分割效果
GridSplitter 用于在 Grid 布局中创建可调整大小的分割效果。它允许用户通过拖动分隔条来改变相邻行或列的大小。
以下是 GridSplitter 的基本用法示例:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="Left" Grid.Column="0" />
<GridSplitter Width="5" Grid.Column="1" ResizeBehavior="PreviousAndNext" />
<TextBlock Text="Right" Grid.Column="2" />
</Grid>
在上述示例中,我们创建了一个包含三个列的 Grid 布局。在中间的列,我们添加了一个 GridSplitter 控件,并设置其宽度为 5 个设备无关单位(Device Independent Units)。GridSplitter 的 ResizeBehavior 属性设置为 PreviousAndNext,表示它将影响前一个列和后一个列的大小。
用户可以在运行时通过拖动 GridSplitter 控件来调整左侧和右侧列的宽度。
GridSplitter 还有其他属性可用于定制其外观和行为,例如 Background、ResizeDirection、ResizeCursor 等。您可以根据需要设置这些属性来满足特定的布局要求。
请注意,GridSplitter 只能用于 Grid 布局,并且需要适当的行和列定义才能正常工作。确保在使用 GridSplitter 时,考虑布局的其他方面,如最小宽度、最大宽度等,以提供更好的用户体验。
混合布局效果

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid Width="Auto" Height="Auto" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="61*"/>
<RowDefinition Height="101*"/>
<RowDefinition Height="108*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="139"/>
<ColumnDefinition Width="184*"/>
<ColumnDefinition Width="45*"/>
<ColumnDefinition Width="250*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="第一行、第一列,占1列" Background="LightBlue" HorizontalAlignment="Center"/>
<Button Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3" Content="第一行、占3列"/>
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Content="第3行,第1列开始,占4列" />
</Grid>
</Window>
添加子元素时不声明具体几行几列时默认都是0;
Grid grid = new Grid();
grid.Width = double.NaN;
grid.Height = double.NaN;
grid.ShowGridLines = true;
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(61, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(101, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(108, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(139) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(184, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(45, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(250, GridUnitType.Star) });
TextBlock textBlock = new TextBlock();
textBlock.Text = "第一行、第一列,占1列";
textBlock.Background = Brushes.LightBlue;
textBlock.HorizontalAlignment = HorizontalAlignment.Center;
Grid.SetRow(textBlock, 0);
Grid.SetColumn(textBlock, 0);
Grid.SetColumnSpan(textBlock, 1);
Button button1 = new Button();
button1.Content = "第一行、占3列";
Grid.SetRow(button1, 0);
Grid.SetColumn(button1, 1);
Grid.SetRowSpan(button1, 2);
Grid.SetColumnSpan(button1, 3);
Button button2 = new Button();
button2.Content = "第3行,第1列开始,占4列";
Grid.SetRow(button2, 2);
Grid.SetColumn(button2, 0);
Grid.SetColumnSpan(button2, 4);
grid.Children.Add(textBlock);
grid.Children.Add(button1);
grid.Children.Add(button2);
this.Content = grid;
设计的时候看不清楚的话可以通过
ShowGridLines属性把网格线显示出来
UniformGrid
Grid 简化版,强制所有单元格具有相同尺寸。每个单元格的大小相同,不用在定义行列集合。均布网格每个单元格只能容纳一个元素,将自动按照定义在其内部的元素个数,自动创建行列,并通常保持相同的行列数。UniformGrid 中没有Row 和Column 附加属性,也没有空白单元格。
与
Grid布局控件相比,UniformGrid布局控件很少使用。Grid面板是用于创建简单乃至复杂窗口布局的通用工具。UniformGrid面板是一个一种更特殊的布局容器,主要用于在一个刻板的网格中快速地布局元素。

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript" Width="800" Height="450"
Loaded="Window_Loaded" WindowStartupLocation="CenterScreen" mc:Ignorable="d">
<UniformGrid Columns="2" Rows="2">
<Button>(0,0)</Button>
<Button>(0,1)</Button>
<Button>(1,0)</Button>
<Button>(1,1)</Button>
</UniformGrid>
</Window>
C#代码:
Grid grid = new Grid();
grid.Width = double.NaN;
grid.Height = double.NaN;
grid.ShowGridLines = true;
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(61, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(101, GridUnitType.Star) });
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(108, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(139) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(184, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(45, GridUnitType.Star) });
grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(250, GridUnitType.Star) });
TextBlock textBlock = new TextBlock();
textBlock.Text = "第一行、第一列,占1列";
textBlock.Background = Brushes.LightBlue;
textBlock.HorizontalAlignment = HorizontalAlignment.Center;
Grid.SetRow(textBlock, 0);
Grid.SetColumn(textBlock, 0);
Grid.SetColumnSpan(textBlock, 1);
Button button1 = new Button();
button1.Content = "第一行、占3列";
Grid.SetRow(button1, 0);
Grid.SetColumn(button1, 1);
Grid.SetRowSpan(button1, 2);
Grid.SetColumnSpan(button1, 3);
Button button2 = new Button();
button2.Content = "第3行,第1列开始,占4列";
Grid.SetRow(button2, 2);
Grid.SetColumn(button2, 0);
Grid.SetColumnSpan(button2, 4);
grid.Children.Add(textBlock);
grid.Children.Add(button1);
grid.Children.Add(button2);
this.Content = grid;
ViewBox
Viewbox 是一个容器控件,允许其内容根据可用空间进行缩放,同时保持其纵横比。它通常用于为其中的内容提供自动缩放和调整大小的行为。
ViewBox这个控件通常和其他控件结合起来使用,是WPF中非常有用的控件,用来定义一个内容容器。ViewBox组件的作用是拉伸或延展位于其中的组件,以填满可用空间,使之有更好的布局及视觉效果。
一个
Viewbox中只能放一个控件。如果多添加了一个控件就会报错。
以下是一些常用的 Viewbox 属性:
Stretch(拉伸):指定内容在视图框中的拉伸方式。常见的取值包括:Uniform(均匀):保持内容的纵横比,同时填充视图框,可能导致内容被裁剪。UniformToFill(均匀填充):保持内容的纵横比,同时填充视图框,可能导致内容被裁剪。Fill(填充):不保持内容的纵横比,将内容拉伸以填充视图框。
StretchDirection(拉伸方向):指定内容在视图框中拉伸的方向。常见的取值包括:Both(双向):内容可以在水平和垂直方向上拉伸。DownOnly(仅向下):内容只能在垂直方向上拉伸。UpOnly(仅向上):内容只能在水平方向上拉伸。
HorizontalAlignment(水平对齐)和VerticalAlignment(垂直对齐):指定内容在视图框中的水平和垂直对齐方式。常见的取值包括:Left(左对齐):内容在视图框的左侧对齐。Center(居中对齐):内容在视图框的中间对齐。Right(右对齐):内容在视图框的右侧对齐。Top(顶部对齐):内容在视图框的顶部对齐。Bottom(底部对齐):内容在视图框的底部对齐。
MaxWidth(最大宽度)和MaxHeight(最大高度):指定内容在视图框中的最大宽度和最大高度限制。当内容超过指定的最大尺寸时,将被自动缩放以适应。
Uniform效果下的Viewbox:

<Viewbox Stretch="Uniform">
<Button Content="Hello,Knights Warrior" />
</Viewbox>
Viewbox vb = new Viewbox();
vb.Stretch = Stretch.Uniform;
//Button1
Button b1 = new Button();
b1.Content = "Hello,Knights Warrior";
vb.Child = b1;
this.Content = vb;
Border
Border不是布局面板,但是经常与布局类的面板一起配合使用,用于为其内部的内容提供边框和可选的背景样式。它可以包含单个子元素,并且可以根据需要调整其大小以适应子元素。
以下是 Border 控件的一些常用属性:
Background(背景):指定Border的背景颜色或背景画刷。可以使用颜色名称、十六进制值或画刷对象来设置背景。BorderBrush(边框画刷):指定Border的边框颜色或边框画刷。可以使用颜色名称、十六进制值或画刷对象来设置边框。BorderThickness(边框厚度):指定Border的边框厚度。可以设置为单个值,表示统一的边框宽度,或设置为Left、Top、Right、Bottom分别指定不同的边框宽度。CornerRadius(圆角半径):指定Border的圆角半径,以使边框的角变得圆润。可以设置为单个值,表示统一的圆角半径,或设置为TopLeft、TopRight、BottomRight、BottomLeft分别指定不同的圆角半径。Padding(内边距):指定Border内容与边框之间的空间,也就是内边距。可以设置为单个值,表示统一的内边距,或设置为Left、Top、Right、Bottom分别指定不同的内边距。Child(子元素):指定Border的单个子元素。这个子元素将被包含在边框内部,并且可以根据需要调整边框的大小。
通过使用 Border 控件,您可以为内部内容提供边框和背景样式,并控制边框的大小、边框颜色以及内部内容的对齐和间距。这使得 Border 成为一种常用的容器控件,用于组织和美化界面元素。

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Border Width="270"
Height="250"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="LightGray"
BorderBrush="LightGreen"
BorderThickness="8"
CornerRadius="5">
<Canvas Background="LightYellow">
<Rectangle Canvas.Left="30"
Canvas.Top="20"
Width="150"
Height="150"
Fill="red"
Stroke="Black"
StrokeThickness="10" />
</Canvas>
</Border>
</Window>
Border border = new Border();
border.Width = 270;
border.Height = 250;
border.HorizontalAlignment = HorizontalAlignment.Center;
border.VerticalAlignment = VerticalAlignment.Center;
border.Background = Brushes.LightGray;
border.BorderBrush = Brushes.LightGreen;
border.BorderThickness = new Thickness(8);
border.CornerRadius = new CornerRadius(5);
Canvas canvas = new Canvas();
canvas.Background = Brushes.LightYellow;
Rectangle rectangle = new Rectangle();
rectangle.Width = 150;
rectangle.Height = 150;
rectangle.Fill = Brushes.Red;
rectangle.Stroke = Brushes.Black;
rectangle.StrokeThickness = 10;
Canvas.SetLeft(rectangle, 30);
Canvas.SetTop(rectangle, 20);
canvas.Children.Add(rectangle);
border.Child = canvas;
// Add the Border to the main Window
this.Content = border;
ScrollViewer
ScrollViewer 是 WPF 中常用的滚动容器控件,用于在需要时提供滚动功能以显示超出容器可见区域的内容。它可以包含单个子元素,并根据需要在垂直和/或水平方向上显示滚动条。
以下是 ScrollViewer 控件的一些常用属性:
HorizontalScrollBarVisibility(水平滚动条可见性):指定水平滚动条的可见性。常见的取值包括:Disabled(禁用):禁用水平滚动条。Auto(自动):根据需要自动显示水平滚动条。Visible(可见):始终显示水平滚动条。
VerticalScrollBarVisibility(垂直滚动条可见性):指定垂直滚动条的可见性。常见的取值包括:Disabled(禁用):禁用垂直滚动条。Auto(自动):根据需要自动显示垂直滚动条。Visible(可见):始终显示垂直滚动条。
CanContentScroll(内容滚动):指定ScrollViewer是否以逻辑单元(例如行或项)为单位滚动内容。当设置为True时,滚动条将以逻辑单元为单位滚动,而不是以像素为单位滚动。Content(内容):指定ScrollViewer的单个子元素。这个子元素将被包含在滚动容器中,并可以根据需要进行滚动。
通过使用 ScrollViewer 控件,可以将内容放置在可滚动的容器中,以便在容器的可见区域之外显示内容,并通过滚动条来浏览内容。这对于处理大量内容或需要显示大型元素的情况非常有用,以保持界面的可用性和可访问性。

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="XAMLWithScript"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<ScrollViewer>
<Rectangle Width="500" Height="500" Fill="Gray"></Rectangle>
</ScrollViewer>
</Grid>
</Window>
只能放单个子元素
布局综合应用
叙利亚战损版 Microsoft ToDo 布局 - 不建议

<Window x:Class="WpfApp2.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:local="clr-namespace:WpfApp2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="WPFDemo"
Width="800"
Height="450"
Loaded="Window_Loaded"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid Width="Auto" Height="Auto">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="8*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="6*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.Column="1" Grid.RowSpan="8" Background="#218868"
Orientation="Vertical">
<TextBlock Text=" 我的一天" FontSize="18" FontWeight="Bold" Foreground="White"/>
<TextBlock Text=" 5月20日,星期六" FontSize="10" Foreground="White"/>
<TextBlock />
<Border BorderThickness="10" CornerRadius="2" BorderBrush="#1e7b5e" Background="#1e7b5e" >
<TextBlock Text="+ 添加任务" Foreground="AntiqueWhite" FontSize="13"/>
</Border>
</StackPanel>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Miscrosoft ToDo" FontSize="16" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Grid.Row="1" Grid.Column="0" Content="我的一天" FontSize="16" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Background="LightBlue"/>
<Button Grid.Row="2" Grid.Column="0" Content="重要" FontSize="16" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Background="LightBlue"/>
<Button Grid.Row="3" Grid.Column="0" Content="已计划日程" FontSize="16" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Background="LightBlue"/>
<Button Grid.Row="4" Grid.Column="0" Content="已分配任务" FontSize="16" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Background="LightBlue"/>
<Button Grid.Row="5" Grid.Column="0" Content="任务" FontSize="16" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Background="LightBlue">
<Button.Style>
<Style TargetType="Button">
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="5"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="2">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
</Window>
如果你真的看到了这儿,那我觉得,这件事真是 - 泰裤辣!!!
自定义Panel
该章节省略未读。。。
WPF 入门笔记 - 01 - 入门基础以及常用布局的更多相关文章
- 【python3两小时快速入门】入门笔记01:基础
又要我搞爬虫了,这次的源网站使用的ajax加载数据,我用java爬下来的页面内容部分全都是空,虽然java也有插件,但是使用起来感觉很麻烦,所以,python!老子来了. 1. 版本:pytho ...
- [Java入门笔记] 面向对象编程基础(二):方法详解
什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...
- Android 基础:常用布局 介绍 & 使用(附 属性查询)
Android 基础:常用布局 介绍 & 使用(附 属性查询) 前言 在 Android开发中,绘制UI时常需各种布局 今天,我将全面介绍Android开发中最常用的五大布局 含 Andr ...
- matlab入门笔记(一):常用快捷键
摘自<matlab从入门到精通>胡晓东 matlab命令窗口常用快捷键与命令 matlab的工作空间和变量编辑窗口 搜索命令help和look for
- Ansible入门笔记(2)之常用模块
目录 Ansible常用模块 1.1.Ansible Ad-hoc 1.2.Ansible的基础命令 1.3.常用模块 Ansible常用模块 1.1.Ansible Ad-hoc 什么事ad-hoc ...
- [Python]Python入门笔记:语法基础
Python笔记 一.基本语法 1.1 注释 文档注释: """contents""" 多行注释: ''' contents ''' 单行注 ...
- [Java入门笔记] 面向对象编程基础(一):类和对象
什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...
- [Java入门笔记] Java语言基础(二):常量、变量与数据类型
常量与变量 什么是常量和变量 常量与变量都是程序在运行时用来存储数据一块内存空间 常量: 常量的值在程序运行时不能被改变,Java中声明常量必须使用final关键字.常量还可以分为两种意思: 第1种意 ...
- 【苏勇老师Linux 入门笔记】网络基础
IP 地址 IP 编制时一个双层编制方案,一个 IP 地址标示一个主机 (或一个网卡接口). 一个 IP 地址分为两个部分:网络部分(所属区域)和主机部分(标示区域中的哪个主机).IPv4 共32位, ...
- [Java入门笔记] 面向对象编程基础(三):成员变量和局部变量
在类中,变量根据定义的位置不同,可以分为成员变量和局部变量.
随机推荐
- 如何使用Github创建一个仓库
创建仓库(对我来说,这是新建) 点击这里的Create repository: 进入到这样一个界面: 其中,Repository name,是我们即将创建完成的仓库名称: 而这里: 需要填写的是对仓库 ...
- T-Dubbo,最好的RPC接口测试工具,支持nacos、zookeeper两大主流注册中心,真香!
这可能是有史以来最好用的RPC接口测试工具 文末有视频简介 获取方式 一只小Coder 简介 T-Dubbo,是一个基于Dubbo的全自动RPC接口测试平台为当下最流行的微服务架构中的RPC接口提供了 ...
- 前端转向PHP进阶之路
一.PHP简介 Hypertext Preprocessor,又称为超文本预处理器(HTML为超文本标签语言),就是我们所说的PHP.它是一种糅杂百家的后台语言,在PHP中,可以见到C.Java等语言 ...
- SpringBoot 启动类的原理
SpringBoot启动类上使用 @SpringBootApplication注解,该注解是一个组合注解,包含多个其它注解.和类定义(SpringApplication.run)要揭开 SpringB ...
- MyBatis 版本升级引发的线上问题
MyBatis上线前后的版本:上线前(3.2.3)上线后(3.4.6) 服务上线后,开始陆续出现了一些更新系统交互日志方面的报警,这属于系统的辅助流程,报警如下代码所示.我们发现都是跟 MyBatis ...
- 全面了解 Redis 高级特性,实现高性能、高可靠的数据存储和处理
目录 高性能.高可用.高可扩展性的原理 持久化 RDB持久化 AOF持久化 持久化的配置 RDB配置 AOF配置 持久化的恢复 RDB的恢复 AOF的恢复 RDB和AOF的选择 持久化对性能的影响 数 ...
- Java设计模式 —— 代理模式
15 代理模式 15.1 代理模式概述 Proxy Pattern: 给某一个对象提供一个代理或占位符,由代理对象来控制对原对象的访问. 代理对象是客户端和目标对象的之前的桥梁,它接收来自客户端的请求 ...
- DG:switchover切换操作
问题描述:我们配置DG的目的就是为了在主库出现故障时,备库能够提供服务,保证业务的正常运行,switchover是用户有计划的进行停机切换,能够保证不丢失数据,我记录一下我进行switchover中的 ...
- 【Java SE】IO流
1.File类 ①File类的一个对象代表一个文件或一个文件目录 ②File类声明在java.io下 1.1 FIle类的声明 路径分隔符 Windows和DOS系统默认使用'',UNIX和URL使用 ...
- Android Studio中的一些常见控件
Android Studio是一款非常流行的用于开发Android应用程序的集成开发环境(IDE).它提供了许多内置控件,使开发人员可以轻松创建应用程序界面和功能.在本文中,我们将介绍Android ...