WPF 排版基礎


一、WPF 排版基礎

WPF使用控制面板來進行排版,控制面板實際上是一種可以放入WPF界面元素的容器。當用戶把界面元素放入控制面板后,WPF會自動把這些界面元素放在它認為合適的地方。WPF開發人員需要根據自己對用戶界面的要求來選擇合適的控制面板。

WPF中的基本控制面板類如圖3-1所示,這些類都是從Panel類中派生出來的,Panel本身是UIElement。

WPF中的StackPanel、DockPanel、WrapPanel及Grid則支持另外一種排版機制,使用這些排版類,不需要設置控件在視窗上的絕對位置,只需要說明其相對位置,WPF的控制面板類負責最終確定這些控件的位置。

控制面板中的Children屬性是一個UIElement的集合,即所有從UIElement中派生出來的UI元素都可以加入到控制面板中,也就是說,控制面板中既可以放入各種圖形,又可以放入像Button、TextBlock這樣的常規控件。由於Panel本身是從UIElement中派生出來的,所以Panel可以含有Panel。

二、堆積面板(StackPanel)

StackPanel是最簡單的一種控制面板,它把其中的UI元素按橫向或縱向堆積排列。

其中,可以使用Orientation屬性來操作子元素的排列。

Orientation的屬性有兩個值:Vertical和Horizontal。

當Orientation設為Vertical時,StackPanel中的元素占滿StackPanel的水平方向空間,即其中元素的寬度Width是相同的。當Orientation設為Horizontal時,StackPanel中的元素則占滿垂直方向空間,其中元素的高度Height都相同。

StackPanel里還有一個屬性:LayoutTransform。這個屬性可以把StackPanel內的UI元素一起放大、縮小或旋轉。

1.在XAML中使用StackPanel

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        Icon="https://demosc.chinaz.net/Files/pic/iconsico/8254/e10.ico"
        mc:Ignorable="d"
        Title="無題" Height="200" Width="200">
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <StackPanel Margin="5" Orientation="Vertical">
            <TextBlock FontSize="20" Text="昨夜星辰昨夜風," Background="Wheat"/>
            <TextBlock FontSize="20" Text="畫樓西畔桂堂東。" Background="Wheat"/>
            <TextBlock FontSize="20" Text="身無彩鳳雙飛翼," Background="Wheat"/>
            <TextBlock FontSize="20" Text="心有靈犀一點通。" Background="Wheat"/>
        </StackPanel>
    </ScrollViewer>
</Window>

2.在C#中使用StackPanel
StackPanel和其中元素間的父子關系(這里的父子關系指的是一種包容關系,而不是繼承關系)更加清晰。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        this.Title = "無題";
        StackPanel sp = new StackPanel();
        this.Content = sp;
        sp.Children.Add(CreatTextBlock("昨夜星辰昨夜風,"));
        sp.Children.Add(CreatTextBlock("畫樓西畔桂堂東。"));
        sp.Children.Add(CreatTextBlock("身無彩鳳雙飛翼,"));
        sp.Children.Add(CreatTextBlock("心有靈犀一點通。"));
    }

    private TextBlock CreatTextBlock(String text)
    {
        TextBlock textBlock = new TextBlock();
        textBlock.FontSize = 20;
        textBlock.Foreground = new SolidColorBrush(Color.FromRgb(200, 100, 100));
        textBlock.Text = text;
        return textBlock;
    }
}

有時候需要把視窗的大小根據其中的內容來自動調整,方法是設置視窗的SizeToContent屬性. SizeToContent是枚舉類型。

若你對StackPanel和窗口間或UI元素與UI元素間的距離不滿意,可以調整StackPanel或界面元素的Margin屬性。

Margin的類型為Thickness,可以對其左、右、上、下分別設置。

上面談到當視窗里的內容比視窗小時,可以用設置視窗的SizeToContent的屬性讓視窗根據其中的內容進行自動調整。但更常遇到的是另外一種情況:視窗里的內容比計算機可用屏幕的尺寸還要大,這時候就要在視窗內加滾動條。

WPF提供ScrollBar和ScrollViewer來實現屏幕滾動,ScrollViewer比ScrollBar在滾動視窗中的內容時用起來方便。

ScrollViewer有兩個控制滾動條顯示方式的相關屬性:HorizontalScrollBarVisibility和VerticalScrollBarVisibility。

VerticalScrollBarVisibility用來控制豎直滾動條,而HorizontalScrollBarVisibility則用來控制水平滾動條。

