【WPF学习】第九章 使用Canvas面板进行基于坐标的布局
Canvas面板允许使用精确的坐标放置元素,如果设置数据驱动的富窗体和标准对话框,这并非好的选择;但如果需要构建其他一些不同的内容(例如,为图形工具创建创建绘图表面),Canvas面板可能是个有用的工具。Canvas面板还是最轻量级的布局容器。这是因为Canvas面板没有包含任何复杂的布局逻辑,用以改变其子元素的首选尺寸。Canvas面板只是在指定的位置放置其子元素,并且子元素具有所希望的精确尺寸。
为在Canvas面板中定位元素,需要设置Canvas.Left和Canvas.Top附加属性。Canvas.Left属性设置元素左边和Canvas面板左边之间的单位数。Canvas.Top属性设置子元素顶部和Canvas面板顶边之间的单位数。同样,这些数值也是以设备无关单位设置的。当将系统DPI设置为96dpi时,设备无关单位恰好等于通常的像素。
可使用Width和Height属性明确设置子元素的尺寸。与使用其他面板相比,使用Canvas面板时这种设置更普遍,因为Canvas面板没有自己的布局逻辑(并且当需要精确控制组合元素如何排列时,经常会使用Canvas面板)。如果没有设置Width和Height属性,元素会获取它所期望的尺寸——换句话说,它将变得足够大以适应其内容。
下面是一个使用Canvas的示例:
<Window x:Class="CanvasLayout.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas> <Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button> <Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button> <Button Canvas.Left="60" Canvas.Top="80" Width="80" Height="60">(60,80)</Button> <Button Canvas.Left="180" Canvas.Top="150" Width="100" Height="60">(180,150)</Button> </Canvas> </Window>
最终效果如下图所示:
如果改变窗口的大小,Canvas面板就会拉伸以填满可用空间,但Canvas面板上的控件不会改变其尺寸和位置。Canvas面板不包含任何锚定和停靠功能,这两个功能是在Windows窗体中使用坐标布局提供的。造成该问题的部分原因是为了保持Canvas面板的轻量级,另一个原因是为了防止以不当目的使用Canvas面板(例如,确定标准用户界面的布局)。
与其他所有布局容器一样,可在用户界面中嵌套Canvas面板。这意味着可使用Canvas面板在窗口的一部分绘制一些细节内容,而在窗口的其余部分使用更合乎标准的WPF面板。
一、Z顺序
如果Canvas面板中有多个互相重叠的元素,可通过设置Canvas.ZIndex附加属性来控制他们的层叠方式。
添加的所有元素通常都具有相同的ZIndex指——0。如果元素具有相同的ZIndex值,就按他们在Canvas.Children集合中的顺序进行显示,这个顺序依赖于元素在XAML标记中定义的顺序。在标记靠后位置声明的元素会显示在前面声明的元素的上面。
然而,可通过增加任何子元素的ZIndex值来提高层次级别。因为具有更高ZIndex值的元素始终显示在较低ZIndex值得元素的上面。如下代码所示:
<Window x:Class="CanvasLayout.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas> <Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button> <Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button> <Button Canvas.Left="180" Canvas.Top="110" Canvas.ZIndex="1" Width="80" Height="60">(180,110)</Button> <Button Canvas.Left="180" Canvas.Top="150" Width="100" Height="60">(180,150)</Button> </Canvas> </Window>
效果如下图所示:
如果需要通过代码来改变元素的位置,ZIndex属性是非常有用的。只需要调用Canvas.SetZIndex()方法,并传递希望修改的元素和希望使用的新ZIndex值即可。遗憾的是,并不存在BringToFront()或SendToBack()方法——要实现这一行为,需要跟踪最高和最低的ZIndex值。
二、InkCanvas元素
WPF还提供了InkCanvas元素,它与Canvas面板在某些方法是类似的(而在其他方面却完全不同)。和Canvas面板一样,InkCanvas元素定义了4个附加属性(Top、Left、Bottom和Right),可将这4个附加属性应用于子元素,以根据坐标进行定位。然而,基本的内容区别很大——实际上,InkCanvas类不是派生自Canvas类,甚至也不是派生自Panel基类,而是直接派生自FrameworkElement类。
InkCanvas元素的主要目的用于接收手写笔输入。手写笔是一种在平板PC中使用的类似钢笔的输入设备,然而,InkCanvas元素同事也可使用鼠标进行工作,就像使用手写笔一样。因此,用户可使用鼠标在InkCanvas元素上绘制线条,或者选择以及操作InkCanvas中的元素。
InkCanvas元素实际上包含两个子内容集合。一个是为人熟知的Children集合,它保存任意元素,就像Canvas面板一样。每个子元素可根据Top、Left、Bottom和Right属性进行定位。另一个是Strokes结合,它保存System.Windows.Ink.Stroke对象,该对象表示用户在InkCanvas元素上绘制的图形输入。用户绘制的每条直线或曲线都变成独立的Stroke对象。得益于这两个集合,可使用InkCanvas让用户使用存储在Strokes集合中的笔画(Stroke)为保存在Children集合中的内容添加注释。
下面的这个示例中包含一副图片的InkCanvas元素,这幅图片已经使用附加的笔画注释过。
<Window x:Class="CanvasLayout.InkCanvasWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="InkCanvasWindow" Height="300" Width="385.714"> <InkCanvas Name="inkCanvas" Background="LightYellow" EditingMode="Ink"> <Image Source="背景.jpg" Width="300" Height="240" Stretch="Fill" InkCanvas.Top="10" InkCanvas.Left="10"></Image> </InkCanvas> </Window>
效果图如下所示:
笔画是在用户在运行时绘制的。
根据为InkCanvas.EditingMode属性设置的值,可以采用截然不同的方式使用InkCanvas元素,下表列出了所有选项:
InkCanvas元素会引发多种事件,当编辑模式改变时会引发ActiveEditingModeChanged事件,在GestureOnly或InkAndGesture模式下删除姿势时会引发Gesture事件,在Select模式下选择元素或改变元素时会引发SelectionChanging事件、SelectionChanged事件、SelectionMoving事件、SelectionMoved事件、SelectionResizing事件和SelectionResized事件。其中,名称以"ing"结尾的事件表示动作将要发生,但可以通过设置EventArgs对象的Cancel属性取消事件。
后期还会进行更详细的介绍。