现在的位置: 首页 > 综合 > 正文

《Programming WPF》翻译 第3章 3.内嵌控件

2012年07月18日 ⁄ 综合 ⁄ 共 8760字 ⁄ 字号 评论关闭

WPF提供了一系列内嵌控件。其中大多数符合标准的你已经熟悉的Windows控件类型。注意到没有一个是包装在旧的Win32控件外面的控件。虽然它们看上去就像是它们的副本,它们都是与生俱来的WPF控件。这意味着它们为WPF在本书中描述的功能提供了完全的支持,包括样式、独立的分辨率、数据绑定、合成、以及充分的集成支持WPF的图形化能力。

3.3.1按钮

按钮是用户可以点击的控件。点击的结果由应有程序的开发者胜任,但是共同的期望依赖于按钮的类型。例如,点击一个用来表示选择的CheckBoxRadioButton,并未正常拥有任何即时的效果来真实反映那个选择。与之对比,点击一个正常的按钮,通常会有即时的效果。

使用按钮是直接的。示例3-11显示了按钮元素的标记。

示例3-11

<Button Click="ButtonClicked">Button</Button>

元素的内容(这种情形下是“Button”文字)用于按钮的标题。点击事件的句柄通过一个属性明确地指定。这表明了xaml的后台代码必须包含一个在标签中明确指定名称的方法,正如示例3-12所示(当然我们还可以附属事件句柄,通过给按钮一个xName,以及使用正常的C#事件句柄语法。)

示例3-12

private void ButtonClicked(object sender, RoutedEventArgs e) {
    MessageBox.Show(
"Button was clicked");
}

可选择性的,一个按钮常规的属性可以被设定,在这种情形中,当按钮被点击时,指定的命令将会被调用。示例3-13显示了一个按钮调用标准ApplicationCommands.Copy命令。

示例3-13

<Button Command="Copy">Copy</Button>

图3-4显示了3种由WPF提供的按钮类型。这些按钮都派生于一个共同的基类,ButtonBase——这个类派生于ContentControl,意味着它们全部支持内容模型:你不受限制于为一个按钮使用简单的文本作为一个标签。

3-4

如图3-5所示,你可以使用无论任何你喜欢的内容,虽然你仍能获取默认的按钮外观在你选择的内容周围或者旁边(如果你希望取代整个按钮的外观,而不是仅定义它的标题,你可以使用一个控件模板。参考第5章获取更多关于模板的信息。)

3-5

虽然这些按钮派生于共同的ButtonBase基类,RadioButtonCheckBox通过ToggleButton类间接派生于这个基类。这个结基类定义了一个IsChecked属性,指出了用户是否检查了按钮。

Radio按钮正常使用于组中,其中每次只能选择一个按钮。使用RadioButtonList元素来指出一组radio按钮作为一个组,正如示例3-14所示。

示例3-14

 

<RadioButtonList>
    
<RadioButton>To be</RadioButton>
    
<RadioButton>Not to be</RadioButton>
</RadioButtonList>

3.3.2 SliderScrollBar控件

WPF提供了允许从一定范围中选取一个值的控件。它们都提供了一个类似的外观和用法:显示了一个跟踪,指定了范围,以及一个可以拖动的“thumb”——用来调整值。有两个Slider控件,HorizontalSliderVerticalSlider,如图3-6所示。有两个ScrollBar控件,HorizontalScrollBarVerticalScrollBar,如图3-7所示。主要的不同是一个约定而不是功能,ScrollBar通常用于与某些滚动的可视化区域协力工作;而slider是用来调整值的。

3-6

图3-7


SliderScrollBar在使用上是非常类似的。它们都派生于一个共同的基类RangeBase。这个类提供了MinimumMaxmum属性——定义了一定范围的由控件表示的值;提供了Value属性保持当前选定的值;还定义了SmallChangeLargeiChange属性——由Value改变的多少来决定,当使用方向键调整的时候,或者是相应的PageUpPageDown键。LargeChange值还用于当slider的一部分trackthumb被点击的任何一边。

Slider控件有一个固定大小的thumb,而ScrollBar上的thumb可以在大小上改变。如果slider用于联合一个可滚动的视图,thumb的大小——相当于track,与可视化区域的大小——相对于全部可滚动区域,是成比例的。例如,thumb是大约scrollbar的长度和宽度的1/3,这就指定了1/3可滚动区域在当前视图中。

你可以通过ViewPortSize属性控制scrollbarthumb大小。无论何处这可以是从0Maximum属性值。如果ViewPortSizeMaximum相同,thumb将会填充track,并且不能移动。ViewPortSize越小,thumb也会越小。

如果你想提供一个可滚动的视图——一个更大的用户区域,你可以非常规的直接使用scrollbar控件。这通常更容易使用ScrollViewer控件。

一个ScrollViewer元素有一个单独的子元素。示例3-15使用了Ellipse元素,但是它可能是任何事情。如果你想放置多个元素在一个scrollable视图中,你可以嵌入它们在一个面板中(在第2章讨论)

         示例3-15

<ScrollViewer HorizontalScrollBarVisibility="Auto">
    
<Ellipse Fill="VerticalGradient Green DarkGreen" Height="1000" Width="2000" />
</ScrollViewer>

如果ScrollViewer的内容大于可利用的空间,ScrollViewer会提供滚动条允许用户滚动内容,如示例3-8所示。默认的,ScrollViewer提供了一个垂直滚动条,并不是一个水平滚动条。在示例3-15中,HorizontalScrollBarVisiablity属性设置为Auto,指定了水平滚动条在需要的时候添加上去。

3-8

这个Auto可见性——我们为水平滚动条所选择的,不同于默认的垂直的行为。VerticalScrollBarVisiablity默认为Visible,意味着这个滚动条总是存在无论是否需要它。

有两种确保滚动条不显示的方式。你可以设置它的可见性为Disabled(默认的水平滚动条)或者Hidden。二者的区别是,Disabled约束了ScrollViewer内容的逻辑上的大小与可利用的空间一样。Hidden允许这个逻辑上的大小是不受约束的,即使用户无法在额外的空间滚动。这可以改变确定的外观样式的行为。

为了检查这些设置如是如何影响ScrollViewer的行为,我们看一下示例3-16发生了什么,当我们改变ScrollViewer属性时。

         示例3-16

<ScrollViewer >
    
<Grid>
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition />
            
<ColumnDefinition />
            
<ColumnDefinition />
        
</Grid.ColumnDefinitions>

        
<RowDefinition Height="Auto" />

        
<Button Grid.Column="0">Stretched</Button>
        
<Button Grid.Column="1">Stretched</Button>
        
<Button Grid.Column="2">Stretched</Button>
    
</Grid>
</ScrollViewer>

这个示例显示了一个Grid,包含了3Button元素在一行中。如果Grid获得更多它需要的空间,它将伸展按钮变得比必需的更宽。如果没有充分的空间,将会裁剪按钮。如果它被取代在ScrollBarViewer中,这将可能为ScrollViewer提供更充分的实质上的、可滚动的空间,即使屏幕上的空间是不充分的。

3-9显示了示例3-16中的Grid是如何显示在一个ScrollViewer中,当这里有更多充分的空间时。显示了HorizontalScrollBarVisiablity所有的四种选项,在这四种情形中,按钮被伸展了来填充空间。

          3-9

         3-10

图3-10显示了相同的四个排列,但是带有不充分的水平空间。顶部的2ScrollViewer元素支持水平滚动,带有各自的VisibleAuto属性。正如你希望的,ScrollViewer提供了充分的空间来容纳所有的内容和允许用户在视图中的裁剪部分内滚动。在左下位置,水平的滚动条被设置为Hidden,外观行为是一样的。它排列了元素,好像有充分的空间来容纳所有的内容。唯一的不同是它并没有显示一个滚动条。在右下位置,我们可以看到,Disabled是不同于行为结果的。这里,不仅不显示滚动条,而且水平滚动条完全被Dsiabled了。Grid因此被强制裁剪按钮来适合可利用的空间。

3.3.3文本控件

WPF提供了编辑和显示文本的控件。最简单的文本编辑控件是TextBox。默认的,它允许编辑文本的单独一行;但是通过设置AcceptReturntrue,它可以编辑多行。它提供了标准的基本文本编辑机制:支持选择,系统剪切板集成(剪切、粘贴等),以及支持多重级别的Undo

示例3-17显示了2TextBox元素,一个带有默认的设置;另一个是多行的模式。它还显示了类似的PasswordBox,设计用来输入密码。图3-11显示了这些结果。正如你看到的,PasswordBox中的文本被显示为一行星号。这是惯例。为了防止密码被任何人可以在屏幕上看到。PasswordBox还拒绝复制到剪切板内容的能力。

         示例3-17

<StackPanel Orientation="Horizontal">

    
<TextBox Margin="5" VerticalAlignment="Center">Single line textbox</TextBox>

    
<TextBox AcceptsReturn="True" Margin="5"
             VerticalAlignment
="Center">Multiline textbox</TextBox>

    
<PasswordBox 
 
Margin="5" VerticalAlignment="Center" />
</StackPanel>

         3-11

TextBoxPasswordBox只支持普通的文本。它们不支持任意种类的内嵌内容——试图嵌入任何而不是普通的文本——会引起一个运行期错误。这使得它们易于在输入和编辑简单数据时使用。TextBox提供了一个Text属性,代表了一个控件的内容,作为一个字符串。

PasswordBox没有Text属性。取代的,它有一个Password属性。它返回一个SecureString类型,而不是返回一个String。它仍然只提供普通的文本值。然而,它还提供了两种机制来防止意外的泄漏密码数据。

首先,它以加密的形式储存字符串。这意味着包含字符串的内存应该被分发到系统分页文件中,它的内容是不可读取的。(.NET在运行时生成了一个随机的密钥,将它存储在一个内存位置中,这个位置是被锁定的,用来防止被修改到分页文件中)。如果使用一个正常的字符串,攻击者能够获取它的内容,通过制作一个你的系统交换文件的副本。各种各样的.NET安全API可以传递一个SecureString,意味着你的代码从不用处理解密的版本。

其次,你可以在Secure上调用Dispose,这个方法会复写密码数据。这意味着即使以其加密的形式,敏感性数据也会按需求被清除。带有一个正常的String,数据能长时间保存在内存中,直到你已经完成了使用,只会被销毁于垃圾收集器开始执行时。

意识到每当你读取Password数据的时候,都会返回一个新的SecureString,因此如果你打算利用按需清除的行为,你必须Dispose在你每次读取这个属性的时候。PasswordBox维护着它内部的字符串复制,当控件被销毁时,将字符串安排给你。

普通文本的简单的是很好的,如果你仅仅需要普通文本作为输入。然而,这样做有时是有用的——允许更加多样化的输入。WPF因此提供了RichTextBox

RichTextBox非常有弹性的,可以包括几乎任意的内容。示例3-18显示了RichTextBox的标记,包含了文本、图形、控件的混合。结果在图3-12显示。

         示例3-18

<RichTextBox>
    RichTextBox
    
<Ellipse Fill="Red" Width="100" Height="25" />
    containing
    
<Polyline Stroke="Blue" Points="0,0 10,30 20,33 30,28 40,35 50,10" />
    
<Bold>rich</Bold>
    
<Button>
        
<TextBlock>and <Italic>varied</Italic></TextBlock>
    
</Button>
    content!
</RichTextBox>

         3-12

对待非文本化的元素和对待文本的字符有同样的方式,你可以在它们的前后插入文本,或者使用剪切和粘贴来移动文本。Windows没有为用户定义一个标准的方法,来使用键盘“输入”一个椭圆,因此虽然我们能在标签中预载带有这些元素的RichTextBox,并没有为用户提供一个添加这类元素的方式。然而,写一个提供了如此便捷的程序,是相当容易的。

示例3-19显示了应用程序使用RichTextBox所用的标签。在屏幕的顶级,它提供了一个包括控件的面板,该控件允许用户添加椭圆和矩形。如图3-13所示。

         3-13


        
示例3-19

<Window x:Class="RichEditApp.Window1"
    xmlns
="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x
="http://schemas.microsoft.com/winfx/xaml/2005"
    Text
="RichEditApp"
    
>
    
<Grid>
        
<Grid.RowDefinitions>
            
<RowDefinition Height="Auto" />
            
<RowDefinition Height="*" />
        
</Grid.RowDefinitions>

        
<StackPanel Orientation="Horizontal">
            
<Label x:Name="widthLabel" Target="{Binding ElementName=widthBox}">
                
<TextBlock><AccessKey>W</AccessKey>idth:</TextBlock>
            
</Label>
            
<TextBox x:Name="widthBox">100</TextBox>
            
<Label x:Name="heightLabel" Target="{Binding ElementName=heightBox}">
                
<TextBlock><AccessKey>H</AccessKey>eight:</TextBlock>
            
</Label>
            
<TextBox x:Name="heightBox">10</TextBox>
            
<Label x:Name="fillLabel" Target="{Binding ElementName=fillBox}">
                
<TextBlock><AccessKey>F</AccessKey>ill:</TextBlock>
            
</Label>
            
<TextBox x:Name="fillBox">Red</TextBox>
            
<Label x:Name="strokeLabel" Target="{Binding ElementName=strokeBox}">
                
<TextBlock><AccessKey>S</AccessKey>troke:</TextBlock>
            
</Label>
            
<TextBox x:Name="strokeBox">Black</TextBox>
            
<Button Click="AddRectangleClick">
                
<TextBlock><AccessKey>R</AccessKey>ectangle</TextBlock>
            
</Button>
            
<Button Click="AddEllipseClick">
                
<TextBlock><AccessKey>E</AccessKey>llipse</TextBlock>
            
</Button>
        
</StackPanel>
        
<RichTextBox 
 
VerticalScrollBarVisibility="Visible" Grid.Row="1"
                     x:Name
="inputBox" Wrap="True" />

    
</Grid>
</Window>

添加了这些元素的代码是相当直接的。示例3-20是它的后台代码文件。这些代码创建了一个Shape;设置了它的WidthHeightFillStroke属性,以及将这个Shape添加到RichTextBox内容中。RichTextBox提供了一个TextSelection熟悉,指出了当前选择或没有选择的点。这段代码简单地在开始区域插入了这个Shape

         示例3-20

 

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;


namespace RichEditApp {
    
public partial class Window1 : Window {
        
public Window1(  ) {
            InitializeComponent(  );
        }


        
private void AddRectangleClick(object sender, RoutedEventArgs e) {
            Rectangle rect 
= new Rectangle(  );
            SetShapeParams(rect);
            inputBox.TextSelection.Start.InsertEmbeddedElement(rect);
        }


        
private void AddEllipseClick(object sender, RoutedEventArgs e) {
            Ellipse ellipse 
= new Ellipse(  );
            SetShapeParams(ellipse);
            inputBox.TextSelection.Start.InsertEmbeddedElement(ellipse);
        }


        
private void SetShapeParams(Shape s) {
            s.Width 
= double.Parse(widthBox.Text);
            s.Height 
= double.Parse(heightBox.Text);
            BrushConverter b 

抱歉!评论已关闭.