HorizontalScrollBarVisibility的默認值是Disbaled,而VerticalScrollBarVisibility的默認值是“auto”。

ScrollViewer提供了8個用於控制每次滾動范圍的方法:LineUp、LineDown、LineLeft、LineRight、PageUp、PageDown、PageLeft和PageRight這些方法可以在程序中模擬人工操作滾動條。

當視窗的大小,滾動條的位置或視點發生改變時,ScrollViewer會產生ScrollChanged事件。

三、WrapPanel

WrapPanel是和StackPanel最相近的一個控制面板,StackPanel把其中的UI元素按行或列排列,而WrapPanel則可根據其中UI元素的尺寸和其自身可能的大小自動地把其中的UI元素排列到下一行或下一列。Windows操作系統中的文件管理器就是這樣的例子,當選擇圖標顯示模式時,文件管理器將根據視窗的寬度和文件圖標的大小,自動布置文件在其中的位置。

WrapPanel中有三個屬性ItemWidth、ItemHeight和Orientation,這三個屬性來安排其中UI元素的位置:

● ItemWidth:定義所有子元素的寬度。每個子元素在其中顯示的寬度由子元素自己的Width及HorizontalAlignment屬性確定,若子元素的寬度大於ItemWidth,WrapPanel就會自動剪掉子元素超過ItemWidth的部分。ItemWidth的默認值為NaN,在這種情況下,WrapPanel使用其中最大子元素的寬度來作為列的寬度。

● ItemHeight:定義所有子元素的高度。每個子元素在其中顯示的高度由子元素自己的Height及VerticalAlignment確定,若子元素的寬度大於ItemHeight,WrapPanel就會自動剪掉子元素超過ItemHeight的部分。ItemHeight的默認值為NaN,在這種情況下,WrapPanel使用其中最大子元素的高度來作為行的高度。

● Orientation:和StackPanel相同,唯一的區別是在WrapPanel中,Orientation的默認值為Horizontal(水平放置)。

對照一下與前面StackPanel的區別:
a.可用鼠標調整視窗的大小,當視窗的寬度只能放置1個矩形時,WrapPanel的效果和StackPanel的效果一樣。

b.當視窗的寬度小於其中矩形寬度的總和時,排在右面的矩形會自動排列到下一行;當視窗的寬度大於其中矩形寬度的總和時,排在下一行最左邊的矩形會自動進入上一行。

四、停靠面板(DockPanel)

停靠(Dock)這個概念並不是新的東西,在傳統的視窗應用程序中,通常在視窗的頂端有菜單,最下面有狀態行,有時還有工具條,工具條一般放在菜單的下面等。控制這些UI元素在視窗中的位置就是停靠,只不過在WPF之前,需要自己做大量工作,或使用第三方提供的工具。

DockPanel定義一個區域,在此區域中,您可以使子元素通過描點的形式排列,這些對象位於 Children 屬性中。停靠面板類似於WinForm中控件的Dock屬性。DockPanel會對每個子元素進行排序,並將根據指定的邊進行停靠,多個停靠在同側的元素則按順序排序。在DockPanel中,指定停靠邊的控件,會根據定義的順序占領邊角,所有控件絕不會交疊。

默認情況下,后添加的元素只能使用剩余空間,無論對DockPanel的最后一個子元素設置任何停靠值,該子元素都將始終填滿剩余的空間。如果不希望最后一個元素填充剩余區域,可以將DockPanel屬性LastChildFill設置為false,還必須為最后一個子元素顯式指定停靠方向。

WPF的DockPanel定義了一個Dock附加屬性,其類型為Dock,是枚舉類型,可取Left、Right、Top和Bottom四個值。注意Dock並沒有一個Fill或Center的值,當LastChildFill屬性設為True時,DockPanel用最后一個加入的UI元素填充所有剩下的地方。

在XAML中設置UI元素的語法為(以TextBlock為例,其他UI元素都一樣):

<TextBlock DockPanel.Dock=Dock.Top>
...
</TextBlock>

在C#里,可以用兩種方法來設定UI元素在DockPanel中的停靠位置,一種是用UI元素中的SetValue()方法,比如設定控件myControl的停靠位置:

myControl.SetValue(DockPanel.DockProperty,Dock.Top);

另外一種方法是在DockPanel里設置:

DockPanel.SetDock(myControl,Dock.Top);

實例:

