WPF入门教程系列六——布局介绍与Canvas(一)
从这篇文章开始是对WPF中的界面如何布局做一个较简单的介绍,大家都知道:UI是做好一个软件很重要的因素,如果没有一个漂亮的UI,功能做的再好也无法吸引很多用户使用,而且没有漂亮的界面,那么普通用户会感觉这个软件没有多少使用价值。
一. 总体介绍
WPF的布局控件都在System.Windows.Controls.Panel这个基类下面,使用 WPF提供的各种控件在WPF应用程序中界面进行布局,同时对各种子控件(如按钮、文本框,下拉框等)进行排列组合。

Pane类的公共属性太多了。就简单介绍几个常见的属性如下表。
|
名称 |
说明 |
|
|
Cursor |
获取或设置在鼠标指针位于此元素上时显示的光标。 |
|
|
DataContext |
获取或设置元素参与数据绑定时的数据上下文。 |
|
|
Dispatcher |
获取与此 DispatcherObject 关联的 Dispatcher。 |
|
|
FontFamily |
获取或设置控件的字体系列。 |
|
|
FontSize |
获取或设置字号。 |
|
|
FontWeight |
获取或设置指定的字体的权重或粗细。 |
|
|
Foreground |
获取或设置描述前景色的画笔。 |
|
|
HandlesScrolling |
获取一个值控件是否支持滚动。 |
|
|
Height |
获取或设置元素的建议高度。 |
|
|
HorizontalContentAlignment |
获取或设置控件内容的水平对齐。 |
|
|
IsLoaded |
获取一个值,该值指示是否已加载此元素以供呈现。 |
|
|
IsMouseOver |
获取一个值,该值指示鼠标指针是否位于此元素(包括可视树上的子元素)上。这是一个依赖项属性。 |
|
|
IsTabStop |
获取或设置一个值控制是否在选项卡上导航包含。 |
|
|
IsVisible |
获取一个值,该值指示此元素在用户界面 (UI) 中是否可见。这是一个依赖项属性。 |
|
|
LayoutTransform |
获取或设置在执行布局时应该应用于此元素的图形转换方式。 |
|
|
Margin |
获取或设置元素的外边距。 |
|
|
Name |
获取或设置元素的标识名称。 该名称提供一个引用,以便当 XAML 处理器在处理过程中构造标记元素之后,代码隐藏(如事件处理程序代码)可以对该元素进行引用。 |
|
|
Opacity |
获取或设置当 UIElement 在用户界面 (UI) 中呈现时为其整体应用的不透明度因子。这是一个依赖项属性。 |
|
|
Padding |
获取或设置控件中的空白。 |
|
|
RenderTransform |
获取或设置影响此元素的呈现位置的转换信息。这是一个依赖项属性。 |
|
|
TabIndex |
获取或设置使用 tab 键时,确定顺序接收焦点的元素的值,当用户将控件定位。 |
|
|
Tag |
获取或设置任意对象值,该值可用于存储关于此元素的自定义信息。 |
|
|
ToolTip |
获取或设置在用户界面 (UI) 中为此元素显示的工具提示对象。 |
|
|
TouchesCaptured |
获取在此元素上捕获的所有触摸设备。 |
|
|
TouchesCapturedWithin |
获取在此元素或其可视化树中的任何子元素上捕获的所有触摸设备。 |
|
|
VerticalContentAlignment |
获取或设置控件内容的垂直对齐方式。 |
|
|
Visibility |
获取或设置此元素的用户界面 (UI) 可见性。这是一个依赖项属性。 |
|
|
VisualOpacityMask |
获取或设置 Brush 值,该值表示 Visual 的不透明蒙板。 |
|
|
Width |
获取或设置元素的宽度。 |
一个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的左上方(等同于设置Left和Top为0)。
Canvas的主要用途是用来画图。Canvas默认不会自动裁减超过自身范围的内容,即溢出的内容会显示在Canvas外面,这是因为默认 ClipToBounds=”False”;我们可以通过设置ClipToBounds=”True”来裁剪多出的内容。
接下来我们来看两个实例,第一个实例使用XAML代码实现:
<Window x:Class="WpfApp1.WindowCanvas"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="WindowCanvas" Height="400" Width="500">
<Grid>
<Canvas Margin="0,0,0,0" Background="White">
<Rectangle Fill="Blue"
Stroke="Azure"
Width="250"
Height="200"
Canvas.Left="210" Canvas.Top="101"/>
<Ellipse Fill="Red"
Stroke="Green"
Width="250" Height="100"
Panel.ZIndex="1"
Canvas.Left="65" Canvas.Top="45"/>
</Canvas>
<Canvas>
<Button Name="btnByCode" Click="btnByCode_Click">后台代码实现</Button>
</Canvas>
</Grid>
</Window>
实例后的效果如下图。

第二个实例,我们使用后台代码来实现。我使用C#来实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// WindowCanvas.xaml 的交互逻辑
/// </summary>
public partial class WindowCanvas : Window
{
public WindowCanvas()
{
InitializeComponent();
}
public void DisplayCanvas()
{
Canvas canv = new Canvas();
//把canv添加为窗体的子控件
this.Content = canv;
canv.Margin = new Thickness(, , , );
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 = ;
r.Height = ;
r.SetValue(Canvas.LeftProperty, (double));
r.SetValue(Canvas.TopProperty, (double));
canv.Children.Add(r);
//Ellipse
Ellipse el = new Ellipse();
el.Fill = new SolidColorBrush(Colors.Blue);
el.Stroke = new SolidColorBrush(Colors.Blue);
el.Width = ;
el.Height = ;
el.SetValue(Canvas.ZIndexProperty, );
el.SetValue(Canvas.LeftProperty, (double));
el.SetValue(Canvas.TopProperty, (double));
canv.Children.Add(el);
}
private void btnByCode_Click(object sender, RoutedEventArgs e)
{
DisplayCanvas();
}
}
}
实现后的效果如下图。

最后 要说明一点Canvas内的子控件不能使用两个以上的Canvas附加属性,如果同时设置Canvas.Left和Canvas.Right属性,那么后者将会被忽略。
WPF入门教程系列六——布局介绍与Canvas(一)的更多相关文章
- WPF入门教程系列三——Application介绍(续)
接上文WPF入门教程系列二——Application介绍,我们继续来学习Application 三.WPF应用程序的关闭 WPF应用程序的关闭只有在应用程序的 Shutdown 方法被调用时,应用程序 ...
- WPF入门教程系列二——Application介绍
一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...
- WPF入门教程系列八——布局之Grid与UniformGrid(三)
五. Grid Grid顾名思义就是“网格”,它的子控件被放在一个一个实现定义好的小格子里面,整齐配列. Grid和其他各个Panel比较起来,功能最多也最为复杂.要使用Grid,首先要向RowDef ...
- WPF入门教程系列四——Dispatcher介绍
一.Dispatcher介绍 微软在WPF引入了Dispatcher,那么这个Dispatcher的主要作用是什么呢? 不管是WinForm应用程序还是WPF应用程序,实际上都是一个进程,一个进程可以 ...
- WPF入门教程系列九——布局之DockPanel与ViewBox(四)
七. DockPanel DockPanel定义一个区域,在此区域中,您可以使子元素通过描点的形式排列,这些对象位于 Children 属性中.停靠面板其实就是在WinForm类似于Dock属性的元 ...
- WPF入门教程系列十——布局之Border与ViewBox(五)
九. Border Border 是一个装饰的控件,此控件绘制边框及背景,在 Border 中只能有一个子控件,若要显示多个子控件,需要将一个附加的 Panel 控件放置在父 Border 中.然后可 ...
- WPF入门教程系列七——布局之WrapPanel与StackPanel(二)
三. WrapPanel WrapPanel布局面板将各个控件从左至右按照行或列的顺序罗列,当长度或高度不够是就会自动调整进行换行,后续排序按照从上至下或从右至左的顺序进行. Orientation— ...
- WPF入门教程系列五——Window 介绍
一.窗体类基本概念 对于WPF应用程序,在Visual Studio和Expression Blend中,自定义的窗体均继承System.Windows.Window类.用户通过窗口与 Windows ...
- WPF入门教程系列二十三——DataGrid示例(三)
DataGrid的选择模式 默认情况下,DataGrid 的选择模式为“全行选择”,并且可以同时选择多行(如下图所示),我们可以通过SelectionMode 和SelectionUnit 属性来修改 ...
随机推荐
- Cauchy 级数浓缩判别法
- node.js之path
说到node.js,可能实际中用到node进行后台开发的公司不多,大部分人都没有开发后台的经验.但是也要了解node相关模块的用法,因为现在前端自动化脚本的构建,模块的打包越来越离不开node.特别是 ...
- Android应用:横竖屏切换总结
眨眼间,已经到了2016你年春节前,离上一篇博客的时间已经有6个月多,回想起这半年的种种,不得不说,学习和工作实在是太忙了,或许这就是程序员的真实写照吧. 写博客之初,主要的目的还是为了把自己的学习痕 ...
- 【MySQL】MySQL中where条件的执行分析
1.问题描述 一条SQL,在数据库中是如何执行的呢?相信很多人都会对这个问题比较感兴趣.当然,要完整描述一条SQL在数据库中的生命周期,这是一个非常巨大的问题,涵盖了SQL的词法解析.语法解析.权限检 ...
- Form表单提交数据的几种方式
一.submit提交 在form标签中添加Action(提交的地址)和method(post),且有一个submit按钮(<input type='submit'>)就可以进行数据的提交, ...
- JavaWeb的学习之Servlet(转载自孤傲苍狼)
一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...
- BI先特技软件 Analyzer安装时的部分问题
废话不多,先看第一个问题,我先运行了 Analyzer.3.0.2357b.64.exe 然后就是傻瓜式地下一步 好的,现在问题来了,当我运行安装完毕的Analyzer时,页面给了我这样的提示“ HT ...
- Replication的犄角旮旯(四)--关于事务复制的监控
<Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...
- SQL入门经典(三) 之连接查询
上一篇介绍到查询.这一篇主要讲连接查询,将介绍INNER JOIN,OUTER JOIN(LEFT和RIGHT),FULL JOIN,CROSS JOIN. 连接顾名斯义就是把多个数据表数据合并到一个 ...
- 【转载】关于.NET里的内存泄漏
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中..Net 中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉.虽然.N ...