程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 《Programming WPF》翻譯 第3章 3.內嵌控件

《Programming WPF》翻譯 第3章 3.內嵌控件

編輯:關於.NET

WPF提供了一系列內嵌控件。其中大多數符合標准的你已經熟悉的Windows控 件類型。注意到沒有一個是包裝在舊的Win32控件外面的控件。雖然它們看上去 就像是它們的副本,它們都是與生俱來的WPF控件。這意味著它們為WPF在本書中 描述的功能提供了完全的支持,包括樣式、獨立的分辨率、數據綁定、合成、以 及充分的集成支持WPF的圖形化能力。

3.3.1按鈕

按鈕是用戶可以點擊的控件。點擊的結果由應有程序的開發者勝任,但是共 同的期望依賴於按鈕的類型。例如,點擊一個用來表示選擇的CheckBox或 RadioButton,並未正常擁有任何即時的效果來真實反映那個選擇。與之對比, 點擊一個正常的按鈕,通常會有即時的效果。

使用按鈕是直接的。示例3-11顯示了按鈕元素的標記。

示例3-11

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

元素的內容(這種情形下是“Button”文字)用於按鈕的標題。點擊事件的 句柄通過一個屬性明確地指定。這表明了xaml的後台代碼必須包含一個在標簽中 明確指定名稱的方法,正如示例3-12所示(當然我們還可以附屬事件句柄,通過 給按鈕一個x:Name,以及使用正常的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基類,RadioButton和CheckBox通過 ToggleButton類間接派生於這個基類。這個結基類定義了一個IsChecked屬性, 指出了用戶是否檢查了按鈕。

Radio按鈕正常使用於組中,其中每次只能選擇一個按鈕。使用 RadioButtonList元素來指出一組radio按鈕作為一個組,正如示例3-14所示。

示例3-14

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

3.3.2 Slider和ScrollBar控件

WPF提供了允許從一定范圍中選取一個值的控件。它們都提供了一個類似的外 觀和用法:顯示了一個跟蹤,指定了范圍,以及一個可以拖動的“thumb”—— 用來調整值。有兩個Slider控件,HorizontalSlider和VerticalSlider,如圖3 -6所示。有兩個ScrollBar控件,HorizontalScrollBar和VerticalScrollBar, 如圖3-7所示。主要的不同是一個約定而不是功能,ScrollBar通常用於與某些滾 動的可視化區域協力工作;而slider是用來調整值的。

圖3-6

圖3-7

Slider和ScrollBar在使用上是非常類似的。它們都派生於一個共同的基類 RangeBase。這個類提供了Minimum和Maxmum屬性——定義了一定范圍的由控件表 示的值;提供了Value屬性保持當前選定的值;還定義了SmallChange和 LargeiChange屬性——由Value改變的多少來決定,當使用方向鍵調整的時候, 或者是相應的PageUp和PageDown鍵。LargeChange值還用於當slider的一部分 track在thumb被點擊的任何一邊。

Slider控件有一個固定大小的thumb,而ScrollBar上的thumb可以在大小上改 變。如果slider用於聯合一個可滾動的視圖,thumb的大小——相當於track,與 可視化區域的大小——相對於全部可滾動區域,是成比例的。例如,thumb是大 約scrollbar的長度和寬度的1/3,這就指定了1/3可滾動區域在當前視圖中。

你可以通過ViewPortSize屬性控制scrollbar的thumb大小。無論何處這可以 是從0到Maximum屬性值。如果ViewPortSize與Maximum相同,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,包含了3個Button元素在一行中。如果Grid獲得更 多它需要的空間,它將伸展按鈕變得比必需的更寬。如果沒有充分的空間,將會 裁剪按鈕。如果它被取代在ScrollBarViewer中,這將可能為ScrollViewer提供 更充分的實質上的、可滾動的空間,即使屏幕上的空間是不充分的。

圖3-9顯示了示例3-16中的Grid是如何顯示在一個ScrollViewer中,當這裡有 更多充分的空間時。顯示了HorizontalScrollBarVisiablity所有的四種選項, 在這四種情形中,按鈕被伸展了來填充空間。

圖3-9

圖3-10

圖3-10顯示了相同的四個排列,但是帶有不充分的水平空間。頂部的2個 ScrollViewer元素支持水平滾動,帶有各自的Visible和Auto屬性。正如你希望 的,ScrollViewer提供了充分的空間來容納所有的內容和允許用戶在視圖中的裁 剪部分內滾動。在左下位置,水平的滾動條被設置為Hidden,外觀行為是一樣的 。它排列了元素,好像有充分的空間來容納所有的內容。唯一的不同是它並沒有 顯示一個滾動條。在右下位置,我們可以看到,Disabled是不同於行為結果的。 這裡,不僅不顯示滾動條,而且水平滾動條完全被Dsiabled了。Grid因此被強制 裁剪按鈕來適合可利用的空間。

3.3.3 文本控件

WPF提供了編輯和顯示文本的控件。最簡單的文本編輯控件是TextBox。默認 的,它允許編輯文本的單獨一行;但是通過設置AcceptReturn為true,它可以編 輯多行。它提供了標准的基本文本編輯機制:支持選擇,系統剪切板集成(剪切 、粘貼等),以及支持多重級別的Undo。

示例3-17顯示了2中TextBox元素,一個帶有默認的設置;另一個是多行的模 式。它還顯示了類似的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

TextBox和PasswordBox只支持普通的文本。它們不支持任意種類的內嵌內容 ——試圖嵌入任何而不是普通的文本——會引起一個運行期錯誤。這使得它們易 於在輸入和編輯簡單數據時使用。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:</TextBlo ck>
            </Label>
            <TextBox x:Name="widthBox">100</TextBox>
            <Label x:Name="heightLabel" Target="{Binding ElementName=heightBox}">
                <TextBlock><AccessKey>H</AccessKey>eight:</TextBl ock>
            </Label>
            <TextBox x:Name="heightBox">10</TextBox>
            <Label x:Name="fillLabel" Target="{Binding ElementName=fillBox}">
                <TextBlock><AccessKey>F</AccessKey>ill:</TextBloc k>
            </Label>
            <TextBox x:Name="fillBox">Red</TextBox>
            <Label x:Name="strokeLabel" Target="{Binding ElementName=strokeBox}">
                <TextBlock><AccessKey>S</AccessKey>troke:</TextBl ock>
            </Label>
            <TextBox x:Name="strokeBox">Black</TextBox>
            <Button Click="AddRectangleClick">
                <TextBlock><AccessKey>R</AccessKey>ectangle</Text Block>
            </Button>
            <Button Click="AddEllipseClick">
                <TextBlock><AccessKey>E</AccessKey>llipse</TextBl ock>
            </Button>
        </StackPanel>
        <RichTextBox
 VerticalScrollBarVisibility="Visible" Grid.Row="1"
                     x:Name="inputBox" Wrap="True" />

    </Grid>
</Window>

添加了這些元素的代碼是相當直接的。示例3-20是它的後台代碼文件。這些 代碼創建了一個Shape;設置了它的Width、Height、Fill和Stroke屬性,以及將 這個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 = new BrushConverter(  );
            if (fillBox.Text.Length > 0) {
                s.Fill = (Brush) b.ConvertFromString (fillBox.Text);
            }
            if (strokeBox.Text.Length > 0) {
                s.Stroke = (Brush) b.ConvertFromString (strokeBox.Text);
            }
        }
    }
}