點擊查看代碼
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp"
        Icon="https://demosc.chinaz.net/Files/pic/iconsico/8254/e10.ico"
        mc:Ignorable="d"
        Title="無題" Height="500" Width="800" WindowStartupLocation="CenterScreen">
    <DockPanel>
        <Button DockPanel.Dock="Left" Content="ButtonLeft"></Button>
        <Button DockPanel.Dock="Top" Content="ButtonTop"></Button>
        <Button DockPanel.Dock="Right" Content="ButtonRight"></Button>
        <Button DockPanel.Dock="Bottom" Content="ButtonBottom"></Button>
        <Button  Content="ButtonTop"></Button>
    </DockPanel>
</Window>

六、表格式面板(Grid)

Grid控制面板是WPF中最常用的控制面板,當用Visual Studio或ExpressBlend創建WPF程序時,Visual Studio會自動在XAML中添加 標記。

Grid排版和HTML表格排版是一樣的,盡管現在有無數的網站,提供的內容豐富多彩,但網頁版面設計都是基於表格排版的。

WPF中還有一個和Grid的類似的類——Table。Table類常用於文檔,而Grid則多用於用戶界面。

在XAML中定義Grid的語法為:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
</Grid>

上面的這段XAML定義了3行3列的Grid 。在C#里創建Grid的語法和創建其他對象一樣:

Grid myGrid = new Grid();
for(int i = 0; i < 3; i++)
{
    RowDefinition rowdef = new RowDefinition();
    myGrid.RowDefinitions.Add(rowdef);
}
for(int j = 0; j < 3; j++)
{
    ColumnDefinition coldef = new ColumnDefinition();
    myGrid.ColumnDefinitions.Add(coldef);
}

Grid類中含有行容器RowDefinitions和列容器ColumnDefinitions。

行和列分別用RowDefinition和ColumnDefinition類來表達。

1.設定UI元素在Grid中的位置

設定Grid中UI元素在Grid中的相對位置要使用Grid的兩個附加屬性Row和Column,若Grid里面含有Label,則使用XAML設置Label的行列位置的語法為:

<Label Grid.Row="1" Grid.Column="1"  VerticalContentAlignment="Center" FontSize="20">I'm here.</Label>

上面的XAML句子把標簽“I'm here.”放在Grid的第二行、第二列的位置。

注意:Grid中的行列號為從0開始的整數,設定UI元素在Grid中的行列號,該行列號一定要在Grid所定義的范圍內;換句話說,若你定義了3行3列的Grid,則不能設定UI元素的行列號為大於或等於3。

2.設定Grid行或列的尺寸

和普通UI元素可以設定Height和Width屬性不同,RowDefinition類和ColumnDefinition類中相應的屬性的類型不是Double,而是System.Window.GridLength。

Grid調整Grid行列尺寸的方法有如下三種:

● 絕對尺寸 把Grid行列大小尺寸設為一個數值,這時Grid的行列尺寸不會隨着其中UI元素的大小進行自動調整。

● 自動尺寸 把Grid的Height和Width設為Auto。這時WPF會根據Grid中的UI元素自動調整其行列的高度或寬度。其原則為:Grid的行高度由該行中元素的最大高度決定,Grid的列寬度由該列中元素的最大寬度決定。使用這種方法可以保證Grid中的UI元素不會只顯示一部分。

● 按比例分割行列尺寸 把有限的平面大小按照一定的比例划分行的高度或列的寬度,其比例的數值可以是浮點數。如第一列的寬度設為“”、第二列的寬度設為“1.2”、第三列的寬度設為“2.5*”,等等。

WPF默認設置行的高度和列的寬度為1個*,即每行或每列的大小一樣。在XAML中設定行列尺寸的語法:

<RowDefinition Height="100"/>  (絕對比例)
<RowDefinition Height="Auto"/>  (自動尺寸)
<RowDefinition Height="2*"/>  (按比例尺寸)

在C#中設定行列尺寸的語法:

RowDefinition rowdef = new RowDefinition();
rowdef.Height = new GridLength(100, GridUnitType.Pixel);  (絕對比例)
rowdef.Height = GridLength.Auto;  (自動尺寸)
rowdef.Height = new GridLength(2, GridUnitType.Star);  (按比例尺寸)

實例:

3. 元素橫跨多個行列時的設定

有時候需要設置某個UI元素橫跨多行或多列,Grid支持這種操作。

用XAML設置跨行和跨列的例子如下:

<Label Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalContentAlignment="Center" FontSize="40">I'm here.~~~~~</Label>

用C#設置跨行和跨列的例子如下:

TextBlock tb = new TextBlock();
tb.Width = 100;
tb.Height = 100;
Grid.SetRowSpan(tb, 2);  // 跨越兩行
Grid.SetColumnSpan(tb, 2);  // 跨越兩列

4.在Grid中保持多行或多列大小的一致性

有時候希望Grid中的一些行或列無論在什么情況下都保持相同的高度或寬度,使用RowDefinition和ColumnDefinition中的SharedSizedGroup可以達到這一目的。

首先在Grid層面設置了一個IsSharedSizeScope屬性為True,表明在Grid中,會有行或列使用SharedSizeGroup屬性。在ColumnDefinition中,筆者把第1列和第3列的SharedSizeGroup設為“mgroup1”,把第2列和第4列的SharedSize-Group設為“mgroup2”。SharedSizedGroup可以設為任何字符串,具有相同Shared-SizedGrou字符串的行或列,其高度或寬度相同。字符串大小寫敏感,如mgroup1和mGroup1是不同的字符串。

設定SharedSizeGroup后,位於同一組中的列的寬度保持相同的比例。

六、UniformGrid

有時候覺得使用Grid過於煩瑣的話,可以設置行列數,行列的寬度或高度等屬性,實際上就是需要一種大小相等在平面上均勻排列的表格。UniformGrid支持這種表格的排版類,它是一般Grid的一個特例。這時,不必要定義行列的集合;不必設定每行的列數或行數,UniformGrid總是把行數和列數設為相等;每個單元只含有一個子元素,所以也不必用附加屬性來說明哪個元素位於哪個單元。

七、畫布面板(Canvas)

過去視窗界面排版都是用絕對坐標,比如,用戶設計的對話框,每個控件在對話框中的位置,都是用x,y值確定的;又比如用戶要在窗口內顯示文字或圖形,也要指出所要顯示圖素的確切位置。WPF也支持這種精確定位的排版,Canvas面板就是為此設計的。

Canvas中的坐標值是一種與設備無關的單位值,其坐標原點位於Canvas的左上角。X坐標軸從原點指向屏幕的右邊,Y坐標軸從原點指向屏幕的下方。UI元素並沒有指定其位置的坐標屬性,要把某個元素放在Canvas上的某個位置,需要使用Canvas的Left和Top附加屬性。

XAML:

<Canvas>
    <TextBlock Canvas.Left="15" Canvas.Top="15" Background="Aquamarine">Location1</TextBlock>
    <TextBlock Canvas.Left="100" Canvas.Top="100" Background="Bisque">Location2</TextBlock>
</Canvas>

C#:

public MainWindow()
{
    InitializeComponent();
    TextBlock tb1 = new TextBlock();
    tb1.Text = "Location1";
    tb1.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
    Canvas.SetLeft(tb1, 15);
    Canvas.SetTop(tb1,15);
    TextBlock tb2 = new TextBlock();
    tb2.Text = "Location2";
    tb2.Background = new SolidColorBrush(Color.FromRgb(0, 255, 0));
    Canvas.SetLeft(tb2, 100);
    Canvas.SetTop(tb2, 100);
    Canvas cv = new Canvas();
    cv.Width = 400;
    cv.Height = 400;
    cv.Background = new SolidColorBrush(Color.FromRgb(255,200,0));
    cv.Children.Add(tb1);
    cv.Children.Add(tb2);
    this.AddChild(cv);
}

在WPF中,Canvas排版常用在對圖形元素的版面布置上。雖然可以設置圖形元素在Canvas上的左上角或右下角的位置,但若設置了圖形元素的左上角的位置,同時又設置了圖形元素右下角的位置,WPF就會自動忽略所設的右下角的位置坐標。

七、小結

本節介紹了WPF中的排版系統,重點在StackPanel、WrapPanel、DockPanel、Grid、UniformGrid等元素的使用方法。WPF允許這些面板類可以互相包含,甚至在UI元素(如Button)中也可以包含面板元素作為其自身的子元素,這些排版元素組合起來使得WPF很容易支持各種版面設計。還有與排版相關的一些UI類,如LayoutTransform,該類支持FrameWorkElement在版面上的變換。還有一些處在控件和版面設計之間的類,如TabControl;使用TabControl的例子如IE 7.0。這個類可用作版面設計,但在屬性上應把它歸於UI控件元素。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM