65.9K
CodeProject 正在变化。 阅读更多。
Home

简单但有用的 WPF 容器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (6投票s)

2011年12月2日

CPOL

2分钟阅读

viewsIcon

39205

downloadIcon

1613

利用 Grid 和 GridSplitter 组合成 SplitContainer

- 张戈登

引言

多年来,我从 CodeProject 中受益匪浅,现在是时候贡献一点力量了,希望我的成功和错误能对他人有所帮助。我才开始研究 WPF 不到一个月,所以可能有很多错误。因此,请自行承担风险免费使用。但任何评论或改进都将不胜感激。

废话不多说…

互联网上已经有 2、3 个不错的 WPF SplitContainer其中一个 来自开源 CodePlex,你可以从中获取源代码。但对我来说,它太复杂了(因为我对 WPF 还不熟悉)。在这里,我将利用 WPF Grid GridSplitter 来创建自定义控件,其代码非常简单,反正它能满足我的需求。

Using the Code

SplitContainer 类来自自定义控件

它非常简单,该类继承自 Control,并且将具有四个 DependencyProperties

  • Child1PropertyPanel1 用于容纳控件,注册为 UIPropertyMetadata
  • Child2PropertyPanel2 用于容纳控件,注册为 UIPropertyMetadata
  • SplitterDistanceProperty – 用于设置分割器的距离,double 值
  • OrientationProperty – 用于设置分割器的方向,枚举值。

以下是代码片段

namespace SplitContainer 
{ 
/// <summary> 
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file. 
/// 
/// Step 1a) Using this custom control in a XAML file that exists in the current project. 
/// Add this XmlNamespace attribute to the root element of the markup file where it is 
/// to be used: 
/// 
/// xmlns:MyNamespace="clr-namespace:SplitContainer" 
/// 
/// 
/// Step 1b) Using this custom control in a XAML file that exists in a different project. 
/// Add this XmlNamespace attribute to the root element of the markup file where it is 
/// to be used: 
/// 
/// xmlns:MyNamespace="clr-namespace:SplitContainer;assembly=SplitContainer" 
/// 
/// You will also need to add a project reference from the project 
/// where the XAML file lives 
/// to this project and Rebuild to avoid compilation errors: 
/// 
/// Right click on the target project in the Solution Explorer and 
/// "Add Reference"->"Projects"->[Select this project] 
/// 
/// 
/// Step 2) 
/// Go ahead and use your control in the XAML file. 
/// 
/// <MyNamespace:CustomControl1/> 
/// 
/// </summary> 
public enum Orientation 
{ 
Vertical = 0, 
Horizontal 
}; 
public class SplitContainer : Control 
{ 
public static readonly DependencyProperty Child1Property; 
/// <summary>Identifies the <see cref="Child2"/> dependency property.</summary> 
public static readonly DependencyProperty Child2Property; 
public static readonly DependencyProperty SplitterDistanceProperty; 
public static readonly DependencyProperty OrientationProperty; 
static SplitContainer() 
{ 
DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitContainer), 
  new FrameworkPropertyMetadata(typeof(SplitContainer))); 
Child1Property = DependencyProperty.Register
		("Child1", typeof(UIElement), typeof(SplitContainer), 
new UIPropertyMetadata(null)); 
Child2Property = DependencyProperty.Register(
  "Child2", typeof(UIElement), typeof(SplitContainer), 
new UIPropertyMetadata(null)); 
//It also set the default value to 100 
SplitterDistanceProperty = DependencyProperty.Register(
  "SplitterDistance", typeof(double), typeof(SplitContainer), 
new FrameworkPropertyMetadata(100.0, 
  FrameworkPropertyMetadataOptions.AffectsMeasure | 
  FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 
OrientationProperty = DependencyProperty.Register("Orientation", 
  typeof(Orientation), typeof(SplitContainer), 
new UIPropertyMetadata(null)); 
} 
public override void OnApplyTemplate() 
{ 
base.OnApplyTemplate(); 
} 
public UIElement Child1 
{ 
get { return (UIElement)GetValue(Child1Property); } 
set { SetValue(Child1Property, value); } 
} 
/// <summary>Gets or sets the right or bottom child of the
/// <see cref="SplitContainer"/>, depending
/// on <see cref="Orientation"/>. This is a dependency property.</summary> 
/// <value>If <see cref="Orientation"/>
/// is <see cref="System.Windows.Controls.Orientation.Vertical"/>,
/// the bottom child of the <see cref="SplitContainer"/>. 
/// If <see cref="Orientation"/> is
/// <see cref="System.Windows.Controls.Orientation.Horizontal"/>,
/// the right child of the <see cref="SplitContainer"/>.</value> 
public UIElement Child2 
{ 
get { return (UIElement)GetValue(Child2Property); } 
set { SetValue(Child2Property, value); } 
} 
public double SplitterDistance 
{ 
get { return (double)GetValue(SplitterDistanceProperty); } 
set { SetValue(SplitterDistanceProperty, value); } 
} 
public Orientation Orientation 
{ 
get { return (Orientation)GetValue(OrientationProperty); } 
set { SetValue(OrientationProperty, value); } 
} 
} 
}

