對比MFC資源文件談談WPF布局方式
MFC方式
對於傳統的MFC基於UI的應用程序設計通常分兩步走,首先是設計UI,使用的是RC文件,然后是代碼文件,對RC文件進行操作,如下面Figure 1 的基於對話框的應用程序,其對應的代碼如Figure 2所示,這就是MFC時代的所見即所得,如大家所見,每個控件的代碼都和位置都是寫死的坐標,這樣會帶來的問題是當你改變系統運行的的DPI或者軟件需要支持本地化的時候,由於有的語言對於同樣的意思需要比較長的文字表示,就會帶來文字顯示不下或者顯示不完整的情況。解決問題的方式想必大家都遇見過,手動的去拖拽控件的大小,然后再在不同的語言的系統上進行測試,整個過程異常繁瑣,而且錯誤百出。

Figure 1 MFC對話框 UI設計
IDD_MAINWIDNOW DIALOGEX 0, 0, 201, 250 STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "SendMessage" MENU IDC_SENDMESSAGE CLASS "SendMessage" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN CONTROL 137,IDC_CAPTURE,"Static",SS_BITMAP,162,68,21,17 GROUPBOX "Message",IDC_STATIC,6,94,188,131 COMBOBOX IDC_WINDOWSMESSAGE,72,106,118,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP EDITTEXT IDC_LPARAM,72,124,118,14,ES_AUTOHSCROLL EDITTEXT IDC_WPARAM,72,142,118,14,ES_AUTOHSCROLL LTEXT "Results from window:",IDC_STATIC,12,162,70,8 EDITTEXT IDC_RESULTS,12,174,178,45,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER PUSHBUTTON "Send message",IDC_SENDMESSAGE,107,229,87,14 GROUPBOX "Window",IDC_STATIC,6,7,186,83 CONTROL "Message:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,108,54,8,WS_EX_RIGHT CONTROL "wParam:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,126,54,8,WS_EX_RIGHT CONTROL "lParam:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,144,54,8,WS_EX_RIGHT CONTROL "Handle:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,18,54,8,WS_EX_RIGHT EDITTEXT IDC_WINDOW_HANDLE,72,16,118,14,ES_AUTOHSCROLL CONTROL "Window name:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,34,54,8,WS_EX_RIGHT EDITTEXT IDC_WINDOW_NAME,72,32,118,14,ES_AUTOHSCROLL EDITTEXT IDC_WINDOW_CLASS,72,48,118,14,ES_AUTOHSCROLL CONTROL "Window class:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,12,50,54,8,WS_EX_RIGHT PUSHBUTTON "Highlight Window",IDC_HIGHLIGHT_WINDOW,12,72,96,14 END
Figure 2 MFC對話框 UI代碼
WPF方式
微軟當然知道傳統MFC程序的問題,終極的解決方案就是WPF布局,WPF的主流的最簡單的布局方式有一下5種。
-Canvas
-StackPanel
-WrapPanel
-DockPanel
-Grid
舉例來說DockPanel,簡單的幾行代碼,對5個button進行了布局,大家可以看到,整個布局過程沒有一個坐標,這樣帶來的還出就是改變窗口大小或者DPI,國際化,都不需要任何額外的工作,生活如此美好。
<Window x:Class="WpfApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Hi There!" Height="350" Width="525"> <DockPanel> <Button DockPanel.Dock=" Top" Background=" pink" >1 (Top)</Button> <Button DockPanel.Dock=" Left" Background=" Orange" >2 (Left)</Button> <Button DockPanel.Dock=" Right" Background=" Yellow" >3 (Right)</Button> <Button DockPanel.Dock=" Bottom" Background=" Lime" >4 (Bottom)</Button> <Button Background=" Aqua" >5</Button> </DockPanel> </Window>
Figure 3 簡單WPF布局方式

Figure 4 簡單WPF布局方式效果圖
當然WPF的能力不止於此,應用WPF完全可以做出基於MFC沒辦法做出來,或者很難做出來的效果,而且極其簡單明了,這就是框架的力量,下面我們來看一個稍微復雜點的例子,當然這樣的例子在網上隨處可見,不作為奇,但是已經可以說明一定的問題。我們先來看看效果圖,然后是源代碼。

Figure 5 一個稍微復雜的WPF布局方式效果圖
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Hi There!"
>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File"/>
<MenuItem Header="Edit"/>
<MenuItem Header="View"/>
<MenuItem Header="Project"/>
<MenuItem Header="Build"/>
<MenuItem Header="Data"/>
<MenuItem Header="Tools"/>
<MenuItem Header="Window"/>
<MenuItem Header="Community"/>
<MenuItem Header="Help"/>
</Menu>
<StackPanel Name="buttonBar" Orientation="Horizontal" DockPanel.Dock="Right">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
<Button Name="pane1Button" MouseEnter="pane1Button_MouseEnter">
Toolbox
</Button>
<Button Name="pane2Button" MouseEnter="pane2Button_MouseEnter">
Solution Explorer
</Button>
</StackPanel>
<Grid Name="parentGrid" Grid.IsSharedSizeScope="True">
<Grid Name="layer0" MouseEnter="layer0_MouseEnter">
<!-- Define four rows: -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Define two columns: -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Background="Blue" Foreground="White"
HorizontalContentAlignment="Center">
<Label.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Label.LayoutTransform>
SolidMango
</Label>
<GroupBox Grid.Row="1" Grid.Column="0" Background="White"
Header="Recent Projects">...</GroupBox>
<GroupBox Grid.Row="2" Grid.Column="0" Background="White"
Header="Getting Started">...</GroupBox>
<GroupBox Grid.Row="3" Grid.Column="0" Background="White" Header="Headlines">...</GroupBox>
<GroupBox Grid.Row="1" Grid.Column="1" Grid.RowSpan="3" Background="White" Header="Online Articles">
<ListBox>
<ListBoxItem>Item #1</ListBoxItem>
<ListBoxItem>Item #2</ListBoxItem>
<ListBoxItem>Item #3</ListBoxItem>
<ListBoxItem>Item #4</ListBoxItem>
</ListBox>
</GroupBox>
</Grid>
<Grid Name="layer1" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="column1" Width="auto"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1" MouseEnter="pane1_MouseEnter"
Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" >
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Button Width="26" Name="pane1Pin" DockPanel.Dock="Right" Click="pane1Pin_Click" Background="White">
<Image Name="pane1PinImage" Source="pinHorizontal.gif"/>
</Button>
<TextBlock Padding="8" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" DockPanel.Dock="Left">Toolbox</TextBlock>
</DockPanel>
<ListBox Padding="10" Grid.Row="1">
<ListBoxItem>Button</ListBoxItem>
<ListBoxItem>CheckBox</ListBoxItem>
<ListBoxItem>ComboBox</ListBoxItem>
<ListBoxItem>Label</ListBoxItem>
<ListBoxItem>ListBox</ListBoxItem>
</ListBox>
</Grid>
<GridSplitter Width="5" Grid.Column="1" HorizontalAlignment="Left"/>
</Grid>
<Grid Name="layer2" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition SharedSizeGroup="column2" Width="auto"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1" MouseEnter="pane2_MouseEnter" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Button Width="26" Name="pane2Pin" DockPanel.Dock="Right" Click="pane2Pin_Click" Background="White">
<Image Name="pane2PinImage" Source="pinHorizontal.gif"/>
</Button>
<TextBlock Padding="8" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource {x:Static SystemColors.ActiveCaptionTextBrushKey}}" DockPanel.Dock="Left">Solution Explorer</TextBlock>
</DockPanel>
<ToolBar Grid.Row="1">
<Button>
<Image Source="iconVSproperties.bmp"/>
</Button>
<Button>
<Image Source="iconVSshowall.bmp"/>
</Button>
<Button>
<Image Source="iconVSrefresh.bmp"/>
</Button>
</ToolBar>
<TreeView Grid.Row="2">
<TreeViewItem Header="My Solution">
<TreeViewItem Header="Project #1"/>
<TreeViewItem Header="Project #2"/>
<TreeViewItem Header="Project #3"/>
</TreeViewItem>
</TreeView>
</Grid>
<GridSplitter Width="5" Grid.Column="1" HorizontalAlignment="Left"/>
</Grid>
</Grid>
</DockPanel>
</Window>
Figure 6 一個稍微復雜的WPF布局方式源代碼
總結
對比兩種UI設計方式,讀者不難看出,WPF對於UI設計的優越性是MFC RC方式無可匹敵的,無論是從生產力還是界面的美觀性上來講,差一代的技術,差的還是很明顯的,WPF的UI設計方式受HTML的啟發,在里面能看到很多的HTML的影子,當年微軟以一個輕量級的WPF--silverlight 劍指 HTML,雖然silverlight 已經一敗塗地,但是WPF仍然是Windows UI 設計的首選。
