WPF控件和布局
WPF控件和布局,根據(jù)劉鐵猛《深入淺出WPF》書籍講解內(nèi)容,主要記錄控件和布局的原理,如果有不足的地方,請大牛們鍵盤下留情--輕噴!如果還算有用,請給點動力,支持一把!
一、WPF里的控件
1.1 控件的實質(zhì)
我們先從UI上分析,UI的功能是讓用戶觀察和操作數(shù)據(jù),為了能顯示數(shù)據(jù)和響應(yīng)用戶的操作通知程序(通過事件來通知,如何處理事件又是一系列的算法),所以控件就是顯示數(shù)據(jù)和響應(yīng)用戶操作的UI元素,也即:控件就是數(shù)據(jù)和行為的載體。?
1.2 WPF中的一個重要概念--數(shù)據(jù)驅(qū)動UI
什么是數(shù)據(jù)驅(qū)動UI呢?我們知道傳統(tǒng)的GUI界面都是由windows消息通過事件傳遞給程序,程序根據(jù)不同的操作來表達出不同的數(shù)據(jù)體現(xiàn)在UI界面上,這樣數(shù)據(jù)在某種程度上來說,受到很大的限制。WPF中是數(shù)據(jù)驅(qū)動UI,數(shù)據(jù)是核心,處于主動的,UI從屬于數(shù)據(jù)并表達數(shù)據(jù),是被動的。因為以后的章節(jié)會重點介紹,在此不做過多的說明,只要記著,WPF數(shù)據(jù)第一,控件第二。
1.3 WPF中控件的知多少
雖然控件沒有數(shù)據(jù)重要,但是還是比較重要的,畢竟是門面啊,只是在數(shù)據(jù)面前,它比較"有禮貌"。控件有很多,但是如果仔細去分析,也是有規(guī)律可循的,根據(jù)其作用,我們可以把控件分為6類:
- 布局控件:是可以容納多個控件或者嵌套其他布局的控件,用于在UI上組織和排列控件。其父類為Panel。
- 內(nèi)容控件:只能容納一個控件或者布局控件作為他的內(nèi)容。所以經(jīng)常借助布局控件來規(guī)劃其內(nèi)容。其父類為ContentControl。
- 帶標題內(nèi)容控件:相當(dāng)于一個內(nèi)容控件,但是可以加一個標題,標題部分也可以容納一個控件或者布局,其父類為HeaderedContentControl。
- 條目控件:可以顯示一列數(shù)據(jù),一般情況下,是數(shù)據(jù)的類型是相同的。其共同的基類為ItemsControl。
- 帶標題的條目控件:和上面的帶標題內(nèi)容控件類同,其基類為HeaderdeItemsControl。
- 特殊內(nèi)容控件:這類控件比較獨立,但也比較常用,如TextBox,TextBlock,Image等(由于其常用性和相對比較簡單,本篇筆記不做說明)。
上面的控件的派生關(guān)系如圖1:
圖1
二、各類控件模型詳解
2.1 WPF中的內(nèi)容模型
為了理解各個控件的模型,還是先了解一下WPF中的內(nèi)容模型。在上述各類控件里,至少可以容納一個內(nèi)容,主要原因是由于每個控件對象都會有一個重要又不常寫出來的屬性--Content Property(有Content,Child,Items,Children幾個屬性,如Grid可以容納多個控件,用的是Children)。內(nèi)容模型就是每一族的控件都含有一個或者多個元素作為其內(nèi)容(其下面的元素可能是其他控件)。為什么可以不常寫出來呢?先讓我們看下面兩段代碼:
XAML Code <Window x:Class="Chapter_03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="內(nèi)容屬性測試" Height="350" Width="525"><Grid><Grid.Children><Button Content="1" Margin="120,146,0,146" HorizontalAlignment="Left" Width="82" /><Button Content="2" x:Name="btn2" Margin="0,146,142,145" HorizontalAlignment="Right" Width="82" /></Grid.Children></Grid> </Window> XAML <Window x:Class="Chapter_03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="內(nèi)容屬性測試" Height="350" Width="525"><Grid><Button Content="1" Margin="120,146,0,146" HorizontalAlignment="Left" Width="82" /><Button Content="2" x:Name="btn2" Margin="0,146,142,145" HorizontalAlignment="Right" Width="82" /></Grid> </Window>運行兩段代碼效果一樣。充分說明了重要而有不常見的原因。因為省略的省時,而且簡潔明了。所以多數(shù)引用時都省去了。
2.2ContentControl族
先說一下其特點:他們內(nèi)容屬性的名稱為Content,只能有單一元素充當(dāng)其內(nèi)容。下面通過例子說明其特點:
<Button Margin="120,146,0,76" HorizontalAlignment="Left" Width="100"><TextBox Text="測試"/><TextBox Text="測試"/><TextBox Text="測試"/></Button>上面的會報錯,原因是Button里面只能有單一元素充當(dāng)其內(nèi)容。去掉后面的兩個TextBox,效果如圖2:
?
圖2
發(fā)現(xiàn)button里面不僅可以顯示文字還可以用一個控件來當(dāng)其內(nèi)容。其他的控件不在一一舉例。在此列出此類的主要控件:
Button、ButtonBase、CheckBox、ComboBoxItem、ContentControl、Frame、GridViewColumnHeader、GropItem、Label、ListBoxItem、ListViewItem、NavigationWindow、RadioButton、ScrollViewer、StatusBarItem、ToggleButton、ToolTip、UserControl、Window。
2.3 HeaderedContentControl族
特點:可以顯示帶標題的數(shù)據(jù),內(nèi)容屬性為Content和Header,其這兩個屬性都只能容納一個元素。在此舉例說明GroupBox的用法,然后列出其他屬于此類的控件。XAML代碼為:
View Code <Window x:Class="Chapter_03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="內(nèi)容屬性測試" Height="200" Width="300"><Grid Background="Gold"><GroupBox Margin="42,0,96,26"><GroupBox.Header><Label Content="我是標題"/></GroupBox.Header><Button HorizontalAlignment="Left" Width="117" Height="45"><TextBox Text="測試"/></Button></GroupBox></Grid> </Window>效果圖如圖3:
圖3
是不是看著很還好呢?現(xiàn)在列出同類主要的控件:Expender,GroupBox,HeaderedContentControl,TabItem。
2.4 ItemsControl族
特點:該類控件用于顯示列表化的數(shù)據(jù),內(nèi)容屬性為Items或ItemsSource,每種ItemsControl都對應(yīng)有自己的條目容器(Item Container)。本類元素可能會用的比較多些,也比較靈活,所以這里不做過多記錄,以后的記錄會經(jīng)常用到,具體的再詳細說明。下面就用一個ListBox控件來小試牛刀吧!XAML代碼、Cs代碼如下:
XAML <Window x:Class="Chapter_03.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="內(nèi)容屬性測試" Height="260" Width="408"><Grid Background="Gold"><ListBox x:Name="listbox" Margin="0,0,198,55"><CheckBox x:Name="cb1" Content="選擇"/><CheckBox x:Name="cb2" Content="選擇"/><CheckBox x:Name="cb3" Content="選擇"/><CheckBox x:Name="cb4" Content="選擇"/><Button x:Name="btn1" Content="按鈕1"/><Button x:Name="btn2" Content="按鈕1"/><Button x:Name="btn3" Content="按鈕1"/></ListBox></Grid> </Window> CS using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes;namespace Chapter_03 {/// <summary>/// MainWindow.xaml 的交互邏輯/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();Button btn=new Button();btn.Content="另外添加一個";btn.Click += new RoutedEventHandler(btn_Click);this.listbox.Items.Add(btn);btn3.Click+=new RoutedEventHandler(btn_Click);}/// <summary>/// 用來找到button的父級元素類型/// </summary>/// <param name="sender"></param>/// <param name="e"></param>void btn_Click(object sender, RoutedEventArgs e){Button btn=(sender) as Button;DependencyObject level1 = VisualTreeHelper.GetParent(btn);DependencyObject level2 = VisualTreeHelper.GetParent(level1);DependencyObject level3 = VisualTreeHelper.GetParent(level2);if (btn != null)MessageBox.Show(level3.GetType().ToString());else MessageBox.Show("無找到!");}} }效果圖如圖4:
圖4
? 先來說明一下代碼:在listBox里面放了幾個checkbox和button,說明ListBoxI的Item不僅支持類型相同的元素,還支持類型不同的元素。這是因為,Listbox的每一項都是經(jīng)過“ListBoxItem”加工廠處理的,最終放入當(dāng)做自己的內(nèi)容--放入自己的容器內(nèi)。這里通過后臺代碼說明了每一個條目都被ListboxItem包裝過了,完全沒有必要每一個條目都在xmal文件按照如下寫法:
<ListBoxItem><Button x:Name="btn3" Content="按鈕1"/></ListBoxItem>在實際項目中,很少像上面那樣把代碼寫死,可以動態(tài)的綁定ListBox。把數(shù)據(jù)源賦給ListBox的ItemsSource,通過DisplayMemberPath屬性來顯示string類型的數(shù)據(jù)源里面的字段條目(如果想顯示復(fù)雜的數(shù)據(jù)的話,要使用DataTemplate,具體在模板再記錄,在此知道有這么一回事就好了);通過SelectedItem和SelectionChanged來觀察選中的項。下面的例子實現(xiàn)在listbox上綁定指定數(shù)據(jù),然后彈出選中人的年齡。直接給出代碼:
XAML <Window x:Class="Chapter_03.ListBoxTest"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="ListBoxTest" Height="300" Width="300"><Grid><ListBox x:Name="listbox1" Margin="0,0,60,31" SelectionChanged="listbox1_SelectionChanged"></ListBox></Grid> </Window> CS using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes;namespace Chapter_03 {/// <summary>/// ListBoxTest.xaml 的交互邏輯/// </summary>public partial class ListBoxTest : Window{public ListBoxTest(){InitializeComponent();InitData();}protected void InitData(){List<People> peopleList = new List<People>(){new People(){Id=1,Name="Tim",Age=30},new People(){Id=2,Name="Tom",Age=30},顯示結(jié)果如圖5:
圖5
下面列出屬于ItemsControl族元素和其對應(yīng)的Item Container有ComboBox——ComboBoxItem,ContextMenu——MenuItem,ListBox——ListBoxItem,ListView——ListViewItem,Menu——MenuItem,StatusBar——StatusBarItem,TabControl——TabItem,TreeView——TreeViewItem.
由于已經(jīng)演示了HeaderedContentControl和ItemsControl的功能,另外HeaderedItemsControl的用法就不再記錄了,僅僅列出屬于其族的控件:
MemuItem、TreeViewItem、TooBar。
三、?UI布局
在介紹布局之前還是先記錄一下布局控件的特點與屬于Panel族的控件。
panel族控件內(nèi)容屬性為Children,所以內(nèi)容可以是多個元素,這對布局來說是很重要的特征。布局控件與ItemControl的區(qū)別是:前者強調(diào)的是對元素的布局,后者強調(diào)的是條目。屬于Panel類的控件有:Canvas,DockPanel,Grid,TabPanel,ToolBarOverflowPanel,StackPanel,ToolBarPanel,UniformGrid,VirtualizingPanel,VirtualizingStackPanel,WrapPanel。這么多控件不可能一個個去介紹,找?guī)讉€比較重要的實踐一下。回頭如果有用到的話再逐一研究。
3.1 主要布局控件的特性
在WPF里面控件與控件的關(guān)系除了相鄰和重疊(用Opacity來控制哪個控件在上面,哪個在下面),還有一個包含。正因為如此,才有了以window為根的樹形結(jié)構(gòu)的XAML。下面介紹一下主要布局元素的特性:
- Grid:網(wǎng)格。可以自定義行和列,并通過行列的數(shù)量、行高和列寬來調(diào)整控件的布局,有點類似于html中的Table。
- StackPanel:棧式面板。可以將包含元素排成一條直線,當(dāng)添加或移除包含元素時,后面的元素會自動向下或向上移動。
- Canvas:畫布。可以指定包含元素的絕對坐標位置。
- DockPanel:泊靠式面板。內(nèi)部元素可以選擇泊靠方式。
- WarpPanel:自動折行面板。當(dāng)一行元素排滿后會自動換行。類似html中的流式布局。
3.2 Grid
Grid的特點如下:
- 可以定義任意數(shù)量的行和列
- 行高與列寬可以使用絕對值,相對比以及最大值和最小值
- 內(nèi)部元素可以設(shè)置自己的所在列和行,還可以設(shè)置自己跨幾列和行。
- 可以設(shè)置Children元素的對齊方式
現(xiàn)在給出定義行與列的代碼(記得在后臺代碼上加上 this.grid.ShowGridLines=true以便顯示出網(wǎng)格):
XAML <Window x:Class="Chapter_03.Grid"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Grid" Height="300" Width="300" MinHeight="300" MaxWidth="500"><!--MinHeight="300" MaxWidth="500"限制窗口的最小高度和最大寬度--><Grid x:Name="grid"><!--定義行--><Grid.RowDefinitions><RowDefinition Height="25" ></RowDefinition><RowDefinition Height="50"/><RowDefinition Height="1*"/><RowDefinition Height="*"/><RowDefinition Height="auto"></RowDefinition></Grid.RowDefinitions><!--定義列--><Grid.ColumnDefinitions><ColumnDefinition Width="25" ></ColumnDefinition><ColumnDefinition Width="50"/><ColumnDefinition Width="1*"/><ColumnDefinition Width="*"/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><!--在指定的行列中布置控件--><TextBox Grid.Column="1" Grid.Row="2" Grid.ColumnSpan="2" Text="布局" Background="Gray"/></Grid> </Window>運行效果圖如圖6,可以放大觀察效果(是因為Width="*"的原因,本例子中利用了兩個*其中第三行是一個*,所以占剩余的二分之一,可以試著改成2*,就是三分之二了,可以試著觀察效果):
圖6
?3.3 StackPanel
StackPanel可以把內(nèi)部的元素在縱向或者橫向上緊密排列,形成棧式布局。先介紹一下其三個屬性:
- Orientation 決定內(nèi)部元素是橫向還是縱向累積。可取值為Horizontal,Vertical。
- HorizontalAlignment 決定內(nèi)部元素水平方向上的對齊方式。可取值Left,Center,Right,Stretch。
- VerticalAlignment 決定內(nèi)部元素豎直方向上的對齊方式。可取Top,Center,Bottom,Stretch。
StackPanel也是布局中比較常見的控件,下面舉例:添加按鈕,其他內(nèi)容控件會自動下移。效果如圖7:
圖7
下面上代碼:
XAML <Window x:Class="Chapter_03.StackPanel"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="StackPanel" Height="338" Width="423"><Grid Height="286" Width="382"><GroupBox Header="測試StackPanel" BorderBrush="Black" Margin="5"><StackPanel Margin="5" x:Name="stackpanel"><StackPanel Orientation="Vertical" x:Name="btnList"></StackPanel><StackPanel Orientation="Horizontal" HorizontalAlignment="Center"><TextBlock Text="填寫添加按鈕名稱: " Height="20" /><TextBox Name="btnName" Width="102" Height="20" /><Button Content="添加" Width="60" Margin="5" Click="Button_Click" /></StackPanel></StackPanel></GroupBox></Grid> </Window> Cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes;namespace Chapter_03 {/// <summary>/// StackPane_.xaml 的交互邏輯/// </summary>public partial class StackPanel : Window{public StackPanel(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){if (!string.IsNullOrEmpty(this.btnName.Text)){Button btn = new Button();btn.Content = this.btnName.Text;this.btnList.Children.Add(btn);}elseMessageBox.Show("請輸入按鈕名稱!");}} }當(dāng)輸入按鈕名稱的話,點擊添加,原有的內(nèi)容會下移。
3.4 Canvas
畫布:內(nèi)容控件可以準確定位到指定坐標,但是不足的地方是,如果要修改的話可能會關(guān)系到很多的控件,所以如果不需要經(jīng)常修改的窗體,使用該控件布局,或者是藝術(shù)性比較強(用來實現(xiàn)依賴于橫縱坐標的動畫等功能)的布局使用此控件布局。
在此制作一個登陸頁面主要來看一下Canvas.Left與Canvas.Top的用法。效果圖如圖8,直接上代碼:
XAML <Window x:Class="Chapter_03.Canvas"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="登陸" Height="145" Width="300"><Canvas Background="Sienna"><TextBlock Canvas.Left="0" Canvas.Top="13" Margin="5" Text="用戶名:"/><TextBox Canvas.Left="50" Canvas.Top="13" Width="160" /><TextBlock Canvas.Left="0" Canvas.Top="47" Margin="5" Text="密 碼:"/><TextBox Canvas.Left="50" Canvas.Top="47" Width="160" /><Button Content="確定" Canvas.Left="70" Canvas.Top="77" Width="63" Height="22" /><Button Canvas.Left="150" Canvas.Top="77" Content="清除" Width="63" Height="22" /></Canvas> </Window>?圖8
3.5 DockPanel
這個控件主要有個最后一個內(nèi)容控件實現(xiàn)填充所有剩余部分的功能。主要用到LastChildFill=True屬性。下面給出一個例子,先看一下把LastChildFill分別設(shè)置為True和False的結(jié)果對比圖如圖9:
圖9
XAML代碼給出:?
XAML <Window x:Class="Chapter_03.DockPanel"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="DockPanel" Height="300" Width="300"><DockPanel Name="dockpanel" LastChildFill="True"><Button Name="button1" DockPanel.Dock="Top">1</Button><Button Name="button2" DockPanel.Dock="Bottom" >2</Button><Button Name="button3" DockPanel.Dock="Left">3</Button><Button Name="button4" DockPanel.Dock="Right">4</Button><Button DockPanel.Dock="Top">剩余空間</Button></DockPanel></Window>在此說明一下,如果LastChildFill=True,最后一個元素?<Button >剩余空間</Button>就會充滿其剩余部分。上面的只能填充,但是不能通過拖拽的方式改變控件的寬度。下面給出一個實現(xiàn)拖拽功能的代碼。不過是在Grid里面的通過GridSplitter(可以改變Grid初始設(shè)置的行高或列寬)控件實現(xiàn)。直接給出代碼:
XAML <Window x:Class="Chapter_03.GridSplitter"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="GridSplitter" Height="300" Width="300"><Grid><Grid.RowDefinitions><RowDefinition Height="5"/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="150"/><ColumnDefinition Width="Auto"/><ColumnDefinition/></Grid.ColumnDefinitions><TextBox Grid.ColumnSpan="3" BorderBrush="Black"/><TextBox Grid.Row="1" BorderBrush="Black"/><GridSplitter Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Center"Width="5" Background="Gray" ShowsPreview="True"/><TextBox Grid.Row="1" Grid.Column="2" BorderBrush="Black"/></Grid> </Window>具體的GridSplitter的屬性見http://www.cnblogs.com/luluping/archive/2011/08/26/2155218.html。
3.6 WrapPanel
此控件會根據(jù)布局的大小來控制內(nèi)容元素的排列。不會因為窗體沒有放大,影響到其他內(nèi)容的顯示。在此只舉一個例子,來理解WrapPanel。上代碼了:
XAML <Window x:Class="Chapter_03.WrapPanel"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="WrapPanel" Height="300" Width="300"><WrapPanel><Button Width="50" Height="50"/><Button Width="50" Height="50"/><Button Width="50" Height="50"/><Button Width="50" Height="50"/><Button Width="50" Height="50"/><Button Width="50" Height="50"/><Button Width="50" Height="50"/></WrapPanel> </Window>效果圖如圖10:
圖10
四、總結(jié)
布局一直是自己的弱項,所以可能這篇記錄的會比較差點,但是重在理解控件的作用以及能舉一反三。?雖然控件沒有一一列出,但是對于每一族的控件都給出了一個實例,可以通過實例加深對各個控件的理解,具體的運用還需多加強練習(xí)和查閱msdn。下一篇:深入淺出話Binding。?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/lzhp/archive/2012/09/05/2669764.html
總結(jié)
- 上一篇: 嵌入式成长轨迹37 【Zigbee项目】
- 下一篇: WPF中使用流文档灵活地显示内容