《深刻淺出WPF》學習總結之控件與佈局

1、控件究竟是什麼

  控件的本質是「數據+算法」——用戶輸入原始數據,算法處理原始數據並獲得結果數據。問題就在於程序如何將結果數據展現給用戶。一樣一組數據,你可使用LED陣列顯示出來,或者是以命令行模式藉助各類控制字符(如Tab)對其並輸出,但這些都不如圖形化用戶界面(Graphics User Interface ,GUI)來的友好和方便。GUI是程序界的優勝者,但在Windows上實現圖形化界面有不少中方法。每種方法又擁有本身的一套開發理念和工具。每種GUI開發與它的裏理念和工具共同組成一種方法論。常見的有:程序員

  1. Windows API (Win API):調用Windows底層繪圖函數,使用C語言,最原始也最基礎。
  2. Microsoft Foundation Class(MFC):使用C++語法將原始的Win32 API封裝成控件類。
  3. Visual Component Library(VCL):Delphi 和C++Builder使用的與MFC相近的控件類庫
  4. Vistal Basic+ActiveX控件(VB6):使用組件化的思想把Win API 封裝成UI控件,已其多語言共用
  5. Java Swing/AWT:Java SDK中用於跨平臺開發GUI程序的控件類庫
  6. Windows Form:.NET平臺上進行GUI開發的老牌勁旅,徹底組件化但須要.NET運行時支持。
  7. Windows Presentation Foundation(WPF):後起之秀,使用全新的數據驅動UI理念。

  咱們能夠對以上方法論分爲四代:算法

  1. Win API時代:函數調用+Windows消息處理。
  2. 封裝時代:使用面向對象理念把Win API 封裝成類:由來自UI的消息驅動程序處理數據。
  3. 組件化時代:使用面向組件理念在類的基礎上封裝成組件;消息被封裝成事件,變成事件驅動。
  4. WPF時代:在組件化的基礎上,使用專門的UI設計語言並引入數據驅動UI的理念。

  WPF之因此可以稱上最新的一代在於兩點:第一,以前幾代GUI方法論只能使用編程語言進行UI設計,而WPF具備專門的UI設計的XAML;第二,前幾代在UI與數據交互方面是由Windows消息到控件事件一脈相承,始終是把UI控件放在主導地位而把數據放在被動地位,用UI來驅動數據的改變,WPF在事件驅動的基礎上引入了數據驅動界面的理念,讓數據重回核心地位而讓UI迴歸數據表達者的位置編程

在從Winform轉到WPF的學習過程當中,心中必定要樹立起這樣一個概念——WPF中是數據驅動UI,數據是核心、是主動的。UI從屬於數據並表達數據、是被動的。也能夠這麼理解,當咱們想改變控件上的顯示內容時,只須要改變該控件綁定的數據源內容便可。app

  UI的功能始讓用戶觀察和操做數據,爲了讓用戶觀察數據,咱們須要用UI元素來顯示數據;爲了讓用戶操做數據,咱們須要用UI元素響應用戶的操做。WPF把那些可以展現數據、響應用戶操做的UI元素稱爲控件(Control)。控件所展現的數據,咱們稱之爲控件的「數據內容」,控件在響應用戶的操做後會執行本身的一些方法以事件(Event)的形式通知應用程序(開發人員就能夠決定如何處理這些事件),咱們稱之爲控件的「行爲」或「算法」內容。可見,WPF中的控件扮演者雙重角色、是個很是抽象的概念——Control是數據和行爲的載體,而無需具備固定的形象。換句話說,Button之因此是Button不是由於它長得方方正正、顯示一串文字而且可以響應用戶單擊,而是應該倒過來想——凡是符合「能顯示一些提示文字(能夠是文字、也能夠是圖片、動畫甚至是視頻)並能響應用戶單擊」這一抽象概念的UI元素均可以是Button,至於Button具體長成什麼樣子(是方是圓、是顯示文字仍是顯示動畫)徹底由它的風格(Style)和模板(Template)來決定。編程語言

在平常的開發工做中咱們打交道的控件大體分爲6類,即:函數

  1.   佈局控件:能夠容納多個控件或嵌套其餘佈局控件,用於在UI上組織和排列控件,Grid、StackPanel、DockPanel等控件都屬於此類,他們擁有共同的父類Panel。
  2.   內容控件:只能容納一個其餘控件或佈局控件做爲他的內容。Window、Button等控件屬於此類,由於只能容納一個控件做爲其內容,因此常常須要藉助佈局控件來規劃其內容。它們的共同父類是ContentControl。
  3.   帶標題內容控件:至關於一個內容控件,可是可加標題(Header),標題部分亦可容納一個控件或佈局。GroupBox、TabItem等是這類控件的典型表明。它們的共同父類是HeaderedContentControl
  4.   條目控件:能夠顯示一列數據,一把狀況下這列數據類型相同。此類控件包括ListBox、Combobox等。它們的共同基類是ItemsControl。此類控件在顯示集合類型數據方面功能很是強大。
  5.   帶標題條目控件:至關於在一個條目控件加上一個標題顯示區。TreeViewItem、MenuItem都屬於此類控件。這類控件每每用於顯示層級關係數據,結點顯示在其Header區域,子級節點則顯示在其條目控件區域。此類控件的共同基類是HeaderedItemsControl。
  6.   特殊內容控件:好比TextBox容納的是字符串、Textblock能夠容納可自由控制格式的文本,Image容納圖片類型數據....這類控件相對比較獨立

2、WPF的內容模型

   WPF能夠分爲以下幾類工具

名稱 註釋
ContentControl 單一內容控件
HeaderedContentControl 帶標題的單一內容控件
ItemsControl 以條目集合爲內容的控件
HeaderedItemsControl 帶標題的以條目集合爲內容的控件
Decorator    控件裝飾的元素
Panel     面板類元素
Adorner  文字點綴元素
Flow Text 流式文本元素
TextBox 文本輸入框
TextBlock 靜態文字
Shape 圖形元素

 

 

 

 

 

 

 

 

 

 

 

 

 

 


下面咱們逐一剖析這些元素的內部結構,瞭解內容與內容屬性。組件化

  咱們能夠把控件想象成一個容器,容器裏面裝的東西就是它的內容,控件的內容能夠直接是數據,也能夠是控件。當控件的內容仍是控件的時候就造成了控件的嵌套,因此WPF的UI會造成一個樹形結構。若是不考慮控件內部的組成結構,只觀察由控件組成的「樹」,那麼這棵樹稱爲「邏輯樹(Logicol Tree)」;WPF控件每每是由更基本的控件構成的。即控件自己就是一棵樹,若是連控件自己的樹也考慮在內,則這顆比邏輯樹更「繁茂」的樹稱爲可視元素樹(Visual Tree)。佈局

  控件時內存中的對象,控件的內容也是內存中的對象。控件經過本身的某個屬性引用着做爲其控件的對象,這個屬性稱爲內容屬性(Content Property)。「內容屬性」是個統稱,具體到每種控件上,內容屬性都有本身確切的名字——有的直接叫Content,有的叫Child;有些控件的內容能夠是集合,其內容屬性叫Items或Children的。學習

3、各類內容模型詳解

  咱們把符合某類內容模型的UI元素稱爲一個族,每一個族用它們共同的基類來命名

3.一、ContentControl族

本類元素的特色以下:

  1. 均派生自ContentControl類。
  2. 它們都是控件(Control)。
  3. 內容屬性的名稱爲Content。
  4. 只能由單一元素充當其內容。  

怎麼理解「只能由單一元素充當其內容」這句話呢?看下例子。

Button控件屬於ContentControl族,因此下面兩個Button代碼都是正確的——第一個Button的內容是一個靜態文字,第二個Button的內容是一張圖片。

    <StackPanel>
        <Button>
            <TextBlock>Hello World</TextBlock>
        </Button>
        <Button>
            <Image Source=".\1.jpg" Height="30" Width="30"></Image>
        </Button>
    </StackPanel>

但若是你想讓Button的內容即包含文字又包含圖片是不行的:

    <StackPanel>
        <Button>
            <TextBlock>Hello World</TextBlock>
            <Image Source=".\1.jpg" Height="30" Width="30"></Image>
        </Button>
    </StackPanel>

編譯器會報錯「對象「Button」已經具備子級且沒法添加「Image」。「Button」只能接受一個子級。」但是若是咱們真的須要一個帶圖標的Button那怎麼辦呢?咱們只須要先用一個能夠包含多個元素的佈局控件把圖片和文字包裝起來,再把這個佈局控件做爲Button的內容就行了

ContentControl族包含的控件以下

Button ButtonBase CheckBox ComboboxItem
ContentControl Frame GridViewColumnHeader GroupItem
Label ListBoxItem ListViewItem NavigationWindow
RadioButton RepeatButton ScrollViewer StatusBarItem
ToggleButton ToolTip UserControl Window

 

 

 

 

 

3.二、HeaderedContentControl族

本族元素的特色以下:

  1. 它們都派生自HeaderedContentControl類,HeaderedContentControl是ContentControl類的派生類。
  2. 它們都是控件,用於顯示帶標題的數據。
  3. 除了用於顯示主體內容的區域外,控件還具備一個顯示標題(Header)的區域。
  4. 內容屬性爲Content和Header
  5. 不管是Content仍是Header都只能容納一個元素做爲其內容。

HeaderedContentControl族包含的控件以下

Expander GroupBox HeaderedContentControl TabItem

 

 

下面這個例子是一個以圖標爲Header、以文字爲內容主體的GroupBox

    <StackPanel>
        <GroupBox Margin="10">
            <GroupBox.Header>
                <Image Source=".\1.jpg" Height="30"></Image>
            </GroupBox.Header>
            <TextBlock TextWrapping="WrapWithOverflow" Margin="10">
                願你慢慢長大,願你有好運,若是沒有,但願你在不幸中學會慈悲;願你被不少人愛,若是沒有,但願你在寂寞中學會寬容。
            </TextBlock>
        </GroupBox>
    </StackPanel>

 3.三、ItemsControl族

本族元素特色以下:

  1. 均派生自ItemsControl類。
  2. 它們都是控件,用於顯示列表化的數據。
  3. 內容屬性爲Items或ItemsSource。
  4. 每種ItemsControl都對應有本身的條目容器(Item Container)。

本族的包含控件以下所示

Menu MenuBase ContextMenu Combobox
ItemsControl ListBox ListView TabControl
TreeView Selector StatusBar  

 

 

 

 

本族控件具備特點的一點就是會自動使用條目容器對提交給它的內容進行包裝。合法的ItemsControl內容必定是個集合,當咱們把這個集合做爲內容提交給ItemsControl時,ItemsControl不會把這個集合直接拿來用,而是使用本身對應的條目容器把集合中的條目逐個包裝,而後再把包裝好的條目序列看成本身的內容。這種自動包裝的好處就是容許程序員向ItemsControl提交各類數據類型的集合,程序員在思考問題時會天然而然的感受到ItemsControl控件直接裝載着數據,若是須要進行增長、刪除、更新或者排序,那麼直接去操做數據集合就能夠,UI會自動將改變展示出來,這正體現了在WPF開發時數據直接驅動UI再進行顯示。

ListBox是典型的ItemsControl,下面將以它爲例,研究一下ItemsControl。

  首先,咱們看看ListBox的自動包裝。WPF的ListBox在顯示功能上比Windows Form或者ASP.NET的ListBox要強大的多。傳統的ListBox只能將條目以字符串的形式顯示,而WPF的ListBox除了能夠顯示中規中矩的字符串條目還能顯示更多的元素,如CheckBox、RadioButton、TextBox等,這樣一來,咱們就能製做出更加豐富的UI,代碼以下

 <ListBox>
            <CheckBox x:Name="ckBoxTim" Content="Tim"/>
            <CheckBox x:Name="ckBoxTom" Content="Tom"/>
            <CheckBox x:Name="ckBoxSimple" Content="Simple"/>
            <Button x:Name="Mess" Content="Mess"/>
            <Button x:Name="Ownen" Content="Ownen"/>
            <Button x:Name="Victor" Content="Victor"/>
        </ListBox>

運行效果以下

 表面看上去是ListBox直接包含了一些CheckBox和Button,實際並不是這樣。咱們爲Ownen這個按鈕添加一個Click事件,看看它的父容器是什麼。

        private void Ownen_Click(object sender, RoutedEventArgs e)
        {
            var invoker = sender as Button;
            var parent = VisualTreeHelper.GetParent( VisualTreeHelper.GetParent(VisualTreeHelper.GetParent(invoker)));
            MessageBox.Show(parent.GetType().ToString());
        }

 VisualTreeHelper類是幫助咱們在這顆由可視化元素構成的樹上進行導航的輔助類。咱們沿着被單擊的Button一層一層向上找,找到第三層發現它是一個ListBoxItem。ListBoxItem就是ListBox對應的Container,也就是說,不管你把什麼樣的數據集合交給ListBox,他都會以這種方式進行自動包裝。

 上面這個例子就是單純的爲了說明ItemsControl可以使用對應的Item Container自動包裝數據。實際工做中,除非列表裏的元素自始至終都是固定的咱們才使用這種直接把UI元素做爲ItemControl內容的方法,好比一年由十二個月、一週有七天等。大多數狀況下,UI上的列表會用於顯示動態的後臺數據,這時候咱們交給ItemsControl的就是程序邏輯中的數據而非控件了。

假設程序中定義有Person類:

    public class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        // ...
    }

而且有一個Person類型的集合:

var lstPerson = new List<Person>()
            {
                new Person() { Age = 20, Name = "Simple", ID = 1 },
                new Person() { Age = 22, Name = "Tim", ID = 2 },
                new Person() { Age = 22, Name = "Tom", ID = 3 },
                new Person() { Age = 22, Name = "jrrey", ID = 3 }
            };

在主程序中有一個名爲lsbPerson的ListBox,咱們只須要這樣寫:

            var lstPerson = new List<Person>()
            {
                new Person() { Age = 20, Name = "Simple", ID = 1 },
                new Person() { Age = 22, Name = "Tim", ID = 2 },
                new Person() { Age = 22, Name = "Tom", ID = 3 },
                new Person() { Age = 22, Name = "jrrey", ID = 3 }
            };
            lsbPerson.DisplayMemberPath = "Name";
            lsbPerson.SelectedValuePath = "ID";
            lsbPerson.ItemsSource = lstPerson;

   DisplayMemberPath這個屬性告訴ListBox顯示每條數據的哪一個屬性,換句話說,ListBox會去調用這個屬性的ToString()方法,把獲得的字符串放入一個TextBlock(最簡單的文本控件),而後再按前面說的辦法把TextBlock包裝進一個ListBoxItem裏。

  ListBox的SelectedValuePath屬性將與其SelectedValue屬性配合使用。當你調用SelectedValue屬性是,ListBox先找到選中的Item所對應的數據對象,而後把SelectedValuePath的值看成數據對象的屬性名稱並把這個屬性的值取出來。

  DisplayMemberPath和SelectedValuePath是兩個至關簡化的屬性。DisplayMemberPath只能顯示簡單的字符串,想用更加複雜的形式顯示數據須要使用DataTemplate。SelectedValuePath也只能返回單一的值,若是想進行一些複雜的操做,不妨直接使用ListBox的SelectedItem和SelectedItems屬性,這兩個屬性返回的就是數據集中的對象,獲得原始的數據對象後就職由程序員操做了。

  理解了ListBox的自動包裝機制,我把所有ItemsControl對應的Item Container列在下面

Items名稱 對應的Item Container
ComboBox ComboBoxItem
ContextMenu MenuItem
ListBox ListBoxItem
ListView ListViewItem
Menu MenuItem
StatusBar StatusBarItem
TabControl TabItem
TreeView TreeViewItem

 

 

 

 

 

 

 

 

 

 

 

 3.四、HeaderedItemsControl族

顧名思義,本族控件除了具備ItemsControl的特性外,還具顯示標題的能力。

本族元素的特色以下

  1. 均派生自HeaderedItemsControl類
  2. 它們都是控件,用於顯示列表化的數據,同時能夠顯示一個標題。
  3. 內容屬性爲Items、ItemsSource和Header
  4. 由於它與ItemsControl很是類似,在這就不作演示了

 3.五、Decorator族

在本族中的元素,在UI上是其裝飾做用的。如可使用Border元素爲一些組織在一塊兒的內容加個邊框。若是須要組織在一塊兒的內容可以自由縮放,則可使用ViewBox元素。

本元素的特色以下:

  1. 均派生自Decorator類。
  2. 起UI裝飾做用。
  3. 內容屬性爲Child。
  4.  只能由單一元素充當內容。

本族元素以下

ButtonChrome ClassicBorderDecorator ListBoxChrome SystemDropShadowChrome
Border InkPresenter BulletDecorator ViewBox
AdornerDectorator      

 

 

 

 

3.六、TextBlock和TextBox

  這兩個控件最主要的功能就是顯示文本。TextBlock只能顯示文本,不能編輯,因此又稱靜態文本。TextBox則容許用戶編輯其中的內容。TextBlock雖然不能編輯內容,但可使用豐富的印刷級的格式控制標記顯示專業的排版效果。

  TextBox不須要太多的顯示格式,因此它的內容是簡單的字符串,內容屬性爲Text。

  TextBlock因爲須要操縱格式,因此內容屬性是InLines(印刷中的「行」),同時TextBlock也保留一個名爲Text的屬性,當簡單的顯示一個字符串時,可使用這個屬性。

3.七、Shape族元素

  友好的界面離不開各類圖形的搭配,Shape族元素(它們只是簡單的視覺元素,不是控件)就是專門用來在UI上繪製圖形的一類元素。這類元素沒有本身的內容,咱們可使用Fill屬性爲它們設置填充效果,還可使用Stroke屬性爲它們設置邊線效果。

本族的元素特色以下:

  1. 均派生自Shape類。
  2. 用於2D圖形繪製。
  3. 無內容屬性。
  4.  使用Fill屬性設置填充,使用Stroke屬性設置邊線。

3.八、Panel族元素

  之因此把Panel元素放在最後是由於這一族控件實在是過重要了——全部用於UI佈局的元素都屬於這一族。

  本族元素的特色以下:

  1. 均派生自Panel抽象類
  2. 主要功能是控制UI佈局。
  3. 內容屬性爲Children。
  4. 內容能夠是多個元素,Panel元素將控制它們的佈局。

  對比ItemsControl和Panel元素,雖然內容均可以是多個元素,但ItemsControl強調以列表的形式來展示數據而Panel則強調對包含元素進行佈局。因此ItemsControl的內容屬性是Items和ItemsSource而Panel的內容屬性名爲Children。

本族元素以下所示

Canvas DockPanel Grid TabPanel
ToolBarOverflowPanel StackPanel ToolBarPanel UniformGrid
VirtualizingPanel VirtualizingStackPanel WrapPanel
相關文章
相關標籤/搜索