目錄
前言
為什么要寫WPF呢?
我一開始算是比較抵觸WPF的,因為用的人少嗎。感覺都是窗體應用能和Winform有什么區別。可是我錯了,非常感謝我的講師,給我推薦劉鐵猛的《深入淺出WPF》,讓我了解到了WPF的魅力——數據驅動UI 。
所以,這么優秀的框架,我想寫下來,都知道WPF開發人員非常少,以至於大部分教程視頻都是10年前的。我記錄下來,不是為了什么,是真的喜歡WPF,那種“怪誕不經”的感覺。
一、UI布局
俗話說:“人靠衣裝馬靠鞍”
什么意思呢?意思是人穿上一身得體的衣服,就會顯得分外精神;馬備上一副講究的鞍韉,就會顯得特別駿美。指衣服對人體的形象美有極大的影響。出自《薛仁貴征爾》
那么,我們把這句話引入到WPF中
- WPF作為專門的用戶界面技術,布局的功能是它的核心功能之一。友好的用戶界面和良好的用戶體驗離不開設計精良的布局。
- WPF設計師工作量最大的倆部分就是布局和動畫,布局是靜態的,動畫是動態的,用戶體驗就是用 戶在這動靜之中與軟件功能產生交互時的感受。
- 也就是說,布局就是WPF的衣服!
二、控件
"我老生涯鷗水相依,他舊風流鴻塞荒投。”
意思是野生動物和野生環境鷗水相依,不可分離
那么到WPF中呢? 一個頁面的布局,顯示。都是由一個個控件組成的。控件們離不開WPF這個賴以生存的環境,組成了一幅幅美麗生動的畫面(布局)。
在開始學習這些布局元素前,我們要知道每個布局元素都有自己的特點,我們要靈活使用。切莫不要無所不用其極,要合理搭配。
(就像生態環境一樣,要合理搭配,否則就會出現“生物入侵”這種“偷雞不成蝕把米”的行為,加重了生態環境的負擔。破壞)
1、 控件的分類
粗略而言,日常工作中我們打交道最多的控件無外乎6類,即:
- 1、布局控件:可以容納多個控件或嵌套其他布局控件,用於UI上組織和排列控件,如:StackPanel,Grid,Dock,WrapPanel,,Canvas;
- 2、內容控件:只能容納一個其他控件或布局控件作為它的內容,如:Button,Window;
- 3、帶標題的內容控件:相當於一個內容控件,但可以加一個標題,如:Group Box,TabItem;
- 4、條目控件:可以顯示一列數據,一般情況下這列數據的類型相同。如ListBox,ComboBox;
- 5、帶標題的條目控件:相當於給一個條目控件加上一個標題顯示區,如:TreeViewItem,MenuItem,往往用於顯示層級數據;
- 6、特殊的內容控件:比如TextBox容納的是字符串,TeztBlock可以容納可自由控制格式的文本,Image容納圖片類型數據……這類的控件相對比較獨立。
至於,為什么這么分類呢,其實我們只需要細細體會一下,就明白了。實在不行,你從工具欄拖出來,看着它的樣子,在看着我的話。“什么?你還不懂”………………………………作者卒
好,我們不對這些控件做太多詳細介紹,我們主要介紹布局控件,其他相信屬性可以參考下面:
WPF 基本控件使用介紹:https://blog.csdn.net/niewq/article/details/50244227
三、布局控件
好了,終於來到我們的重點了。 WPF為我們提供了5中布局方式,他們的特點各不相同,可以相互嵌套,讓我們來認識一下吧。
- 1、Grid :列表布局
- 2、StackPanel :堆棧面板布局
- 3、WrapPanel : 流布局面板(當元素水平對齊,內容超過寬度時,自動換行;當元素垂直對齊,內容超過高度時,自動換列)
- 4、DockPanel:停靠面板
- 5、Canvas:坐標面板
1、Grid列表布局
Grid一詞譯為“網格;格子,柵格”
沒錯,它就像一個網格一樣把我們的頁面分割成一塊又一塊。
我們在窗體放置了2個TextBlock和2個TextBox,想實現登錄窗口的樣式,可是不進人意,他們都重疊在了一起。
為什么都重疊在了一起呢?
因為,我們沒有對Grid這個容器做相關調整,他現在是一個一行一列的“大格子”。在一個各自當然就重疊了,除非去設置Magin屬性,當然我們想要實現的並不是這種效果。
所以,我們通過設置
- 列<Grid.ColumnDefinitions></Grid.ColumnDefinitions>
- 行<Grid.RowDefinitions></Grid.RowDefinitions>
“分割Grid”
我們可以通過添加ColumnDefinitions節點和RowDefinitions節點 ,來確定把我們的“Grid分割成幾個格子”。
如圖,分割成了四個格子:
通過看設計窗口,我們也會發現,Grid被線條分割成了四塊:
唉?不對啊,為什么我們的控件還重疊的呢?是因為我們沒有去設置他們處於哪個格子,接下載,我們設置一下。
確定位置,與合並單元格
當我們的容器處於Grid布局容器里時,會增加附加屬性:
- 行所在位置: Grid.Row="0"
- 列所在位置: Grid.Column="0"
默認不設置,值為0 ,所以才會出現重疊的情況,來我們調整一下位置。
好了,一切恢復正常了,在增加一個按鈕,登錄怎么能沒有按鈕呢!
通過設置
- 行單元格合並: Grid.RowSpan="1"
- 列單元格合並: Grid.ColumnSpan="1"
默認值位1,合並幾個就寫幾。
經過稍作修改,我們的界面變成了這樣:
額……長得有點丑,我們添加些屬性來調整一下,
設置寬高
我們通過對ColumnDefinition的Width來設置寬和RowDefinition的Height來設置高
Width和Height支持像素,比例,以及自適應
- 像素: 直接用數字表示即可
- 比例: 以*做單位
- 自適應: 設置值位 auto
好,我們來運用上面的知識,調整一下我們的視圖:
總結
好,我們來總結一下上面的寬高:
- 首先我們對Grid的列經行了比例設置1:3(*,3*),當我們拉動窗體大小時,會發現它們的大小是等比例變化的。
- 其次,對Grid的行設定了倆個固定高度,我們可以發現無論窗體怎么變化他們的高度是不變的。
- 最后一行設定了自適應,我刻意把按鈕的高度設為100,我們可以看到表格最后一行也為100,可以知道auto是根據內容來自適應的。
2、StackPanel堆棧面板
大家都對“堆棧”倆個詞不陌生吧,堆棧面板就好像容器在“排隊”一樣,我們把Window下的Grid 換位StackPanel,來體驗一下吧。
設置方向
我們在StackPanel里放置了很多按鈕,發現他們就像排隊一樣,一個接一個,水平方向。如果你不喜歡這樣,當然是可以改方向的啦!
通過設置Orientation
- 水平對齊: Orientation="Horizontal"
- 垂直對齊: Orientation="Vertical"(默認)
對於內部元素自身,也可以選擇對其對齊方式
- HorizontalAlignment="left" Center,right
- VerticalAlignment="Top" Bottom,Center,Stretch
3、WrapPanel 流動布局
當元素水平對齊,內容超過寬度時,自動換行;當元素垂直對齊,內容超過高度時,自動換列)
它可能和上面的長得像,其實是不一樣的。當StackPanel的內部子元素數量超出寬度(高度)會溢出窗體,而流動布局會自動換行。常用於動態數據生成。
這就不做詳細介紹了,屬性都和上面的一樣。
4、DockPanel 停靠布局
做過winfrom開發的朋友,都知道一個Docl屬性吧,那么我們的Dock布局也是這個道理,我們來實踐一下。
在DockPanel的容器里的控件,會增加一個附加屬性DockPanel.Dock
- DockPanel.Dock="Top"
- DockPanel.Dock="Bottom"
- DockPanel.Dock="Left"
- DockPanel.Dock="Right"
分別是上下左右停靠,要注意的是控件會隨着設定的先后順序,具有不同的寬高(或大小),默認最后一個停靠控件的大小占剩下界面的全部
仔細看左 和 右 ,你就會知道我說的注意項,新停靠的控件占剩余頁面的所有區域。
5、Canvas 坐標布局
好吧,這個真的就相當於winform的布局了, 設置坐標,確定控件的位置。
在Canvas容器里的控件,會增加附加屬性
- 距離窗口上方: Canvas.Top="20"
- 距離窗口左方:Canvas.Left="100"
- 距離窗口下方:Canvas.Bottom="20"
- 距離窗口右方:Canvas.Right="0"
四、綜合小案例
如果都看完的話,我們來做一個小案例吧。
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="4*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Grid.RowSpan="2" Grid.Column="0">
<Button Height="50">新建StackPanel</Button>
<Button Height="50">保存StackPanel</Button>
<Button Height="50">增加StackPanel</Button>
<Button Height="50">導入StackPanel</Button>
<Button Height="50">導出StackPanel</Button>
<Button Height="50">關閉StackPanel</Button>
</StackPanel>
<DockPanel Grid.Row="0" Grid.Column="1">
<TextBlock DockPanel.Dock="Top" HorizontalAlignment="Center">我是DockPanel</TextBlock>
<WrapPanel>
<Button Width="200">WrapPanel</Button>
<Button Width="200">WrapPanel</Button>
<Button Width="200">WrapPanel</Button>
<Button Width="200">放不下啦WrapPanel</Button>
<Button Width="200">WrapPanel</Button>
</WrapPanel>
</DockPanel>
<Canvas Grid.Row="1" Grid.Column="1">
<TextBlock Canvas.Top="100" Canvas.Left="100">Canvas賬號:</TextBlock>
<TextBox Canvas.Top="100" Canvas.Left="200">Canvas請輸入賬號:</TextBox>
<TextBlock Canvas.Top="130" Canvas.Left="100">Canvas密碼:</TextBlock>
<TextBox Canvas.Top="130" Canvas.Left="200">Canvas請輸入密碼:</TextBox>
</Canvas>
</Grid>