3.3.4標簽

在前面的章節,示例3-19使用了Label控件。它典型地應用於為控件提供一個 標題,該控件沒有其自身的內嵌標題。最普通的示例是TextBox。Label看起來可 能是多余的,由於達到了同樣的可視化效果而不用一個完整的控件;你可以只使 用底層的TextBlock元素。然而,Label有一個重要的處理焦點的職責。

設計良好的用戶界面應該是易於鍵盤使用的。一種通常的達到目的的方式是 提供訪問鍵。訪問鍵是一個關聯到控件的字母,從而一旦你按住了Alt鍵,同時 按住訪問鍵,這個行為與你點擊當前控件是一樣的:當按下Alt鍵的時候,通過 在控件的標題中給相應的字母加下劃線,應用程序把它們的訪問鍵告訴用戶。( 你也可以配置Windows來一直顯示訪問鍵的下劃線,無論是否按下Alt鍵)。示例 3-19中使用了內部的AccessKey元素,為我們管理這個下劃線。

圖3-14顯示了示例3-9中用戶界面一部分的特寫,顯示了訪問鍵下劃線,正如 用戶會看到的,如果當他按下Alt鍵的時候。只是一個Button,並不需要特殊的 代碼,就能使訪問鍵工作。所有你需要做的是,使用AccessKey內嵌的修改器, 在控件的標記之中。在這種情形中,使用Alt+R或Alt+E將自動的得到相同的效果 ,如同點擊矩形的或橢圓形的按鈕。

圖3-14

TextBox面臨著比較多的挑戰。他們有一個標題,只有顯示文本而且這些文本 還是可以編輯的。這個標題由一個獨立的元素提供在TextBox的左邊——這裡引 進了一個Label。Label控件的意圖是為了提供放置標題的地方,同時帶有 AccessKey元素。當訪問鍵按下的時候,這個Label將會重定向輸入到相關的控件 ,這裡是一個TextBox。

Label是如何知道重定向訪問鍵到哪一個控件的呢?Label有一個Target屬性 ,指出了訪問鍵的已有目標。我們使用綁定表達式來連接label到它的目標。綁 定表達式細節將在第4章討論。這裡的表達式簡單地設置了Target屬性到相關的 命名元素。

3.3.5 Selectors

一些類型的控件運行用戶從一組項目中進行選擇。在某些情形中,如 RadioButtonList和TabControl,經常只有一個選擇項。ComboBox擴展了這個功 能——沒有當前的選項。ListBox                   則更 加進一步的,具有同時選擇多個選項的能力。所有的這些控件從Selector基類繼 承了共同的列表和選擇功能。

最簡單的使用這些控件的方式是將內容添加到它們的Item屬性。示例3-21顯 示了ComboBox標簽,各種元素被添加到它的Item中。

示例3-21

<ComboBox>
    <ComboBox.Items>
        <Button>Click!</Button>
        <TextBlock>Hello, world</TextBlock>
        <StackPanel Orientation="Horizontal">
            <TextBlock>Ellipse:</TextBlock><Ellipse Fill="Blue" Width="100" />
        </StackPanel>
    </ComboBox.Items>
</ComboBox>

同樣的技術還可以使用在ListBox、TabControl和RedioButtonList中,正如 你在圖3-15中看到的,每一個控件以它們自己的方式代表項。RadioButtonList 為每個項生成一個RadioButton,使用這些項作為標題。TabControl把每個元素 放進它自己的TabControl中,為了以其自己的tab頁面來表示。(圖3-15只顯示 了第一項,但是另外聯合的三個貫穿3個tab頭)。

圖3-15

所有的Selector控件包裝了我們所有的項,為了以一種合適的方式表示它們 。雖然這有可能是便利的,在某種情形中,你可能想要更多的控件。例如,顯示 在上面的TabControl控件,並不是特別的有用。它包裝了我們的項,帶著沒有標 題的tab。為了修復這個問題,我們簡單地提供了我們自己的TabItem控件,取代 以讓TabControl元素為我們生這些項。我們可以接著設置Header屬性從而控制 tab頁的標題。這些技術在示例3-22中說明。

示例3-22

<TabControl Grid.Row="3" Grid.Column="1">
    <TabControl.Items>

        <TabItem Header="Button">
            <Button>Click!</Button>
        </TabItem>

        <TabItem>
            <TabItem.Header>
                <TextBlock FontSize="18"
                    FontFamily="Palatino Linotype">Text</TextBlock>
            </TabItem.Header>
            <TextBlock>Hello, world</TextBlock>
        </TabItem>

        <TabItem>
            <TabItem.Header>
                <Ellipse Fill="Blue" Width="30" Height="20" />
            </TabItem.Header>

             <StackPanel Orientation="Horizontal">
                <TextBlock>Ellipse:</TextBlock>
                <Ellipse Fill="Blue" Width="100" />
            </StackPanel>
        </TabItem>

    </TabControl.Items>
</TabControl>

示例3-22顯示了TabControl標簽,帶有和前面相同的三個選項,但是這次是 顯示的指定TabItem元素。在其中的第一項,Header屬性被設置為文本“button ”。另外兩項解釋了這些頭部支持內嵌內容。第一項使用TextBlock控制文本外 觀;第二項把一個Ellipse放入頭部,取代了文本。圖3-16顯示了這個結果。

圖3-16

提供一組固定的元素貫穿Item屬性,對tab頁和RadioButton來說是有意義的 ,在你設計用戶界面的時候,你想知道需要什麼元素。但是這可能不適合 ComboBox和列表。為了支持你在運行時決定要顯示哪一個選項,所有Selector控 件提供了一個折中的方法來導入列表:數據綁定。替代以使用Items,你可以提 供一個帶有ItemSource屬性的數據源對象,以及使用數據樣式來決定元素如何顯 示。這些技術將在第4章和第5章介紹。

不管你是否使用了一組項或一個綁帶數據源,你總是可以查找出選中的元素 什麼時候有改動——通過處理SelectionChanged事件。你可以接著使用 SelectedIndex或者SelectedItem屬性來查找當前選中了哪一個元素。

3.3.6菜單