分割器样式

我们为 Vertical Horizontal 设置样式,如下所示,使其看起来更美观

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
<Color x:Key ="Color1">#E3EFFF</Color> 
<Color x:Key="Color5">#C0DBFF</Color> 
<!-- GridSplitter Brushes --> 
<LinearGradientBrush x:Key="GridSplitterHorzBackgroundBrush" 
			EndPoint="0,1" StartPoint="0,0"> 
<GradientStop Offset="0" Color="{StaticResource Color1}"/> 
<GradientStop Offset="1" Color="{StaticResource Color5}"/> 
</LinearGradientBrush> 
<LinearGradientBrush x:Key="GridSplitterVertBackgroundBrush" 
			EndPoint="0,1" StartPoint="1,1"> 
<GradientStop Offset="0" Color="{StaticResource Color5}"/> 
<GradientStop Offset="1" Color="{StaticResource Color1}"/> 
</LinearGradientBrush> 
<!-- Vertical GridSplitter --> 
<Style x:Key="VerticalGridSplitterStyle" TargetType="{x:Type GridSplitter}"> 
<Setter Property="UIElement.SnapsToDevicePixels" Value="True"/> 
<Setter Property="UIElement.Focusable" Value="False"/> 
<Setter Property="ShowsPreview" Value="True"/> 
<Setter Property="VerticalAlignment" Value="Stretch"/> 
<!--<Setter Property="ResizeBehavior" Value="PreviousAndNext"/> 
<Setter Property="ResizeDirection" Value="Columns"/>--> 
<Setter Property="Height" Value="Auto"/> 
<Setter Property="Width" Value="5"/> 
<Setter Property="Background" Value="{DynamicResource GridSplitterVertBackgroundBrush}"/> 
</Style> 
<!-- Horizontal GridSplitter --> 
<Style x:Key="HorizontalGridSplitterStyle" TargetType="{x:Type GridSplitter}"> 
<Setter Property="UIElement.SnapsToDevicePixels" Value="True"/> 
<Setter Property="UIElement.Focusable" Value="False"/> 
<Setter Property="ShowsPreview" Value="True"/> 
<Setter Property="HorizontalAlignment" Value="Stretch"/> 
<!--<Setter Property="ResizeBehavior" Value="PreviousAndNext"/> 
<Setter Property="ResizeDirection" Value="Rows"/>--> 
<Setter Property="Height" Value="5"/> 
<Setter Property="Width" Value="Auto"/> 
<Setter Property="Background" Value="{DynamicResource GridSplitterHorzBackgroundBrush}"/> 
</Style> 
</ResourceDictionary>

SplitContainer 的 XAML

以下是在 XAML 中使用 Grid GridSplitter 构建 splitcontainer 的方法

<Style TargetType="{x:Type local:SplitContainer}"> 
<Setter Property="Template"> 
<Setter.Value> 
<ControlTemplate TargetType="{x:Type local:SplitContainer}"> 
<Grid> 
<Grid.ColumnDefinitions> 
<ColumnDefinition Name="Column_1" 
  Width="{Binding RelativeSource={RelativeSource TemplatedParent}, 
		Path=SplitterDistance}"/> 
<ColumnDefinition Name="Column_2" Width="*"/> 
</Grid.ColumnDefinitions> 
<Grid.RowDefinitions > 
<RowDefinition Name="Row_1" Height="*"/> 
<RowDefinition Name="Row_2" Height="0"/> 
</Grid.RowDefinitions> 
<!--Define border--> 
<Rectangle Name="Border_Outer" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" 
  Grid.ColumnSpan="2" Stroke="{DynamicResource GridSplitterVertBackgroundBrush}" 
  Fill="{DynamicResource GridSplitterVertBackgroundBrush}" 
		StrokeThickness="1" Margin="0,0,0,0"/> 
<Rectangle Name="Border_Inner" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" 
  Grid.ColumnSpan="2" Stroke="Black" Fill="White" StrokeThickness="1" Margin="0,1,0,1"/> 
<!--Define the Child1 and Child2--> 
<ListView Name="child_1" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" 
  BorderBrush="{DynamicResource GridSplitterVertBackgroundBrush}" 
  BorderThickness="0.5,0.5,0.5,0.5" Margin="1,1,5,1" /> 
<ListView Name="child_2" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" 
  BorderBrush="Black" BorderThickness="0.5,0.5,0.5,0.5" Margin="0.,1,1,1" /> 
<!--Define Spliter--> 
<GridSplitter Name="gridSpliter" Grid.Column="0" Grid.Row="0" 
  Grid.RowSpan="2" Style="{StaticResource VerticalGridSplitterStyle}"/> 
<ContentPresenter Name="CP_1" Margin="0,1,5,1" Grid.Column="0" 
  Grid.Row="0" Grid.RowSpan="2" ContentSource="Child1"/> 
<ContentPresenter Name="CP_2" Margin="0,1,0,1" Grid.Column="1" 
  Grid.Row="0" Grid.RowSpan="2" ContentSource="Child2"/> 
</Grid> 
<ControlTemplate.Triggers> 
<Trigger Property="local:SplitContainer.Orientation" Value="Horizontal"> 
<Setter TargetName="Column_1" Property="Width" Value="*"/> 
<Setter TargetName="Column_2" Property="MaxWidth" Value="0"/> 
<Setter TargetName="Row_1" Property="Height" 
  Value="{Binding RelativeSource={RelativeSource TemplatedParent}, 
		Path=SplitterDistance}"/> 
<Setter TargetName="Row_2" Property="Height" Value="*"/> 
<Setter TargetName="Border_Outer" Property="Stroke" 
  Value="{DynamicResource GridSplitterHorzBackgroundBrush}"/> 
<Setter TargetName="Border_Outer" Property="Fill" 
  Value="{DynamicResource GridSplitterHorzBackgroundBrush}"/> 
<Setter TargetName="Border_Inner" Property="Stroke" 
  Value="{DynamicResource GridSplitterHorzBackgroundBrush}"/> 
<Setter TargetName="Border_Inner" Property="Margin" Value="1,0,1,0"/> 
<Setter TargetName="child_1" Property="Margin" Value="1,1,1.0,5"/> 
<Setter TargetName="child_1" Property="Grid.RowSpan" Value="1"/> 
<Setter TargetName="child_1" Property="Grid.ColumnSpan" Value="2"/> 
<Setter TargetName="child_1" Property="BorderBrush" 
  Value="{DynamicResource GridSplitterHorzBackgroundBrush}"/> 
<Setter TargetName="child_2" Property="Margin" Value="1,0,1,0"/> 
<Setter TargetName="child_2" Property="Grid.Row" Value="1"/> 
<Setter TargetName="child_2" Property="Grid.Column" Value="0"/> 
<Setter TargetName="child_2" Property="Grid.RowSpan" Value="1"/> 
<Setter TargetName="child_2" Property="Grid.ColumnSpan" Value="2"/> 
<Setter TargetName="gridSpliter" Property="Style" 
  Value="{StaticResource HorizontalGridSplitterStyle}"/> 
<Setter TargetName="gridSpliter" Property="Grid.RowSpan" Value="1"/> 
<Setter TargetName="gridSpliter" Property="Grid.Row" Value="0"/> 
<Setter TargetName="gridSpliter" Property="Grid.ColumnSpan" Value="2"/> 
<Setter TargetName="gridSpliter" Property="VerticalAlignment" Value="Bottom"/> 
<Setter TargetName="CP_1" Property="Grid.RowSpan" Value="1"/> 
<Setter TargetName="CP_1" Property="Grid.ColumnSpan" Value="2"/> 
<Setter TargetName="CP_1" Property="Margin" Value="1,1,1.5,5"/> 
<Setter TargetName="CP_2" Property="Margin" Value="0,1,1,1"/> 
<Setter TargetName="CP_2" Property="Grid.RowSpan" Value="1"/> 
<Setter TargetName="CP_2" Property="Grid.ColumnSpan" Value="2"/> 
<Setter TargetName="CP_2" Property="Grid.Row" Value="1"/> 
<Setter TargetName="CP_2" Property="Grid.Column" Value="0"/> 
</Trigger> 
</ControlTemplate.Triggers> 
</ControlTemplate> 
</Setter.Value> 
</Setter> 
</Style>

我们使用属性触发器来更改 splitContainer 的方向。我们还在每个子控件内部绘制一些矩形,使其看起来像 WindowsForms SplitContainer。这两行代码非常重要

<ContentPresenter Name="CP_1" Margin="0,1,5,1" Grid.Column="0" 
  Grid.Row="0" Grid.RowSpan="2" ContentSource="Child1"/> 
<ContentPresenter Name="CP_2" Margin="0,1,0,1" Grid.Column="1" 
  Grid.Row="0" Grid.RowSpan="2" ContentSource="Child2"/>

它们将 Child1 Child2 暴露给应用程序,并使其容纳控件。

在应用程序中使用 SplitContainer

使用起来非常简单,你创建应用程序,在 Window 下,只需将 SplitContainer 放在它下面,就像演示项目中的那样

<Grid> 
<SC:SplitContainer Orientation="Horizontal" SplitterDistance="200"> 
<SC:SplitContainer.Child1> 
<SC:SplitContainer Orientation="Vertical"> 
</SC:SplitContainer> 
</SC:SplitContainer.Child1> 
<SC:SplitContainer.Child2> 
<StackPanel Orientation="Horizontal"> 
<Button Content="Button on Second Panel" Margin="0" Width="276" /> 
</StackPanel> 
</SC:SplitContainer.Child2> 
</SC:SplitContainer> 
</Grid>

为了让默认分割器显示颜色,你需要将以下行放在你的 App.XAML 中,如下所示:

<Application.Resources> 
<ResourceDictionary> 
<ResourceDictionary.MergedDictionaries> 
<ResourceDictionary Source="pack://application:,,,/
    SplitContainer;component/Themes/Splitter.xaml"/> 
</ResourceDictionary.MergedDictionaries> 
</ResourceDictionary> 
</Application.Resources>

就这样了。

© . All rights reserved.