WPF 的內部世界(控制元件與佈局)
目錄
前言
為什麼要寫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>