很多Windows應用程序通過層級菜單,提供了訪問他們功能的方法。這些典型 的表現為頂級窗口的主菜單和彈出式“上下文”菜單。WPF提供了兩種菜單控件 :Menu是永久性可見的菜單(如主菜單);ContextMenu是上下文菜單。

#如今Windows中的菜單被典型的視為不同於其他用戶界面的元素。這是一個 特殊的句柄類型。在Windows窗體中,大多數可見的元素派生於Control基類,但 菜單不是。這意味著菜單趨向於有點非彈性的。為了避免它的缺點,一些用戶界 面工具選擇不用這些Windows內嵌的簡單菜單處理。在WPF中,菜單只是一個普通 的控件,因此它們不需要特殊的樣式和約束。

這兩種類型的菜單都以同樣的方式生成。它們的內容組成了層級的MenuItem 元素。示例3-23創建了一個典型地菜單。結果如圖3-17所示。

圖3-17

示例3-23

<Menu>
    <MenuItem Header="File">
        <MenuItem Header="New" />
        <MenuItem Header="Open" />
        <MenuItem Header="Save" />
        <MenuItem Header="Save As" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Page Setup" />
        <MenuItem Header="Print" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Exit" />
    </MenuItem>
    <MenuItem Header="Edit">
        <MenuItem Header="Undo" />
        <MenuItem Header="Redo" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Cut" />
        <MenuItem Header="Copy" />
        <MenuItem Header="Paste" />
        <MenuItem Header="Delete" />
        <MenuItem Mode="Separator" />
        <MenuItem Header="Select All" />
    </MenuItem>
    <MenuItem Header="Help">
        <MenuItem Header="Help Topics" />
        <MenuItem Header="About" />
    </MenuItem>
</Menu>

上下文菜單使用於一種非常簡單的方式。主要的不同是外觀。Menu在頂部有 一個水平條,事實上一個Menu可以在UI的任何地方使用,而ContextMenu只能用 作一個元素ContextMenu屬性值。示例3-24顯示了一個帶有ContextMenu的Grid元 素。

示例3-24

<Grid>
    <Grid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Foo" />
            <MenuItem Header="Bar" />
        </ContextMenu>
    </Grid.ContextMenu>

</Grid>

帶有這個在適當位置的上下文菜單,在Grid上的任何地方右擊都會產生一個 上下文菜單。圖3-18顯示了這個在活動中的上下文菜單。

圖3-18

每個MenuItem都有一個Header屬性。作為Menu的子一級,這個header決定了顯示 在菜單條上的label。為了一個MenuItem——內嵌在ContextMenu或另一個 MenuItem中,Header包括了菜單線的內容。這個Header熟悉允許普通文本,如示 例3-23,或者內嵌內容。示例3-25顯示了一個菜單項目的修改後版本,暴露了添 加結構的能力,從而標記了一個字母作為訪問鍵。圖3-19顯示了結果。

示例3-25

<MenuItem>
<MenuItem.Header>
<TextBlock>
<AccessKey>N</AccessKey>ew
</TextBlock>
</MenuItem.Header>
</MenuItem>

圖3-19

示例3-23的菜單並沒有做任何有意義的事情,因為沒有明確的事件句柄或命 令。有兩種方式使你可以鉤住MenuItem到一些代碼。你可以處理它的Click事件 ——就像處理一個按鈕的Click事件。折中地,你可以在MenuItem上設置Command 屬性,使用相同的語法——如我們在示例3-8所見。

示例3-26顯示了一個Edit子菜單修改後的版本,菜單項都聯合到相關的標准 命令上。只要焦點落在一個控件上,如TetBox或RichTextBox——理解這些標准 命令;這些標准命令將會被處理而無需任何顯示的代碼。(如果焦點不在這樣的 控件上,這些命令會簡單地向上冒泡。如果沒有處理這些命令的事物,就會忽略 它們。)

示例3-26

<MenuItem Header="Edit">
<MenuItem Header="Undo" Command="Undo" />
<MenuItem Header="Redo" Command="Redo"/>
<MenuItem Mode="Separator" />
<MenuItem Header="Cut" Command="Cut" />
<MenuItem Header="Copy" Command="Copy" />
<MenuItem Header="Paste" Command="Paste" />
<MenuItem Header="Delete" Command="Delete" />
<MenuItem Mode="Separator" />
<MenuItem Header="Select All" Command="SelectAll" />
</MenuItem>

菜單經常還有一個快捷鍵以作為一個加速器。加速器只在菜單打開的時候工 作。一個快捷鍵,如Ctrl+S用於保存,無論菜單是否打開,都會工作。這個菜單 不負責綁定控件快捷鍵到鍵-表示,正如我們先前看到的。我們這麼做使用了 CommandBindings,這就聯合了命令輸入。然而,菜單按照慣例地展示了快捷鍵 ,從而有助於用戶發現它們。

一旦一個菜單的Command有一個聯合的鍵-表示的綁定,WPF會自動地在菜單中 展示這個快捷鍵。由於示例33-26使用了標准的剪切板和undo/redo命令,其產生 的菜單就有聯合的快捷鍵,正如你在圖3-20所看到的。

圖3-20

一旦由於某些原因,你選擇不使用WPF的基本命令系統,你仍然 可以展示一個快捷鍵。MenuItem提供了一個InputGestureText屬性,讓你選擇要 出現的文本在正常的位置,作為這樣一個快捷鍵。示例3-27顯示了一個菜單項同 時具有快捷鍵和訪問鍵。

示例3-27

<MenuItem InputGestureText="Ctrl+N">
<MenuItem.Header>
<TextBlock><AccessKey>N</AccessKey>ew</TextBlock& gt;
</MenuItem.Header>
</MenuItem>

注意到,Menu和ContextMenu都間接派生於ItemControl,同一個基類作為所 有的Selector控件。這意味著你可以使用ItemDataSource屬性來導入使用了數據 綁定的菜單,而不是使用固定的內容。這將是有用的——如果你想要制作自己的 可配置菜單結構。參見第4章獲取更多如何使用數據綁定的細節。

3.3.7 工具欄

大多數Windows應用程序在提供菜單的同時,還提供了工具欄。工具欄為頻繁 使用的操作提供了更快速的訪問,因為用戶不需要通過菜單系統進行導航,而工 具欄在屏幕上一直是可見的,如示例3-21所示。

圖3-21

WPF通過ToobarTray和ToolBar支持工具欄。 ToobarTray提供了一個容器,你可以向其中添加多個TooBar元素。示例3-28顯示 了一個簡單的例子,這為圖3-21提供了標記。

示例3-28

<ToolBarTray Grid.Row="1" Margin="2">
<ToolBar>
<Button>
<Canvas Width="16" Height="16">
<Polygon Stroke="Black" StrokeThickness="0.5"
Fill="LinearGradient 1,1 0.2,0.7 #AAA White"
Points="3,1 10,1 13,5 13,15 3,15" />
<Polygon Stroke="Black" Fill="DarkGray" StrokeThickness="0.5"
StrokeLineJoin="Bevel" Points="10,1 10,5 13,5" />
</Canvas>
</Button>
<Button>
<Canvas Width="16" Height="16">
<Polygon Stroke="Black" StrokeThickness="0.5"
Fill="VerticalGradient Khaki Beige"
Points="1,15 1,4 5.5,4 7.5,6 12,6 12,15" />
<Polygon Stroke="Black" Fill="VerticalGradient Khaki #DB7"
StrokeThickness="0.5"
Points="1.5,15 4,8 15,8 12,15" />
<Path Stroke="Blue" StrokeThickness="1"
Data="M 8,2 C 9,1 12,1 14,3" />
<Polygon Fill="Blue" Points="15,1 15.5,4.5 12,4" />
</Canvas>
</Button>
</ToolBar>
</ToolBarTray>

這只創建了一個工具欄,帶有一對按鈕。當你添加一個按鈕到工具欄的時候 ,它的大小默認為17X17合理的像素。如果內容太大,就會被裁減掉。這是因為 工具欄按鈕通常被希望為圖片而不是文本。工具欄設置所有的按鈕為同樣的大小 ,以確保一致的間隔空間。如果你提供的內容比17X17小,這會被居中設置。( 如果你想要一個更大的按鈕,你可以顯示地設置大小;17X17是簡單的默認值。 )

在這個例子裡,我們使用了一些簡單的向量圖,用來繪制通常的圖標如New和 Open等等。這裡使用的圖形化元素在第7章中更詳細的描述。實際上,你可以很 少地放置像這樣的內嵌的圖形。通常你可以希望圖畫是簡單引用到的資源,通過 工具欄上的按鈕。看第6章獲取更多細節。

由於工具欄上的按鈕只是正常的Button元素,帶有指定的可視化,關於它們 的行為,沒有什麼尤其特殊之處。工具欄很少只提供一個特定的方式排列和呈現 控件。你也可以添加其他元素到工具欄,如TextBox或ComboBox。這些將連同按 鈕一起排列在工具欄上。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved