Application=Code+Markup : The Dock and the Grid @ 錫安主機 :: 隨意窩 Xuite日誌
  • 關鍵字
  • google
  • 2007-03-22 09:58 Application=Code+Markup : The Dock and the Grid
    平均分數:0 顆星    投票人數:0
    我要評分:
     

    傳統的windows程式簡直都有同樣標準的排版。一個程式的選單就幾乎都在主畫面的上方!該選單可以說是dock在上面區域,假如程式有工具列,也會在上方,也會相當擠!而若還有狀態列,就會在下方!

     

    類似檔案總管的程式會在左邊的控制項中顯示樹狀目錄,但是選單和工作列以及狀態列就會各字有自己的優先權,因為他們大多繼承視窗的寬度!

     

    WPF 包含了一個DockPanel類別來完成你想要dock的東西,你可以如下建立:

     

    DockPanel dock=new DockPanel();

     

    假如你在window物件中建立一個dockpanel,那你可以這樣寫:

     

    Content =dock;

     

    這樣是很常見的,且當然可以在dockpanel中再加上其他panel! 可以如下加上一個control:

     

    dock.children.Add(ctrl);

     

    但是現在若你要指定ctrl要在視窗中的哪裡dock就會有點不一樣,若要放在右邊:

     

    DockPanel.SetDock(ctrl,Dock.Right);

     

    不要對這句話錯誤解讀:這並不是說與你命名為dock的物件有關,這是DockPanel的靜態方法,這兩個參數是你想要dock的物件,以及Dock的形式,可以是 Dock.Left,Dock.Top,Dock.Right,Dock.Buttom

     

    這和順序無關,你可以在還沒有建立DockPanel物件時就可以先指定dock的形式。

     

    SetDock 的呼叫還會有個附屬屬性(attached property)的使用!這會在第八章提出!現在你可以先想辦法瞭解為何以下的程式相當於呼叫setDock

     

    ctrl.SetValue(DockPanel.DockProperty, Dock.Right);

     

    SetValue是由DependencyObject所定義,且DockPanel.DockProperty是一個唯讀的欄位,這就是一種attached 屬性,且這個屬性和他的設定(Dock.Right)會被控制項存放著!當執行版面時,Dockpanel物件可以藉由呼叫下面而取得controlDock設定

     

    Dock dck=DockPanel.GetDock(ctrl);

     

    也相當於

     

    Dock dck =(Dock)ctrl.getValue(DockPanel.DockProperty);

     

     

    以下的程式會建立一個DockPanel17個按鈕。

     

    //---------------------------------------------------

    // DockAroundTheBlock.cs (c) 2006 by Charles Petzold

    //---------------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.DockAroundTheBlock

    {

        class DockAroundTheBlock : Window

        {

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new DockAroundTheBlock());

            }

            public DockAroundTheBlock()

            {

                Title = "Dock Around the Block";

     

                DockPanel dock = new DockPanel();

                Content = dock;

     

                for (int i = 0; i < 17; i++)

                {

                    Button btn = new Button();

                    btn.Content = "Button No. " + (i + 1);

                    dock.Children.Add(btn);

                    btn.SetValue(DockPanel.DockProperty, (Dock)(i % 4));

                }

            }

        }

    }

     

     

     

    在這個程式中,btn.SetValue(DockPanel.DockProperty, (Dock)(i % 4)); 巧妙的設計把按鈕動態排版,並且按照順序左上右下!你可以看到那是如何容易地放上去!而第17個按鈕並沒有dock,且佔滿了剩下個空間!而這個功效是來自於設定DockPanelLashChildFill 屬性為true所致且是default!而雖然程式有為最後一個按鈕設定排版方式但是會被忽略!

    可以嘗試設定

    dock.LastChildFill = false;

     

     

     

    就可以看出這不同!

    所以你知道DockPanel會由外往內排列!

     

    DockPanelChildren 一般都會延伸寬度或是高度和panel一樣,因為這些children的HorizontalAlignment和VerticalAlignment都會預設是stretch!

    改成如下會有啥改變:

     

    btn.HorizontalAlignment = HorizontalAlignment.Center;

     

     

     

    可以看到在上面的按鈕就會縮小到自己內容的大小,很明顯這並非一般user可以接受,而設定一個dock的控制項的margin屬性也是一樣!

     

    下面程式會建立一個比較傳統的,會建立menu,toolnar,statusbar,listboxtextbox控制項,但是因為這些控制項會在之後的一些章節討論,所以就大概看一下就好,因為textbox是最後一個,所以他會填滿其他控制項用不到的地方!

     

    //-----------------------------------------------

    // MeetTheDockers.cs (c) 2006 by Charles Petzold

    //-----------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Controls.Primitives;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.MeetTheDockers

    {

        public class MeetTheDockers : Window

        {

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new MeetTheDockers());

            }

            public MeetTheDockers()

            {

                Title = "Meet the Dockers";

     

                DockPanel dock = new DockPanel();

                Content = dock;

     

                // Create menu.

                Menu menu = new Menu();

                MenuItem item = new MenuItem();

                item.Header = "Menu";

                menu.Items.Add(item);

     

                // Dock menu at top of panel.

                DockPanel.SetDock(menu, Dock.Top);

                dock.Children.Add(menu);

     

                // Create tool bar.

                ToolBar tool = new ToolBar();

                tool.Header = "Toolbar";

     

                // Dock tool bar at top of panel.

                DockPanel.SetDock(tool, Dock.Top);

                dock.Children.Add(tool);

     

                // Create status bar.

                StatusBar status = new StatusBar();

                StatusBarItem statitem = new StatusBarItem();

                statitem.Content = "Status";

                status.Items.Add(statitem);

               

                // Dock status bar at bottom of panel.

                DockPanel.SetDock(status, Dock.Bottom);

                dock.Children.Add(status);

     

                // Create list box.

                ListBox lstbox = new ListBox();

                lstbox.Items.Add("List Box Item");

     

                // Dock list box at left of panel.

                DockPanel.SetDock(lstbox, Dock.Left);

                dock.Children.Add(lstbox);

     

                // Create text box.

                TextBox txtbox = new TextBox();

                txtbox.AcceptsReturn = true;

     

                // Add text box to panel & give it input focus.

                dock.Children.Add(txtbox);

                txtbox.Focus();

            }

        }

    }

     

     

     

    很遺憾這些程式並沒有完成,但是是因為這些控制項也沒做啥,但是你應該也知道皆下來該怎麼做,而你看到listboxtextbox之間應該要有個分隔的東西!

     

    一個間格器(splitter)可以用Grid panel來實做,但是這並非是使用Grid的理由!Grid可以行列的方式來規劃children的集合,他很像前面排30個按鈕的範例,然而通常當你在行列中排列控制項時你會想要他們排得很整齊!之前的範例是排不大整齊的!

     

    可以用下建立一個grid!

    Grid grid =new Grid();

     

    你也可以顯示那些格線:

     

    Grid.ShowGridLines=true;

     

    加上去之後會在行列間看到格線,這並沒有方法來改變他的style或是顏色寬度!假如你想要更多控制項在行列間可以被適當大小的列印,最好是使用Table而不是GridSystem.Windows.Documents.Table很像你在HTML中的表格。Grid對於排版比較嚴謹!

     

     

    你必須要告訴Grid有多少行列,並且這些行列的大小,每列的高度可以用下面的表示:

     

    • 固定的與裝置無關的單位
    • 根據每列最高
    • 根據剩下的空間(也許是在其他列的分配後所得到的剩餘空間)

     

    每一行的寬度也是很類似,可以有這三種選項,這需三種選項都有對應到三種的GridUnitType集合!Pixel / Auto / Sar ,star這個名詞是來自於HTML在表格中所代表分配所剩下的空間語法會給個*號!

     

    你可以用GridLength來指定每列的高度和每行的寬度,兩個建構子可以輸入,你可以指定:new GridLength(100);

    或是 new GridLength(100,GridUnitType.Pixel)

    或是 new GridLength(100,GridUnitType.Star) -->只剩下空間,前面數字代表是要和其他也有設定star        的做結合!

     

    你也可以指定Auto

     new GridLength(0,GridUnitType.Auto)

     

    然而在這種情形,數字會被忽略,所以你也可以用他的靜態欄位來設定:

    GridLength.Auto 也是會回傳GridLength物件!

     

    Grid有兩個屬性叫做RowDefinitionsColumnDefinitions 這些屬性是RowDefinitionCollectionColumnDefinitionsCollection集合的型態

     

    你可以為每個列建立RowDefinition,最重要的是Height屬性,也就是你設定的GridLength物件!他也有最大和最小高度的屬性設定,這是選擇性的!而ColumnDefinition比較重要的屬性當然是Width,也有最大最小值,也都是你設定的GridLength物件!

     

    但是不幸地~這些設定RowDefinitionsColumnDefinitions 這些屬性是相當冗長的!這些程式如下:

     

     

                RowDefinition ROWDEF = new RowDefinition();

                ROWDEF.Height = GridLength.Auto;

                grid.RowDefinitions.Add(ROWDEF);

               

                ROWDEF = new RowDefinition();

                ROWDEF.Height = new GridLength(33, GridUnitType.Star);

                grid.RowDefinitions.Add(ROWDEF);

     

                ROWDEF = new RowDefinition();

                ROWDEF.Height = new GridLength(150);

                grid.RowDefinitions.Add(ROWDEF);

     

                ROWDEF = new RowDefinition();

                ROWDEF.Height = new GridLength(67, GridUnitType.Star);

                grid.RowDefinitions.Add(ROWDEF);

     

    這上面分別設定了四列!

    依據Grid本身的設定,還是有可能會有空白處,這些值並不需要指定成百分比,Grid會把所有*的加起來然後平均計算!

     

    Column也是類似的設定,除了需要改成指定Width之外!若幸運的話也可放入for-loop中設定這些寬!

     

     

    GridunitType.starheightwidth屬性預設值都是1,所以當你想要平均的切割在行列間的空白時,你可以使用預設值就好,但是你仍然需要加入RowDefinition/ColumnDefinitions  物件到每個行列的集合中!程式可以簡化如下:

     

    Grid.RowDefinitions.Add(new RowDefinition());

     

    假如你只需要一行的Grid,你不用去定義ColumnDefinition,也不需要為了一列的Grid去定義RowDefinition,甚至若是只有一個格子的grid也不需要設定,

    若你要加入其他元件,可以和其他panel一樣,grid.children.add(ctrl);

    你也可以像下面定義一個grid:

     

    Grid.SetRow(ctrl,2);

    Grid.SetColumn(ctrl,5);

     

    這是三個列和六個欄的表格!這也是會呼叫使用到attached 屬性,預設值都是0,大多數的物件都可以在一般cell裡面存放,但你應該不會想這樣,假如你沒有看到你丟入grid中的任何東西,可能是因為他和其他一些東西放在同一個格子中。

     

    元件的HorizontalAlignmentVeriticalAlignment屬性都影響他在格子中的位置!一般而言,假如該格子中並沒有和元件一樣大小的話,該元件會改變大小和該格子一樣!

     

    下面會建立一個grid,視窗會把大小配合Grid,且也沒有改變大小的按鈕,有三列兩行,都用GridLength.Auto,這程式會計算兩個日期的差距!

     

    而一開始我寫了一個win32的程式,讓有需要的朋友使用,我可以使用DATETIMEPICK_CLASS,但是不幸地,在WPF的一開始版本並沒有相同的控制項,所以這個程式就只能用DateTime.TryParse方法來轉換字串成為日期!

     

     

     

     

     

    //--------------------------------------------------

    // CalculateYourLife.cs (c) 2006 by Charles Petzold

    //--------------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.CalculateYourLife

    {

        class CalculateYourLife : Window

        {

            TextBox txtboxBegin, txtboxEnd;

            Label lblLifeYears;

     

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new CalculateYourLife());

            }

            public CalculateYourLife()

            {

                Title = "Calculate Your Life";

                SizeToContent = SizeToContent.WidthAndHeight;

                ResizeMode = ResizeMode.CanMinimize;

     

                // Create the grid.

                Grid grid = new Grid();

                Content = grid;

     

                // Row and column definitions.

                for (int i = 0; i < 3; i++)

                {

                    RowDefinition rowdef = new RowDefinition();

                    rowdef.Height = GridLength.Auto;

                    grid.RowDefinitions.Add(rowdef);

                }

     

                for (int i = 0; i < 2; i++)

                {

                    ColumnDefinition coldef = new ColumnDefinition();

                    coldef.Width = GridLength.Auto;

                    grid.ColumnDefinitions.Add(coldef);

                }

     

                // First label.

                Label lbl = new Label();

                lbl.Content = "Begin Date: ";

                grid.Children.Add(lbl);

                Grid.SetRow(lbl, 0);

                Grid.SetColumn(lbl, 0);

     

                // First TextBox.

                txtboxBegin = new TextBox();

                txtboxBegin.Text = new DateTime(1980, 1, 1).ToShortDateString();

                txtboxBegin.TextChanged += TextBoxOnTextChanged;

                grid.Children.Add(txtboxBegin);

                Grid.SetRow(txtboxBegin, 0);

                Grid.SetColumn(txtboxBegin, 1);

     

                // Second label.

                lbl = new Label();

                lbl.Content = "End Date: ";

                grid.Children.Add(lbl);

                Grid.SetRow(lbl, 1);

                Grid.SetColumn(lbl, 0);

     

                // Second TextBox.

                txtboxEnd = new TextBox();

                txtboxEnd.TextChanged += TextBoxOnTextChanged;

                grid.Children.Add(txtboxEnd);

                Grid.SetRow(txtboxEnd, 1);

                Grid.SetColumn(txtboxEnd, 1);

     

                // Third label.

                lbl = new Label();

                lbl.Content = "Life Years: ";

                grid.Children.Add(lbl);

                Grid.SetRow(lbl, 2);

                Grid.SetColumn(lbl, 0);

     

                // Label for calculated result.

                lblLifeYears = new Label();

                grid.Children.Add(lblLifeYears);

                Grid.SetRow(lblLifeYears, 2);

                Grid.SetColumn(lblLifeYears, 1);

     

                // Set margin for everybody.

                Thickness thick = new Thickness(5); // ~1/20 inch.

                grid.Margin = thick;

     

                foreach (Control ctrl in grid.Children)

                    ctrl.Margin = thick;

     

                // Set focus and trigger the event handler.

                txtboxBegin.Focus();

                txtboxEnd.Text = DateTime.Now.ToShortDateString();

            }

            void TextBoxOnTextChanged(object sender, TextChangedEventArgs args)

            {

                DateTime dtBeg, dtEnd;

     

                if (DateTime.TryParse(txtboxBegin.Text, out dtBeg) &&

                    DateTime.TryParse(txtboxEnd.Text, out dtEnd))

                {

                    int iYears = dtEnd.Year - dtBeg.Year;

                    int iMonths = dtEnd.Month - dtBeg.Month;

                    int iDays = dtEnd.Day - dtBeg.Day;

     

                    if (iDays < 0)

                    {

                        iDays += DateTime.DaysInMonth(dtEnd.Year,

                                                1 + (dtEnd.Month + 10) % 12);

                        iMonths -= 1;

                    }

                    if (iMonths < 0)

                    {

                        iMonths += 12;

                        iYears -= 1;

                    }

                    lblLifeYears.Content =

                        String.Format("{0} year{1}, {2} month{3}, {4} day{5}",

                                      iYears, iYears == 1 ? "" : "s",

                                      iMonths, iMonths == 1 ? "" : "s",

                                      iDays, iDays == 1 ? "" : "s");

                }

                else

                {

                    lblLifeYears.Content = "";

                }

            }

        }

    }

     

     

    這個程式有為兩個textbox控制項註冊了textchange事件,會在文字有改變的時後計算,轉換成日期後會計算兩者差距,正常的話假如你兩個日期相減會得到一個TimeSpan的物件,但是那並非相當合適!...(以下是計算日期的寫法,忽略之)

     

     

    讓我們來看一下另外一種使用Grid的範例,這是一種可能在輸入表單時看到的格式:

     

    //---------------------------------------------

    // EnterTheGrid.cs (c) 2006 by Charles Petzold

    //---------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.EnterTheGrid

    {

        public class EnterTheGrid : Window

        {

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new EnterTheGrid());

            }

            public EnterTheGrid()

            {

                Title = "Enter the Grid";

                MinWidth = 300;

                SizeToContent = SizeToContent.WidthAndHeight;

     

                // Create StackPanel for window content.

                StackPanel stack = new StackPanel();

                Content = stack;

     

                // Create Grid and add to StackPanel.

                Grid grid1 = new Grid();

                grid1.Margin = new Thickness(5);

                stack.Children.Add(grid1);

     

                // Set row definitions.

                for (int i = 0; i < 5; i++)

                {

                    RowDefinition rowdef = new RowDefinition();

                    rowdef.Height = GridLength.Auto;

                    grid1.RowDefinitions.Add(rowdef);

                }

     

                // Set column definitions.

                ColumnDefinition coldef = new ColumnDefinition();

                coldef.Width = GridLength.Auto;

                grid1.ColumnDefinitions.Add(coldef);

     

                coldef = new ColumnDefinition();

                coldef.Width = new GridLength(100, GridUnitType.Star);

                grid1.ColumnDefinitions.Add(coldef);

     

                // Create labels and text boxes.

                string[] strLabels = { "_First name:", "_Last name:",

                                       "_Social security number:",

                                       "_Credit card number:",

                                       "_Other personal stuff:" };

     

                for(int i = 0; i < strLabels.Length; i++)

                {

                    Label lbl = new Label();

                    lbl.Content = strLabels[i];

                    lbl.VerticalContentAlignment = VerticalAlignment.Center;

                    grid1.Children.Add(lbl);

                    Grid.SetRow(lbl, i);

                    Grid.SetColumn(lbl, 0);

     

                    TextBox txtbox = new TextBox();

                    txtbox.Margin = new Thickness(5);

                    grid1.Children.Add(txtbox);

                    Grid.SetRow(txtbox, i);

                    Grid.SetColumn(txtbox, 1);

                }

     

                // Create second Grid and add to StackPanel.

                Grid grid2 = new Grid();

                grid2.Margin = new Thickness(10);

                stack.Children.Add(grid2);

     

                // No row definitions needed for single row.

                // Default column definitions are "star".

                grid2.ColumnDefinitions.Add(new ColumnDefinition());

                grid2.ColumnDefinitions.Add(new ColumnDefinition());

     

                // Create buttons.

                Button btn = new Button();

                btn.Content = "Submit";

                btn.HorizontalAlignment = HorizontalAlignment.Center;

                btn.IsDefault = true;

                btn.Click += delegate { Close(); };

                grid2.Children.Add(btn);    // Row & column are 0.

     

                btn = new Button();

                btn.Content = "Cancel";

                btn.HorizontalAlignment = HorizontalAlignment.Center;

                btn.IsCancel = true;

                btn.Click += delegate { Close(); };

                grid2.Children.Add(btn);

                Grid.SetColumn(btn, 1);     // Row is 0.

     

                // Set focus to first text box.

                (stack.Children[0] as Panel).Children[1].Focus();

           }

        }

    }

     

    這程式是依賴元件的預設行為來填滿視窗,StackPanel 佔滿了視窗版面,就和兩個grid一樣,在上面的grid panel第一個欄(label)使用GridLength.Auto,而第二欄(textbox)使用 GridLength.Star,這也就是說當你把視窗變大,textbox也會跟著變大,當你輸入的字便多也會變寬,而前五個列的高度都是看textbox的控制項,而為了要看起來有對齊,所以都是設定按鈕和標籤的HorizontalAlignment = HorizontalAlignment.Center;

     

    而在Grid中也可以讓某元件跨欄,可以使用靜態方法:Grid.SetRowSpanGrid.SetColumnSpan!下面是一些範例:

     

    maingrid.Children.add(ctrl);

    Grid.SetRow(ctrl,3);

    Grid.SetRowSpan(ctrl,2);

    Grid.SetColumn(ctrl,5);

    Grid.SetColumnSpan(ctrl,3);

     

    這樣的話,這個ctrl會佔據了六個cell,會從第3.4列,而欄數是5,6,7

     

    下面的程式也大概的介紹使用grid來排版,有6列四欄!前五列包含了labeltextbox,最後一列包含了按鈕,第一欄用來顯示label,靠右邊的兩欄顯示按鈕,textbox佔據了第2.3.4欄!

     

    //---------------------------------------------

    // SpanTheCells.cs (c) 2006 by Charles Petzold

    //---------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.SpanTheCells

    {

        public class SpanTheCells : Window

        {

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new SpanTheCells());

            }

            public SpanTheCells()

            {

                Title = "Span the Cells";

                SizeToContent = SizeToContent.WidthAndHeight;

     

                // Create Grid.

                Grid grid = new Grid();

                grid.Margin = new Thickness(5);

                Content = grid;

     

                // Set row definitions.

                for (int i = 0; i < 6; i++)

                {

                    RowDefinition rowdef = new RowDefinition();

                    rowdef.Height = GridLength.Auto;

                    grid.RowDefinitions.Add(rowdef);

                }

     

                // Set column definitions.

                for (int i = 0; i < 4; i++)

                {

                    ColumnDefinition coldef = new ColumnDefinition();

     

                    if (i == 1)

                        coldef.Width = new GridLength(100, GridUnitType.Star);

                    else

                        coldef.Width = GridLength.Auto;

     

                    grid.ColumnDefinitions.Add(coldef);

                }

     

                // Create labels and text boxes.

                string[] astrLabel = { "_First name:", "_Last name:",

                                       "_Social security number:",

                                       "_Credit card number:",

                                       "_Other personal stuff:" };

     

                for(int i = 0; i < astrLabel.Length; i++)

                {

                    Label lbl = new Label();

                    lbl.Content = astrLabel[i];

                    lbl.VerticalContentAlignment = VerticalAlignment.Center;

                    grid.Children.Add(lbl);

                    Grid.SetRow(lbl, i);

                    Grid.SetColumn(lbl, 0);

     

                    TextBox txtbox = new TextBox();

                    txtbox.Margin = new Thickness(5);

                    grid.Children.Add(txtbox);

                    Grid.SetRow(txtbox, i);

                    Grid.SetColumn(txtbox, 1);

                    Grid.SetColumnSpan(txtbox, 3);

                }

     

                // Create buttons.

                Button btn = new Button();

                btn.Content = "Submit";

                btn.Margin = new Thickness(5);

                btn.IsDefault = true;

                btn.Click += delegate { Close(); };

                grid.Children.Add(btn);

                Grid.SetRow(btn, 5);

                Grid.SetColumn(btn, 2);

     

                btn = new Button();

                btn.Content = "Cancel";

                btn.Margin = new Thickness(5);

                btn.IsCancel = true;

                btn.Click += delegate { Close(); };

                grid.Children.Add(btn);

                Grid.SetRow(btn, 5);

                Grid.SetColumn(btn, 3);

     

                // Set focus to first text box.

                grid.Children[1].Focus();

           }

        }

    }

     

     

     

     

    注意到這個程式設定六列的高為GridLength.Auto,有四欄除了第二欄外其寬度都是Auto,而第二欄是設定為 GridUnitType.Star

     

    當你把視窗變大,textbox也會變寬,其他的不會,因為只有他設定為 GridUnitType.Star

     

    這兩個按鈕會保持他的位置在右邊,且這視窗也在使用者輸入文字過長時自動變大。

     

    Grid panel也有讓你可以有垂直或是水平的splitter,也就是會有一條bar讓使用者可以移動調整兩個區域的空間。

     

    GridSplitter 類別經由Thumb類別而繼承自Control!他必須是Grid panel的一個children,程式會指定GridSplitter行列的位置。

     

    但是有一個奇特的地方:GridSplitter可以在一個cell裡面和其他元件共享,也有可能遮蓋到該cell上的元件或是該cell上的元件會覆蓋到GridSplitter,若你要避免這種問題,就需要設定元件的margin,或是把GridSplitter加到該cell的元件之後,讓他可以出現在前景中!

    預設值,GridSplitter 會有HorizontalAlignmentRightVeriticalAlignmentStretch,會讓垂直的splitter出現在cell的右邊! 預設的寬度是0,所以可以指定其一個寬度,你可以移動該splitter!在下面這個範例中你可以看到在第二列的右邊有一個splitter

    可以移動,來改變排版!

     

     

     

    //--------------------------------------------------

    // SplitNine.cs (c) 2006 by Charles Petzold

    //--------------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    namespace Petzold.SplitNine

    {

        public class SplitNine : Window

        {

            [STAThread]

            public static void Main()

            {

                Application app = new Application();

                app.Run(new SplitNine());

            }

            public SplitNine()

            {

                Title = "Split Nine";

     

                Grid grid = new Grid();

                Content = grid;

     

                // Set row and column definitions.

                for (int i = 0; i < 3; i++)

                {

                    grid.ColumnDefinitions.Add(new ColumnDefinition());

                    grid.RowDefinitions.Add(new RowDefinition());

                }

     

                // Create 9 buttons.

                for (int x = 0; x < 3; x++)

                    for (int y = 0; y < 3; y++)

                    {

                        Button btn = new Button();

                        btn.Content = "Row " + y + " and Column " + x;

                        grid.Children.Add(btn);

                        Grid.SetRow(btn, y);

                        Grid.SetColumn(btn, x);

                    }

     

                // Create splitter.

                GridSplitter split = new GridSplitter();

                split.Width = 6;

                grid.Children.Add(split);

                Grid.SetRow(split, 1);

                Grid.SetColumn(split, 1);

            }

        }

    }

     

     

     

    若是改成以下~你會更覺得有感覺:

     

    Grid.SetRow(split, 0);

                Grid.SetColumn(split, 1);

                Grid.SetRowSpan(split, 3);

     

     

     

     

     

    圖中的splitter變長了~可以移動控制寬度!

     

     

    我們改回控制一個cellGridSplitter其最重要的屬性就是HorizontalAlignmentVerticalAlignment,這些屬性會控制splitter出現在cell中是水平或是垂直!且會有哪些行列被影響!在預設值時,GridSplitter會出現在cell的右邊,移動splitter會改變splitter左右欄寬度的分配!

    你可以改變如下:

     split.HorizontalAlignment = HorizontalAlignment.Center;

     

     

     

     

    可以看到splitter就會變到中間,且當你改變splitter的位置時,就會直接改變鄰近cell的,不會改變本cell的寬度!這種用法應該是比較少見的,但確實他也是GridSplitter的功能之一,因為若是中間沒有按鈕,寬度應該就會正常,看起來就不會這麼怪!

     

    你已經知道splitter如何改變他噁寬度,你可以使用GridSplitterResizeBehavior屬性來覆寫他的行為!~你可以設定選擇GridResizeBehavior集合!這些成員會參考到哪些欄位被splitter影響,這些成員有CurrentAndNext(通常用在splitter是靠右),PreviousAndCurrent(通常用在splitter是靠左),以及PreviousAndBext (通常用在splitter是在置中)還有預設值的BasedOnAlignment(沒有任何對於splitter的靠的位置有關去做覆寫)

     

     GridSplitter split = new GridSplitter();

                split.HorizontalAlignment = HorizontalAlignment.Stretch;

                split.VerticalAlignment = VerticalAlignment.Top;

                split.Height = 6;

     

               

                grid.Children.Add(split);

                //Grid.SetRow(split, 1);

                Grid.SetRow(split, 0);

                Grid.SetColumn(split, 0);

                Grid.SetColumnSpan(split, 3);

     

     

     

    所以假如VeriticalAlignment =Stretch splitter 垂直,影響寬度

             若是HorizonalAlignmentStretchsplitter為水平!影響高度!

    你也可以用ResizeDirection屬性來改變splitter的外觀,你可以設定GridResizeDirection的成員:Column,Rows,Auto!

     

    例如:split.ResizeDirection = GridResizeDirection.Columns;

     

    可以看到雖然splitter是水平的,但是卻無法往上下拉~你只能改變他的寬度!

     

    GridSplitter是相當的多才多藝!你可以設定HorizontalAlignmentVeriticalAlignment成為Stretch來讓sokitter填滿整個cell

     

      split.HorizontalAlignment = HorizontalAlignment.Stretch;

                split.VerticalAlignment = VerticalAlignment.Stretch;

     

     

    或是你也不要使用HorizontalAlignment / VerticalAlignment =stretch,讓splitter依照width或是height來便成一個盒子!

     

    我的建議相當簡單:不要把GidSplitter放到一個cell中且和一個元件共用,並且切開一個Grid專門給切割的功能,假如你想要有一個垂直的splitter,可以建立一個Grid有三個欄為,把中間的欄位的寬度使用GridLength.Auto,並且把splitter放到這個欄位中,假如你想要有一個水平的splitter,可以建立一個三個列的Grid,讓中間列的高度=GridLength.Auto,並且把splitter放到這個欄位中。這些案例中,你都可以把你想要的放在另外兩個欄位中,包含其他的Grid panel!

     

    //-----------------------------------------------

    // SplitTheClient.cs (c) 2006 by Charles Petzold

    //-----------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Input;

    using System.Windows.Media;

     

    class SplitTheClient : Window

    {

        [STAThread]

        public static void Main()

        {

            Application app = new Application();

            app.Run(new SplitTheClient());

        }

        public SplitTheClient()

        {

            Title = "Split the Client";

     

            // Grid with vertical splitter.

            Grid grid1 = new Grid();

            grid1.ColumnDefinitions.Add(new ColumnDefinition());

            grid1.ColumnDefinitions.Add(new ColumnDefinition());

            grid1.ColumnDefinitions.Add(new ColumnDefinition());

            grid1.ColumnDefinitions[1].Width = GridLength.Auto;

            Content = grid1;

     

            // Button at left of vertical splitter.

            Button btn = new Button();

            btn.Content = "Button No. 1";

            grid1.Children.Add(btn);

            Grid.SetRow(btn, 0);

            Grid.SetColumn(btn, 0);

     

            // Vertical splitter.

            GridSplitter split = new GridSplitter();

            split.ShowsPreview = true;

            split.HorizontalAlignment = HorizontalAlignment.Center;

            split.VerticalAlignment = VerticalAlignment.Stretch;

            split.Width = 6;

            grid1.Children.Add(split);

            Grid.SetRow(split, 0);

            Grid.SetColumn(split, 1);

     

            // Grid with horizontal splitter.

            Grid grid2 = new Grid();

            grid2.RowDefinitions.Add(new RowDefinition());

            grid2.RowDefinitions.Add(new RowDefinition());

            grid2.RowDefinitions.Add(new RowDefinition());

            grid2.RowDefinitions[1].Height = GridLength.Auto;

            grid1.Children.Add(grid2);

            Grid.SetRow(grid2, 0);

            Grid.SetColumn(grid2, 2);

     

            // Button at top of horizontal splitter.

            btn = new Button();

            btn.Content = "Button No. 2";

            grid2.Children.Add(btn);

            Grid.SetRow(btn, 0);

            Grid.SetColumn(btn, 0);

     

            // Horizontal splitter.

            split = new GridSplitter();

            split.ShowsPreview = true;

            split.HorizontalAlignment = HorizontalAlignment.Stretch;

            split.VerticalAlignment = VerticalAlignment.Center;

            split.Height = 6;

            grid2.Children.Add(split);

            Grid.SetRow(split, 1);

            Grid.SetColumn(split, 0);

     

            // Bottom at bottom of horizontal splitter.

            btn = new Button();

            btn.Content = "Button No. 3";

            grid2.Children.Add(btn);

            Grid.SetRow(btn, 2);

            Grid.SetColumn(btn, 0);

        }

    }

     

     

     

     

     

    這個程式會設定GridSplitterShowsPreview屬性為true,當你移到splitter,該cell不會改變尺寸,直到你放開滑鼠,注意你可以使用tab鍵來讓不同的splitter得到focus,並且可以用上下左右鍵來移動!若是這些反應不如預期,你可以檢查一下 KeyboradIncrement 屬性!

     

    你可能已經注意到,當你改變這些視窗的大小,兩邊的內容都會一起改變大小,但有時候你想要再改變視窗大小時,內容可以不要更著改大小,類似檔案總管!

     

    下面程式介紹這些技術和一些其他的!我用windows 1.0寫了第一版,大概是在Microsoft Systems Journal 1987 5月提出!在印象中,這程式是在自動化排版的首創研究,原始程式有六個label3scrollbar,讓你可以選擇紅藍綠的程度,當你把視窗變大或變小,程式就會改變,且重新定位labelscrollbar!會用很多計算,也長的很醜!

     

     

    WPF Scrollbar經由RangeBase繼承ControlRangeBase是一個抽象類別,也是ProgressBarSlider的父類別)。Scrollbar可以依照Orientation屬性設定垂直或是水平放置,而Value屬性可以在Minimum屬性以及Maximum屬性範圍間跳動,點選scrollbar的箭頭可以改變Value屬性,且可由SmallChange的值來設定最小跳動值!若要設定一次跳動的最大值,可以設定LargoChange!這些都是double的值!

     

    Scrollbar類別會從Scrollbar繼承ValueChanged事件,並且會定義一個Scroll事件!Scroll事件

    會傳送ScrollEventType列舉中的成員中的一些資訊,讓你可以知道使用者正在操縱scrollbar的哪一個部分!舉例來說,若你的程式並不能知曉大移動,他就有可能會忽略所有ScrollEventType型態的事件,ThumbTrack就會跳到最後面,得到ScrollbarEventType.EndScroll

     

     

    下面的程式會先建立一個Grid panel,只為了要去做一個垂直的splitter,第一個cell會包含一個grid,包含了六個標籤和三個scrollbar,中間的cell包含了GridSplitter,最後一個cell包含一個StackPanel,用來顯示背景色!

     

     

     

    //---------------------------------------------------

    // ScrollCustomColors.cs (c) 2006 by Charles Petzold

    //---------------------------------------------------

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Controls.Primitives;

    using System.Windows.Media;

     

    class ColorScroll : Window

    {

        ScrollBar[] scrolls = new ScrollBar[3];

        TextBlock[] txtValue = new TextBlock[3];

        Panel pnlColor;

     

        [STAThread]

        public static void Main()

        {

            Application app = new Application();

            app.Run(new ColorScroll());

        }

        public ColorScroll()

        {

            Title = "Color Scroll";

            Width = 500;

            Height = 300;

     

            // GridMain contains a vertical splitter.

            Grid gridMain = new Grid();

            Content = gridMain;

     

            // GridMain column definitions.

            ColumnDefinition coldef = new ColumnDefinition();

            coldef.Width = new GridLength(200, GridUnitType.Pixel);

            gridMain.ColumnDefinitions.Add(coldef);

     

            coldef = new ColumnDefinition();

            coldef.Width = GridLength.Auto;

            gridMain.ColumnDefinitions.Add(coldef);

     

            coldef = new ColumnDefinition();

            coldef.Width = new GridLength(100, GridUnitType.Star);

            gridMain.ColumnDefinitions.Add(coldef);

     

            // Vertical splitter.

            GridSplitter split = new GridSplitter();

            split.HorizontalAlignment = HorizontalAlignment.Center;

            split.VerticalAlignment = VerticalAlignment.Stretch;

            split.Width = 6;

            gridMain.Children.Add(split);

            Grid.SetRow(split, 0);

            Grid.SetColumn(split, 1);

     

            // Panel on right side of splitter to display color

            pnlColor = new StackPanel();

            pnlColor.Background = new SolidColorBrush(SystemColors.WindowColor);

            gridMain.Children.Add(pnlColor);

            Grid.SetRow(pnlColor, 0);

            Grid.SetColumn(pnlColor, 2);

     

            // Secondary grid at left of splitter

            Grid grid = new Grid();

            gridMain.Children.Add(grid);

            Grid.SetRow(grid, 0);

            Grid.SetColumn(grid, 0);

     

            // Three rows for label, scroll, and label.

            RowDefinition rowdef = new RowDefinition();

            rowdef.Height = GridLength.Auto;

            grid.RowDefinitions.Add(rowdef);

     

            rowdef = new RowDefinition();

            rowdef.Height = new GridLength(100, GridUnitType.Star);

            grid.RowDefinitions.Add(rowdef);

     

            rowdef = new RowDefinition();

            rowdef.Height = GridLength.Auto;

            grid.RowDefinitions.Add(rowdef);

     

            // Three columns for Red, Green, and Blue.

            for (int i = 0; i < 3; i++)

            {

                coldef = new ColumnDefinition();

                coldef.Width = new GridLength(33, GridUnitType.Star);

                grid.ColumnDefinitions.Add(coldef);

            }

     

            for (int i = 0; i < 3; i++)

            {

                Label lbl = new Label();

                lbl.Content = new string[] { "Red", "Green", "Blue" }[i];

                lbl.HorizontalAlignment = HorizontalAlignment.Center;

                grid.Children.Add(lbl);

                Grid.SetRow(lbl, 0);

                Grid.SetColumn(lbl, i);

     

                scrolls[i] = new ScrollBar();

                scrolls[i].Focusable = true;

                scrolls[i].Orientation = Orientation.Vertical;

                scrolls[i].Minimum = 0;

                scrolls[i].Maximum = 255;

                scrolls[i].SmallChange = 1;

                scrolls[i].LargeChange = 16;

                scrolls[i].ValueChanged += ScrollOnValueChanged;

                grid.Children.Add(scrolls[i]);

                Grid.SetRow(scrolls[i], 1);

                Grid.SetColumn(scrolls[i], i);

     

                txtValue[i] = new TextBlock();

                txtValue[i].TextAlignment = TextAlignment.Center;

                txtValue[i].HorizontalAlignment = HorizontalAlignment.Center;

                txtValue[i].Margin = new Thickness(5);

                grid.Children.Add(txtValue[i]);

                Grid.SetRow(txtValue[i], 2);

                Grid.SetColumn(txtValue[i], i);

            }

     

            // Initialize scroll bars.

            Color clr = (pnlColor.Background as SolidColorBrush).Color;

            scrolls[0].Value = clr.R;

            scrolls[1].Value = clr.G;

            scrolls[2].Value = clr.B;

     

            // Set initial focus.

            scrolls[0].Focus();

        }

        void ScrollOnValueChanged(object sender, RoutedEventArgs args)

        {

            ScrollBar scroll = sender as ScrollBar;

            Panel pnl = scroll.Parent as Panel;

            TextBlock txt = pnl.Children[1 +

                                    pnl.Children.IndexOf(scroll)] as TextBlock;

     

            txt.Text = String.Format("{0}n0x{0:X2}", (int)scroll.Value);

            pnlColor.Background =

                new SolidColorBrush(

                    Color.FromRgb((byte) scrolls[0].Value,

                                  (byte) scrolls[1].Value,(byte) scrolls[2].Value));

        }

    }

     

     

     

     

     

     

    ValueChanged  event handler會隨著Scrollbar改變值而更新TextBlock,且會重新計算新的Color

    Grid很類似的是UniformGrid,其列欄的高度都一樣,且也沒有RowDefinitionColumnDefinition物件,但你可以設定其Rows/Columns屬性來設定列數和欄數,且他也沒有attached屬性,當你加入一個成員,他會順序性地佔據第一列的cell,接下來是第二列

    Rows或是Columns都可以是0,且UniformGrid可以針對children的數量給予比較適當的值!

     

    你可在下一張看到我在實做著名的 14-15 puzzle 時看到Uniform的範例,還有Jeu de Tacquin!

    夏洛克 / Xuite日誌 / 回應(0) / 引用(0) / 好文轉寄
    回應