本系列對實際項目中的XAML佈局場景進行總結,給出了較優化的自適應佈局解決方案,但願對你們有所幫助。html
下面繼續介紹局部佈局設計模式。express
繪圖,三維模型操做等須要工具的狀況,能夠使用帶分類的工具箱來放置工具,以達到較好的效果。實現工具箱效果的方法是使用ItemsControl的分組功能,並修改樣式和佈局。設計模式
首先,在最外層放置一個橫向滾動條爲Auto的ScrollViewer。裏面放一個ItemsControl,修改佈局爲UniformGrid,分兩行。ItemTemplate爲Button,設置合適的背景色和固定長度寬度(和字體字號相關),並設置左和上的Margin。Button的Content爲TextBlock,設置文字折行和截斷,並設置橫向和縱向對齊方式爲居中。TextBlock可設置合適的ToolTip,可以使用轉換器控制只在文字多的時候顯示。數據結構
而後,修改ItemTemplate的GroupStyle,GroupStyle的Panel爲橫向的StackPanel,ContainerStyle的Template放置一個橫向的StackPanel,分別放一個Rectangle作爲分割豎線和一個Grid放置真正的內容。Rectangle使用轉換器控制在第一個位置時不顯示。Grid分爲兩行,第0行爲Auto,第1行爲*,第0行爲一個放在Border裏的TextBlock,設置Margin和居中對齊。第1行是ItemsPresenter,設置Margin的右和下,以和前面呼應。app
最後,綁定到合適的數據源,數據源爲CollectionViewSource,其Source屬性爲一個數據集合,數據包含ClassName和Name屬性,ClassName是分類名稱,Name是條目名稱。在CollectionViewSource的GroupDescriptions屬性中加入名爲ClassName的PropertyGroupDescription。在XAML中設置合適的數據綁定便可。ide
<Window x:Class="BlendDemo.DP5" 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:BlendDemo" mc:Ignorable="d" Title="工具箱模式" Height="400" Width="500"> <Window.Resources> <local:SeparatorVisibilityConverter x:Key="SeparatorVisibilityConverter"/> <local:LengthVisibilityConverter x:Key="LengthVisibilityConverter"/> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Border Background="LightCyan"> <TextBlock Margin="5" Text="此處爲標題" TextTrimming="WordEllipsis"/> </Border> <Grid Grid.Row="1" Background="AliceBlue"/> <Grid Grid.Row="2"> <ScrollViewer HorizontalScrollBarVisibility="Auto"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Rows="2"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Button Background="AliceBlue" Width="70" MaxHeight="40" Margin="5,5,0,0"> <TextBlock Text="{Binding Name}" TextWrapping="Wrap" TextTrimming="WordEllipsis" HorizontalAlignment="Center" VerticalAlignment="Center" ToolTipService.Placement="Bottom" ToolTipService.InitialShowDelay="0" ToolTipService.BetweenShowDelay="0"> <TextBlock.ToolTip> <ToolTip Content="{Binding Name}" Visibility="{Binding Path=Name, Converter={StaticResource LengthVisibilityConverter}, ConverterParameter=10}"/> </TextBlock.ToolTip> </TextBlock> </Button> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.GroupStyle> <GroupStyle> <GroupStyle.Panel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </GroupStyle.Panel> <GroupStyle.ContainerStyle> <Style TargetType="GroupItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <StackPanel Orientation="Horizontal"> <Rectangle Fill="#9ca1ae" Width="2" Visibility="{Binding Path=Name, Converter={StaticResource SeparatorVisibilityConverter}}"/> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Border Background="#e6e9f2"> <TextBlock Text="{Binding Name}" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> <ItemsPresenter Grid.Row="1" Margin="0,0,5,5"/> </Grid> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </GroupStyle.ContainerStyle> </GroupStyle> </ItemsControl.GroupStyle> </ItemsControl> </ScrollViewer> </Grid> </Grid> </Window>
public class SeparatorVisibilityConverter : IValueConverter { public IList Data { get; set; } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (Data == null || Data.Count == 0) { return Visibility.Collapsed; } dynamic item = Data[0]; return ((string)value) == item.ClassName ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } public class LengthVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value == null) { return Visibility.Collapsed; } return ((string)value).Length < int.Parse((string)parameter) ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } public class DataItem { public string ClassName { get; set; } public string Name { get; set; } } public partial class DP5 : Window { public DP5() { InitializeComponent(); var cvs = new CollectionViewSource(); cvs.Source = new List<DataItem> { new DataItem { ClassName="分類1",Name="條目1.1"}, new DataItem { ClassName="分類1",Name="條目1.2"}, new DataItem { ClassName="分類1",Name="條目1.3爲長條目"}, new DataItem { ClassName="分類1",Name="條目1.4"}, new DataItem { ClassName="分類1",Name="條目1.5"}, new DataItem { ClassName="分類2",Name="條目2.1"}, new DataItem { ClassName="分類2",Name="條目2.2"}, new DataItem { ClassName="分類2",Name="條目2.3"}, new DataItem { ClassName="分類2",Name="條目2.4"}, new DataItem { ClassName="分類2",Name="條目2.5"}, new DataItem { ClassName="分類2",Name="條目2.6爲長條目長條目長條目長條目"}, new DataItem { ClassName="分類3",Name="條目3.1"}, new DataItem { ClassName="分類3",Name="條目3.2"}, new DataItem { ClassName="分類3",Name="條目3.3"}, new DataItem { ClassName="分類3",Name="條目3.4"}, }; cvs.GroupDescriptions.Add(new PropertyGroupDescription("ClassName")); DataContext = cvs; ((SeparatorVisibilityConverter)FindResource("SeparatorVisibilityConverter")).Data = (IList)cvs.Source; } }
製做選擇題考試界面時,須要達到紙質選擇題考試的效果。選項的排列方式分幾種狀況,選項文字少的時候排在一行上,文字不太多的時候分兩列排,文字較多的時候按一列排。工具
首先,構造合適的數據結構,並加入一些示例數據。編寫一個模板選擇器,用於根據選項的數據狀況選擇合適的顯示模板。所有選項的文字都小於4個字時,使用折行選項模板。所有選項的文字都小於10個字時,使用兩列選項模板。其餘狀況使用一列選項模板。佈局
而後,分別編寫模板。折行選項模板使用ItemsControl,佈局改成WrapPanel,數據模板使用StackPanel,其中放置序號,點和內容。兩列選項模板使用ItemsControl,佈局改成UniformGrid,列數爲2,數據模板使用Grid,分爲3列,前2列爲Auto,放置序號和點,最後1列爲*,放置顯示Content的TextBlock,並設置折行。一列選項模板使用ItemsControl,不修改佈局,數據模板和兩列的狀況相同。字體
最後,選擇題的總體顯示使用放置在ScrollViewer中的ItemsControl,不修改佈局。數據模板使用StackPanel,首先放置一個Grid,分爲3列,前2列爲Auto,放置序號和點,最後1列爲*,放置顯示問題的TextBlock,並設置折行。下面放一個ContentControl,Content綁定到合適的數據,並設置ContentTemplateSelector爲前文所述的模板選擇器。優化
<Window x:Class="BlendDemo.DP6" 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:BlendDemo" mc:Ignorable="d" Title="選擇題模式" Height="400" Width="650"> <Window.Resources> <DataTemplate x:Key="WrapOptionTemplate"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Margin="0,4,70,4"> <TextBlock Text="{Binding Index}"/> <TextBlock Text="." Margin="3,0,3,0"/> <TextBlock Text="{Binding Content}" TextWrapping="Wrap"/> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </DataTemplate> <DataTemplate x:Key="TwoColumnOptionTemplate"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Margin="0,4,0,4"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Index}"/> <TextBlock Grid.Column="1" Text="." Margin="3,0,3,0"/> <TextBlock Grid.Column="2" Text="{Binding Content}" TextWrapping="Wrap"/> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Rows="2"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </DataTemplate> <DataTemplate x:Key="OneColumnOptionTemplate"> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Margin="0,4,0,4"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Index}"/> <TextBlock Grid.Column="1" Text="." Margin="3,0,3,0"/> <TextBlock Grid.Column="2" Text="{Binding Content}" TextWrapping="Wrap"/> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </Window.Resources> <Grid> <ScrollViewer VerticalScrollBarVisibility="Auto"> <ItemsControl ItemsSource="{Binding}" Margin="20,20,20,3"> <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel Margin="0,0,0,7"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Index}" TextWrapping="Wrap"/> <TextBlock Grid.Column="1" Text="." TextWrapping="Wrap" Margin="0,0,5,0"/> <TextBlock Grid.Column="2" TextWrapping="Wrap" Text="{Binding Question}"/> </Grid> <ContentControl Content="{Binding OptionList}" Margin="33,10,33,10"> <ContentControl.ContentTemplateSelector> <local:OptionTemplateSelector/> </ContentControl.ContentTemplateSelector> </ContentControl> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </ScrollViewer> </Grid> </Window>
public class OptionItem { public string Index { get; set; } public string Content { get; set; } } public class ObjectiveTestItem { public int Index { get; set; } public string Question { get; set; } public List<OptionItem> OptionList { get; set; } } public class OptionTemplateSelector : DataTemplateSelector { public override DataTemplate SelectTemplate(object item, DependencyObject container) { var element = container as FrameworkElement; if (element == null) return null; var optionAnswerItems = item as List<OptionItem>; if (optionAnswerItems == null) return null; if (optionAnswerItems.All(i => i.Content.Length <= 4)) { return element.FindResource("WrapOptionTemplate") as DataTemplate; } if (optionAnswerItems.All(i => i.Content.Length <= 10)) { return element.FindResource("TwoColumnOptionTemplate") as DataTemplate; } return element.FindResource("OneColumnOptionTemplate") as DataTemplate; } } public partial class DP6 : Window { public DP6() { InitializeComponent(); var data = new List<ObjectiveTestItem>(); var item1 = new ObjectiveTestItem { Index = 1, Question = "短的選項", OptionList = new List<OptionItem> { new OptionItem { Index="A",Content="選項1" }, new OptionItem { Index="B",Content="選項2" }, new OptionItem { Index="C",Content="選項2" }, new OptionItem { Index="D",Content="選項2" } } }; data.Add(item1); var item2 = new ObjectiveTestItem { Index = 1, Question = "中等長度的選項", OptionList = new List<OptionItem> { new OptionItem { Index="A",Content="中等長度的選項1" }, new OptionItem { Index="B",Content="選項2" }, new OptionItem { Index="C",Content="選項2" }, new OptionItem { Index="D",Content="選項2" } } }; data.Add(item2); var item3 = new ObjectiveTestItem { Index = 1, Question = "長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項長的選項", OptionList = new List<OptionItem> { new OptionItem { Index="A",Content="長的選項長的選項長的選項長的選項長的選項長的選項長的選項1" }, new OptionItem { Index="B",Content="選項2" }, new OptionItem { Index="C",Content="選項2" }, new OptionItem { Index="D",Content="選項2" } } }; data.Add(item3); DataContext = data; } }