1. 程式人生 > >【WPF學習】第二十三章 列表控制元件

【WPF學習】第二十三章 列表控制元件

  WPF提供了許多封裝項的集合的控制元件,本章介紹簡單的ListBox和ComboBox控制元件,後續哈會介紹更特殊的控制元件,如ListView、TreeView和ToolBar控制元件。所有這些控制元件都繼承自ItemsControl類(ItemsControl類本身又繼承自Control類)。

  ItemsControl類添加了所有基於列表的控制元件都使用的基本功能。最顯著的是,它提供了填充列表項的兩種方式。最直接的方法是程式碼或XAML將列表項直接新增到Items集合中。然而,在WPF中使用資料繫結的方法更普遍。使用資料繫結方法,需將ItemsSource屬性設定為希望顯示的具有資料項集合的物件(後續章節介紹與列表資料繫結相關的更多內容)。

  ItemsControl類之後的繼承橙子有些混亂。一個主要分支是選擇器(selector),包括ListBox、ComboBox以及TabControl。這些控制元件都繼承自Selector類,並且都具有跟蹤當前選擇項(SelectedItem)或其位置(SelectedIndex)的屬性,封裝列表項的控制元件是另一個分支,以不同方式選擇列表項。該分支包括用於選單、工具欄以及樹的類——所有這些類都屬於ItemsControl,但不是選擇器。

  為了充分發揮所有ItemsControl控制元件的功能,需要使用資料繫結。即使不從資料庫甚至外部資料來源獲取資料,也同樣如此。WPF資料繫結非常普遍,可使用各種資料,包括自定義的資料物件和集合。但現在還不需要考慮資料繫結的細節。現在,先快速瀏覽一下ListBox控制元件和ComboBox控制元件。

一、ListBox

  ListBox類代表了一種最常用的Windows設計——允許使用者從長度可變的列表中選擇一項。

  如果將SelectionMode屬性設定為Multiple或Extended,ListBox類還允許選擇多項。在Multiple模式下,可通過單擊項進行選擇或取消選擇。在Extended模式下,需要按下Ctrl鍵選擇其他項,或按下Shift鍵選擇某個選項範圍。在這兩種多選模式下,可用SelectedItems集合替代SelectedItem屬性來獲取所有選擇的項。

  為向ListBox控制元件中新增項,可在ListBox元素中巢狀ListBoxItem元素。例如,下面是一個包含顏色列表的ListBox:

<ListBox>
     <ListBoxItem>Yellow</ListBoxItem>
     <ListBoxItem>Blue</ListBoxItem>
     <ListBoxItem>Green</ListBoxItem>
     <ListBoxItem>Red</ListBoxItem>
     <ListBoxItem>LightBlue</ListBoxItem>
     <ListBoxItem>Black</ListBoxItem>
</ListBox>

  不同控制元件採用不同方式處理巢狀的內容。ListBox控制元件在它的Items集合中儲存每個巢狀的物件。

  ListBox控制元件是一個非常靈活的控制元件。它不僅可以包含ListBoxItem物件,也可以駐留其他任意元素。這是因為ListBoxItem類繼承自ContentControl類,從而ListBoxItem能夠包含一段巢狀的內容。如果該內容繼承自UIElement類,它將ListBox控制元件中呈現出來。如果是其他型別的物件,ListBoxItem物件會呼叫ToString()方法並顯示最終的文字。

  例如,如果決定建立一個包含影象的列表,可使用如下標記:

<ListBox>
        <ListBoxItem>
            <Image Source="happyface.jpg"></Image>
        </ListBoxItem>
        <ListBoxItem>
            <Image Source="redx.jpg"></Image>
        </ListBoxItem>
</ListBox>

  實際上ListBox控制元件足夠職能,它能隱式地建立所需的ListBoxItem物件。這意味著可直接在ListBox元素中放置物件。廈門市一個更復雜的示例,該示例使用巢狀的StackPanel物件組合文字和影象內容:

<Window x:Class="Controls.ImageList"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ImageList" Height="300" Width="300">
    <ListBox Margin="5" SelectionMode="Multiple" Name="lst" SelectionChanged="lst_SelectionChanged">
        <StackPanel Orientation="Horizontal">
            <Image Source="happyface.jpg" Width="30" Height="30"></Image>
            <Label VerticalAlignment="Center">A Happy face</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="redx.jpg" Width="30" Height="30"></Image>
            <Label VerticalAlignment="Center">A Warning face</Label>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Source="happyface.jpg" Width="30" Height="30"></Image>
            <Label VerticalAlignment="Center">A Happy face</Label>
        </StackPanel>
    </ListBox>
</Window>
ImageListBox

  在該例中,StackPanel面板變成被ListBoxItem封裝的項。該標記建立的富列表如圖下圖所示:

 

   利用在列表框中的巢狀任意元素的能力,可創建出各種基於列表的控制元件,而不必使用其他類。例如,Windows窗體的哦該凝聚項中CheckedListBox類,該類顯示在每個項的旁邊都具有複選框的列表。在WPF中不需要這一特殊類,因為完全可使用標準的ListBox控制元件快速構建相同的效果:

<Window x:Class="Controls.CheckBoxList"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="CheckBoxList" Height="300" Width="300">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>

        <ListBox Name="lst" SelectionChanged="lst_SelectionChanged"
             CheckBox.Click="lst_SelectionChanged" >
            <CheckBox Margin="3">Option 1</CheckBox>
            <CheckBox Margin="3">Option 2</CheckBox>
            <CheckBox Margin="3">Option 3</CheckBox>
        </ListBox>
        <StackPanel Grid.Row="1" Margin="0,10,0,0">
            <TextBlock>Current selection:</TextBlock>
            <TextBlock  Name="txtSelection" TextWrapping="Wrap"></TextBlock>
            <Button Margin="0,10,0,0" Click="cmd_CheckAllItems">Examine All Items</Button>
        </StackPanel>
    </Grid>
</Window>
CheckBoxList

  當在列表內部使用不同元素時需要注意一點。當讀取SelectedItem值時(以及SelectedItems和Items集合),看不到ListBoxItem物件——反而將看到放入到列表中的物件。在CheckedListBox示例中,這意味著SelectedItem提供了CheckBox物件。

  例如,下面是一些響應SelectionChanged事件觸發的程式碼。這些程式碼獲取當天選中CheckBox物件並顯示該項是否被選中:

private void lst_SelectionChanged(object sender, RoutedEventArgs e)
        {
            // Select when checkbox portion is clicked (optional).
            if (e.OriginalSource is CheckBox)
            {
                lst.SelectedItem = e.OriginalSource;
            }

            if (lst.SelectedItem == null) return;
            txtSelection.Text = String.Format(
                "You chose item at position {0}.\r\nChecked state is {1}.",
                lst.SelectedIndex,
                ((CheckBox)lst.SelectedItem).IsChecked);
        }

  在下面的程式碼片段中,類似的程式碼遍歷選項集合以確定哪一項被選中(對於使用複選框的多項選擇列表,可以編寫類似的程式碼來遍歷選中項的集合)。

private void cmd_CheckAllItems(object sender, RoutedEventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            foreach (CheckBox item in lst.Items)
            {
                if (item.IsChecked == true)
                {
                    sb.Append(
                        item.Content + " is checked.");
                    sb.Append("\r\n");
                }
            }
            txtSelection.Text = sb.ToString();
        }

  最終效果如下所示:

 

   在列表框中手動放置項時,由你決定是希望直接插入項還是在ListBoxItem物件中明確地包含每項。第二種方法通常更清晰,也更繁瑣。最重要的考慮事項是一致性。例如,如果在列表中放置StackPanel物件,ListBox.SelectedItem物件將是StackPanel。如果放置由ListBoxItem物件封裝的StackPanel物件,ListBox.SelectedItem物件將是ListBoxItem,所以可進行響應編碼。

  ListBoxItem提供了少許額外功能,通過這些功能可得到直接巢狀的物件。換言之,ListBoxItem定義了可以讀取(或設定)的IsSelected屬性,以及用於通知何時選中的Selected和Unselected事件。然而,可使用ListBox類的成員得到類似功能,如SelectedItem屬性(或SelectedItems屬性)以及SelectionChanged事件。

  有趣的是,當使用巢狀物件方法時,有一項技術可為特定的物件檢索ListBoxItem封裝器。技巧是使用常被忽視的ContainerFromElement()方法。下面的程式碼使用該技術檢查列表中的第一個條目是否被選中:

ListBoxItem item=(ListBoxItem)lst.ContainerFromElement((DependencyObject)lst.SelectedItems[0]);
MessageBox.Show("IsSelected:"+item.IsSelected.ToString());

二、ComboBox

  ComboBox控制元件和ListBox控制元件類似。該控制元件包含ComboBoxItem物件的集合,既可以顯示地也可以隱式地建立該集合。與ListBoxItem類似,ComboBoxItem也是可以包含任意巢狀元素的內容控制元件。

  ComboBox類和ListBox類之間的重要區別是他們在視窗中呈現自身的方式。Combox控制元件使用下拉列表,這意味著一次只能選擇一項。

  如果希望允許使用者在組合框中通過輸入文字選項一項,就必須將IsEditable屬性設定為true,並且必須確保選項集合中儲存的是普遍的純文字的ComboBoxItem物件,或是提供了有意義的ToString()表示的物件。例如,如果使用Image物件填充可編輯的組合框,那麼在上面顯示的文字將只有Image類的全名,這用處不大。

  ComboBox控制元件的侷限之一在於當使用自動改變尺寸功能時該控制元件改變自身尺寸的方法。ComboBox控制元件加寬自身以適應它的內容,這意味著當從一項移到另一項是它會改變自身大小。但沒有簡便的方法告訴ComboBox控制元件使用包含項的最大尺寸。相反,需要為Width屬性提供硬編碼的值,而這並不理想。

下面是一個簡單的示例:

<Window x:Class="Controls.ComboBoxTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ComboBoxTest" Height="300" Width="300">
    <StackPanel>
        <ComboBox Margin="5">
            <StackPanel Orientation="Horizontal">
                <Image Source="happyface.jpg" Width="30" Height="30"></Image>
                <Label VerticalAlignment="Center">A Happy face</Label>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Image Source="redx.jpg" Width="30" Height="30"></Image>
                <Label VerticalAlignment="Center">A Warning face</Label>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Image Source="happyface.jpg" Width="30" Height="30"></Image>
                <Label VerticalAlignment="Center">A Happy face</Label>
            </StackPanel>
        </ComboBox>
    </StackPanel>
</Window>
ComboBoxTest

  最終效果圖如下所示: