WPF是和WinForm對應的,而其核心是數據驅動事件,在開發中顯示的是UI界面和邏輯關系相分離的一種開放語言。UI界面是在XAML語言環境下開發人員可以進行一些自主設計的前台界面,邏輯關系還是基於c#語言進行的邏輯設計。在使用WPF做項目的時候,免不了要對界面進行布局,同時需要對其中的控件進行綁定,本文主要是對這幾方面進行介紹。
首先介紹WPF的基礎知識:
1 XAML是什么?
XAML(Extensible Application Markup Language)即可擴展應用程序標記語言,是WPF技術中專門用於設計UI的語言。
2、XAML的文檔結構?
在程序員眼中,UI界面只是一個平面結構。但是從后台XAML代碼中看來,UI界面實際上是一個樹形邏輯結構。
3、XAML中屬性元素的理解?
屬性元素指的是某個標簽的一個元素對應的這個標簽的一個屬性,即以元素的形式來表達一個實例的屬性。XAML為對象屬性賦值語法有兩種:使用字符串進行簡單賦值;使用屬性元素進行復雜賦值。例如:
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" >
<Rectangle x:Name="rectangle" Width="200" Height="120">
<Rectangle.Fill >
<SolidColorBrush Color="Blue" />
</Rectangle.Fill >
</Rectangle>
</Grid >
4、XAML中的標記擴展?
標記擴展就是一種特殊的Attribute=Value,特殊處在於Value是由一對花括號及其括起來的內容組成。XAML編譯器會對這樣的內容作出解析、生成相應的對象。
5、WPF中事件處理器?
WPF支持在XAML里為對象的事件指定事件處理器,方法是使用事件處理器的函數名為對應對象事件的Attribute進行賦值。例如:
<Button x:Name="button1" Click=" button1_Click" Margin="5" Height="15" Width="50"/>
6、在自己的程序里引用類庫?
步驟需要三步:
1編寫類庫項目並編譯得到.dll文件或者獲得別人編譯的.dll文件。
2將類庫項目或者.dll文件中引用自己的項目
3在c#和XAML中引用類庫中的名稱空間。
7、XAML注釋語法?
<!—注釋內容-->
9、 XAML中X名稱空間?
X名稱空間映射的是xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
<Rectangle x:Name="rectangle" Width="200" Height="120">
<Rectangle.Fill >
<SolidColorBrush Color="Blue" />
</Rectangle.Fill >
</Rectangle>
10、X名稱空間大致可分為三類?
Attribute:是語言層面的東西、是給編譯器看的。(Property是面向對象層面的東西,是給編程邏輯用的)
標記擴展:實際上就是一些MarkupExtension類的直接或者間接派生類。X名稱空間中就包含這樣的類。
XAML指令元素:有x:Code,x:XData。
11、WPF中控件?
經常用到的6類:
1 布局控件 可以容納多個控件或嵌套其他布局控件,用於在UI上組織和排列控件。常用的Grid、StackPanel、DockPanel,共同父類是Panel。
2 內容控件 只能容納一個其他控件。父類是ContentControl。
3 帶標題內容的控件 就是一個內容控件+一個標題,標題部分可容納一個控件或布局。
4 條目控件 可以顯示一列數據,共同父類是ItemsControl。
5 帶標題條目控件 就是一個條目控件+一個標題顯示區。基類是HeaderedItemsControl。
6 特殊內容控件
12、WPF精髓?
WPF是數據驅動UI,數據是核心、主動的,UI屬於數據並表達數據、被動的。
13、Binding是什么?
在WPF中,Binding是架在目標(UI層控件對象)和源(邏輯層對象)之間的一座橋梁。
14、Binding對源的要求是:只要它是一個對象,並且通過屬性公開自己的數據,它就能作為Binding的源。
15、Binding的方向即Binding模式有哪些?
Binding模式有OneWay、TwoWay、OnTime、OneWayToSource、Default。
OneWay:每當源發生變化時,數據就會從源流向目標。
TwoWay:綁定會將源數據發送到目標,但如果目標屬性發生變化,則會將變化發回給源。
OnTime:綁定會將源數據發送到目標,但是僅當啟動了應用程序或DataContext發生更改時才會有這個操作,所以它不會偵聽源中的更改通知。
OneWayToSource:綁定會將源數據發送到目標。
Default:根據實際情況來定,可編輯的就是TwoWay,只讀的就是OneWay。在連接沒有設置模式時就是Ddfault。
16、Binding的路徑(Path)和沒有Path的情況?
Binding的路徑設置有很多種,其中最簡捷的就是直接把Binding關聯在Binding源上。
<TextBox x:Name=”textBox1” Text={Binding Path=Value, ElementName=slider1}/>
沒有Path的情況就是Binding源本身就是數據且不需要Path來指明。
17、為Binding指定Source的幾種方法?沒有Source的情況?
(幾種方法,在這里不一一羅列,但是沒有實際用過。)
沒有Source的情況就是使用DataContext作為Binding的源。
18、Binding的數據校驗?Binding的數據轉換?
Binding數據有效性校驗是它的ValidationRules屬性,Binding數據轉換是它的Converter屬性。
19、模板 Template?
ControlTemplate是算法內容的表現形式;DataTemplate是數據內容的表現形式
20、DataTemplate常用到的3個地方:
ContentControl的ContentTemplate屬性,ItemsControl的ItemTemplate屬性,GrdiViewColumn的CellTemplate屬性。
WPF界面布局
在WPF做項目過程中,需要對界面元素進行合理布局,以便系統更人性化。WPF提供了一套強有力的工具:面板panel。下面將逐一介紹這些面板的使用方法。
Grid
網格面板,以表格形式布局元素,對於整個面板上的元素進行布局,有效地解決多行之間、多列之間位置的一致性。Grid很像網頁中的Table,定義一個網格,需要定義行、列,划分單元格,坐標從(0,0)開始。列寬和行高,分別可以在ColumnDefinition、RowDefinition里面指定Width、Height的值。首先定義網格,然后定義元素,並指定元素所在的單元格。如果不定義單元格,默認將元素放到第一個單元格(0,0)。自動長度——自動匹配列中最長元素的寬度;比例長度——*表示占用剩余的全部寬度;兩行都是*,將平分剩余寬度;一個2*,一個*,表示前者2/3寬度。使用Grid.ColumnSpan和Grid.RowSpan附加屬性可以讓相互間隔的行列合並,使用GridSplit控件結合Grid控件實現類似於WinForm中SplitContainer的功能。
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="44*" /> <ColumnDefinition Width="141*" /> <ColumnDefinition Width="218*" /> <ColumnDefinition Width="122*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="62*" /> <RowDefinition Height="70*" /> <RowDefinition Height="102*" /> <RowDefinition Height="116*" /> </Grid.RowDefinitions> <Button Content="Button" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="16,18,0,0" Name="button1" VerticalAlignment="Top" Width="75" Grid.Column="1" /> <Button Content="Button" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="107,38,0,0" Name="button2" VerticalAlignment="Top" Width="75" /> <Button Content="Button" Grid.Row="3" Height="23" HorizontalAlignment="Left" Margin="16,38,0,0" Name="button3" VerticalAlignment="Top" Width="396" Grid.ColumnSpan="3" Grid.Column="1" /> </Grid>
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="102" /> <ColumnDefinition Width="161" /> <ColumnDefinition Width="15" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="66" /> <RowDefinition Height="96" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> </Grid>
Canvas
畫布,用於完全控制每個元素的精確位置。它是布局控件中最為簡單的一種,直接將元素放到指定位置,主要來布置圖畫。使用Canvas時,必須指定一個字元素的位置(相對於畫布),否則所有元素都將出現在畫布的左上角。調整位置用Left、Right、Top和Bottom四個附加屬性。如果Canvas是窗口主元素,用戶改變窗口大小時,Canvas也會隨之變化,字元素的位置也會隨之移動,以保證相對於Canvas的位置屬性不變。Canvas允許子元素的部分或全部超過其邊界,默認不會剪裁子元素,同時可以使用負坐標,因此畫布不需要指定大小。如果想復制畫布內容,將ClipToBounds設為true即可。
<Grid> <Canvas Height="280" HorizontalAlignment="Left" Margin="22,32,0,0" Name="canvas1" VerticalAlignment="Top" Width="482" Background="Gray"> <Canvas Canvas.Left="46" Canvas.Top="58" Height="100" Name="canvas2" Width="200" Background="Red"> <Ellipse Canvas.Left="82" Canvas.Top="20" Height="100" Name="ellipse1" Stroke="Black" Width="200" Fill="White" /> </Canvas> <Canvas Canvas.Left="295" Canvas.Top="184" Height="64" Name="canvas3" Width="143" Background="Green" ClipToBounds="True" > <Ellipse Canvas.Left="24" Canvas.Top="7" Height="100" Name="ellipse2" Stroke="Black" Width="200" Fill="White" /> </Canvas > </Canvas> </Grid>
StackPanel
棧面板,可以將元素排列成一行或者一列。其特點是:每個元素各占一行或者一列。Orientation屬性指定排列方式:Vertical(垂直)【默認】、Horizontal(水平)。默認情況下,水平排列時,每個元素都與面板一樣高;垂直排列時,每個元素都與面板一樣寬。如果元素超過了StackPanel的空間,會截斷多出的內容。 元素的Margin屬性用於使元素之間產生一定得間隔,當元素空間大於其內容的空間時,剩余空間將由HorizontalAlignment和VerticalAlignment屬性來決定如何分配。
<Grid> <StackPanel Height="144" HorizontalAlignment="Left" Margin="33,21,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="346"> <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="235" /> <Button Content="Button" Height="23" Name="button3" Width="658" /> </StackPanel> <StackPanel Height="155" HorizontalAlignment="Left" Margin="45,179,0,0" Name="stackPanel2" VerticalAlignment="Top" Width="401" Orientation="Horizontal" > <Button Content="Button" Height="23" Name="button4" Width="75" /> <Button Content="Button" Height="23" Name="button5" Width="75" /> </StackPanel> </Grid>
WrapPanel
從左至右或從上至下按順序位置定位子元素,如果排滿則自動換行至下一行或列繼續排列。WrapPanel面板也提供了 Orientation屬性設置排列方式,屬性設置橫排(Horizontal默認)和豎排(Vertical)。
<Grid> <WrapPanel Height="129" HorizontalAlignment="Left" Margin="50,36,0,0" Name="wrapPanel1" VerticalAlignment="Top" Width="260"> <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="75" /> <Button Content="Button" Height="23" Name="button3" Width="75" /> <Button Content="Button" Height="23" Name="button4" Width="75" /> </WrapPanel> <WrapPanel Height="105" HorizontalAlignment="Left" Margin="99,195,0,0" Name="wrapPanel2" VerticalAlignment="Top" Width="211" Orientation="Vertical" > <Button Content="Button" Height="23" Name="button5" Width="75" /> <Button Content="Button" Height="23" Name="button6" Width="75" /> </WrapPanel> </Grid>
DockPanel
停靠面板,可以將面板的某一邊指定給每個元素,當面板大小變化時,按鈕將根據指定的邊進行停靠。在DockPanel中,指定停靠邊的控件,會根據定義的順序占領邊角,所有控件絕不會交疊。默認情況下,后添加的元素只能使用剩余空間,最后一個元素填充所有剩余空間。如果不希望最后一個元素填充剩余區域,可以將DockPanel屬性LastChildFill設置為False。
DockPanel面板可以將子元素停靠在面板的上下左右。DockPanel會對每個子元素進行排序,並停靠在面板的一側,多個停靠在同側的元素則按順序排序,最后一個元素填充這個Panel(這個需要設置LastChildFill屬性為 True)。對於在DockPanel中的元素的停靠屬性可以通過Panel.Dock的附加屬性來設置。
<Grid> <DockPanel Height="100" HorizontalAlignment="Left" Margin="43,44,0,0" Name="dockPanel1" VerticalAlignment="Top" Width="281" LastChildFill="False" > <Button Content="Button" Height="23" Name="button1" Width="75" /> <Button Content="Button" Height="23" Name="button2" Width="75" /> <Button Content="Button" Height="23" Name="button3" Width="75" /> </DockPanel> <DockPanel Height="141" HorizontalAlignment="Left" Margin="140,183,0,0" Name="dockPanel2" VerticalAlignment="Top" Width="299"> <Button Content="Button" Height="23" Name="button4" Width="75" /> <Button Content="Button" Height="23" Name="button5" Width="75" /> <Button Content="Button" Height="23" Name="button6" Width="75" /> </DockPanel> </Grid>
視圖框(Viewbox)
視圖框可以自動縮放其內容,以填充可用的空間。它只能有一個子元素。比如,Viewbox中放置一個Canvas,默認將按比例縮放Canvas,填充區域,而此時Canvas指定的長寬已不起作用,僅保留比例。如果想禁用Viewbox的自動縮放功能,將其拉伸屬性Stretch設置為None即可;如果想縮放並且不保留子元素比例,將Viewbox的Stretch屬性(默認為Uniform)改為Fill(完全填充);如果想保留比例並完全填充空白區域,Stretch設置為UniformToFill。
上圖分別是stretch:Stretch="Fill", Stretch="None",Stretch="Uniform",Stretch="UniformToFill"
<Grid>
<Canvas Height="261" HorizontalAlignment="Left" Margin="34,22,0,0" Name="canvas1" VerticalAlignment="Top" Width="416">
<Viewbox Canvas.Left="29" Canvas.Top="18" Height="218" Name="viewbox1" Width="348" Stretch="UniformToFill" >
<Button Content="Button" Height="23" Name="button1" Width="75" />
</Viewbox>
</Canvas>
</Grid>
Border
Border 是一個裝飾的控件,此控件繪制邊框及背景,在 Border 中只能有一個子控件(這個子控件又可以包含多個子控件)。
<Grid> <Border BorderThickness="20" BorderBrush="Green" Height="230" HorizontalAlignment="Left" Background="Red" Margin="35,40,0,0" Name="border1" VerticalAlignment="Top" Width="447" /> </Grid>
滾動視圖控件(ScrollViewer)
滾動視圖控件可以將過多的內容放入一個可滾動的區域來顯示。比如一個很大的橢圓,通過滾動就可以顯示全部內容。但是ScollViewer只能放一個元素,這個元素是任意的。倘若想布局多個元素,可以將多個元素放到一個面板中,再嵌入到ScollViewer中。
滾動條的可見性,默認垂直滾動條是可見的(Visiable),而水平滾動條是不可用的(Disable),此處改為Auto(需要時顯示)或者Visiable(可見,不論需不需要都顯示)。
UniformGrid
均布網格, 顧名思義,所有單元格均勻排布,大小都相同。你可以為其指定行數Rows和列數Columns,UniformGrid將根據行列平分畫布,每個控件一個單元格。如果不指定其行數和列數,UniformGrid會根據子元素個數和大小,默認創建相同的行數和列數,布局所有子元素。由於每個單元格只包含一個子元素,不需要額外指定哪個元素屬於哪個單元格,所以直接添加子元素就可以。
公共布局屬性
Width、Height表示元素寬度和高度,設置該屬性可以是元素具有精確的寬高。MinWidth、MaxWidth、MinHeight、MaxHeight 可以指定元素大小的界限,分別表示元素的最小寬度、最大寬度、最小高度和最大高度。當你指定一個元素的寬度和高度時,WPF會盡可能遵循你的設置。比如元素寬度>屏幕寬度,元素將被剪裁以適應可用空間。
Margin(外邊距),指的是元素周圍的距離,決定了元素周圍留下的空白大小;Padding(內邊距),指的是元素邊界與其內容之間的距離。做過網頁設計、用過CSS的同學對margin和padding屬性肯定不陌生,但也有所區別。WPF中的Margin值可以為一個數字、一對數字和四個數字。
一個數字代表四周距離相同,為指定值。一對數字時,第一個數字表示左側和右側距離相同,為指定值;第二個數字表示頂部和底部距離相同,為指定值。(與CSS中順序不同)。四個數字,分別表示左側、頂部、右側、底部距離,該順序與CSS不同。
CSS中margin和padding屬性順序是:兩個數字對應左右、上下;四個數字對應上、右、下、左;
HorizontalAlignment與VerticalAlignment
在父元素中,當剩余空間很大時(超過子元素需要),這兩個屬性可以控制字元素的位置。比如,在垂直排列的StackPanel中,面板寬度默認和最寬的元素寬度相同,其他控件的寬度默認將會被拉伸。這時,可以使用HorizontalAlignment屬性來控制,默認值為Stretch(拉伸),還有Left、Center、Right。VerticalAlignment則有Stretch、Top、Center和Bottom四個枚舉值。
Visibility可見度,決定元素是否可見。枚舉值有兩個:Collapsed和Hidden。Collapsed元素不可見,並且首選尺寸變為0,不再影響布局。Hidden元素雖然不可見,但尺寸不變,布局系統仍按可見的處理。
FlowDirection文本方向,默認情況下基於系統的本地設置。比如英語、中文都是從左往右排列,LeftToRight;希伯來文從右往左排列,RightToLeft。如果為面板指定該屬性,則面板的所有子元素都按此方向排列。
Panel.ZIndex,Panel定義的一個附加屬性ZIndex,用於多個元素重疊時,指定顯示的上下層關系。ZIndex值大的將出現在值小的元素上方。元素顯示順序將不受文檔定義順序控制。如果不使用ZIndex,重疊元素將根據文檔定義的順序顯示,后定義的元素出現在上方。
Binding基礎
首先,創建一個類Student,繼承INotifyPropertyChanged。定義幾個字段以及一個組件屬性更改引發的方法。貼代碼:
public class Student:INotifyPropertyChanged { private string name; public string Name { get { return name; } set { this.name = value; NotifyPropertyChanged("Name"); } } private int id; public int ID { get { return id; } set { this.id = value; NotifyPropertyChanged("ID"); } } public int age; public int Age { get { return age; } set { this.age = value; NotifyPropertyChanged("Age"); } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
1、在前台wpf界面定義一個button控件(btnBinding_Click),用以改變Name字段的值,將控件textbox(tbDisplay)綁定到Name字段。基本步驟:
(1)所有代碼都寫在后台邏輯:
准備數據源:Student student = new Student();
准備Binding:
Binding binding = new Binding();
binding.Source = student;
binding.Path = new PropertyPath("Name");
使用Binding連接到數據源與Binding目標:BindingOperations.SetBinding(this.tbDisplay, TextBox.TextProperty, binding);
將上述三步操作合一:this.tbDisplay.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = student = new Student() });
(2)不過大家一般的做法是在前台界面進行綁定,同時在后台邏輯上指定綁定源,如下:
前台綁定:<TextBox Height="23" HorizontalAlignment="Left" Margin="27,12,0,0" Name="tbDisplay" Text="{Binding Path=Name}" VerticalAlignment="Top" Width="120" />
后台邏輯:
Binding bind = new Binding("Name") { Source = student };
this.tbDisplay.SetBinding(TextBox.TextProperty, bind);
(3)現在將其放在一個容器內進行綁定:
前台綁定:<StackPanel Height="226" HorizontalAlignment="Left" Margin="790,36,0,0" Name="SP" VerticalAlignment="Top" Width="239" >
<TextBox x:Name="tbDisplay" Width="171" Height="47" Margin="5,100,10,100" Text="{Binding Path=Name}" />
</StackPanel >
后台邏輯:Student student = new Student();
this.SP.DataContext = student;
2、將控件作為binding源於binding標記擴展
所有操作都放在在前台界面中:
<TextBox Height="23" HorizontalAlignment="Left" Margin="27,170,0,0" Name="tbNum" Text="{Binding Path=Value,ElementName=slider1}" VerticalAlignment="Top" Width="120" />
<Slider Height="23" HorizontalAlignment="Left" Margin="27,210,0,0" Name="slider1" VerticalAlignment="Top" Maximum="100" Minimum="0" Width="187" />
3、使用集合對象作為列表控件的ItemsSource
下面這個例子是要把List<Student>集合的實例作為ListBox的ItemsSource,同時讓ListBox顯示Student的Name並使用TextBox顯示當前選中的條目的ID。
前台界面:
<TextBox Height="23" HorizontalAlignment="Right" Margin="0,52,571,0" Name="tbID" VerticalAlignment="Top" Width="120" />
<ListBox Height="100" HorizontalAlignment="Left" Margin="414,162,0,0" Name="lbStudent" VerticalAlignment="Top" Width="120" />
后台的邏輯:
List<Student> stuList = new List<Student>() { new Student (){ID=0,Name="Tim",Age=23}, new Student (){ID=1,Name="Tom",Age=24}, new Student (){ID=2,Name="Mike",Age=25}, }; //為listbox設置Binding this.lbStudent.ItemsSource = stuList; this.lbStudent.DisplayMemberPath = "Name"; //為textbox設置Binding Binding binding = new Binding("SelectedItem.ID") { Source = this.lbStudent }; this.tbID.SetBinding(TextBox.TextProperty, binding);
都是一些基礎,謹供學習。
綁定補充: