Silverlight CustomerPanel

对于 Silverlight(也适用于 WPF),用来在布局中调整元素大小和定位元素的技术分为两个步骤:先执行“度量”处理过程,然后执行“排列”处理过程.

重写 MeasureOverride,以便在类参与 Silverlight 布局系统时实现类的自定义布局大小调整行为。实现时应执行以下任务:

  • 循环访问类的特定子对象集合(属于布局的一部分),并对每个子对象调用 Measure

  • 直接在每个子级上获取 DesiredSize(该对象将在调用 Measure 后设置为一个属性)。

  • 根据子对象所需的连续测量大小,计算父级所需的净大小。

 protected override Size MeasureOverride(Size constraint);constraint,指定给子对象可用的大小

 

要自定义布局处理的排列处理过程的控件作者(或面板作者)应重写 ArrangeOverride。实现模式应在每个可见子对象上调用 Arrange,然后将每个子对象的最终所需大小作为 finalRect 参数进行传递。包含可见子对象的容器应在每个子对象上调用 Arrange;否则,将不会呈现子对象。

  protected override Size ArrangeOverride(Size arrangeBounds);父级中此对象应用来排列自身及其子元素的最终区域

附上代码:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace ProSilverlight.CustomerPanel
{
public class WrapBreakPanel : Panel
{
public Orientation Orientation
{
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}


//定义一个依赖属性
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(WrapBreakPanel),
new PropertyMetadata(Orientation.Horizontal));


//定义一个附加属性
public static DependencyProperty LineBreakBeforeProperty = DependencyProperty.RegisterAttached("LineBreakBefore", typeof(bool),
typeof(WrapBreakPanel), null);

public static void SetLineBreakBefore(UIElement element, Boolean value)
{
element.SetValue(LineBreakBeforeProperty, value);
}

public static Boolean GetLineBreakBefore(UIElement element)
{
return (bool)element.GetValue(LineBreakBeforeProperty);
}

/// <summary>
/// 进行度量
/// </summary>
/// <param name="constraint">指定给子对象可用的大小</param>
/// <returns></returns>
protected override Size MeasureOverride(Size constraint)
{
if (Orientation != Orientation.Horizontal)
throw new NotImplementedException("The WrapBreakPanel only supports horizontal orientation.");

//当前行的大小
Size currentLineSize = new Size();
//容器的大小
Size panelSize = new Size();

//遍历容器的所有子对象
foreach (UIElement element in this.Children)
{
// 更新 UIElement 的 DesiredSize,
element.Measure(constraint);
//获取此 UIElement 在布局过程的测量处理过程中计算的大小。
Size desiredSize = element.DesiredSize;

// 判断当前行的大小加上控件的测量大小是否大约设置给子对象的大小,如果小于,则直接换行
//或者判断属性LineBreak是否手动换行
if ((currentLineSize.Width + desiredSize.Width > constraint.Width) || (WrapBreakPanel.GetLineBreakBefore(element)))
{
//当前容器的高所有行的和
panelSize.Height += currentLineSize.Height;
//容器的宽是当前容器的宽和行的宽进行对比,数值大的为容器的width
panelSize.Width = Math.Max(currentLineSize.Width, panelSize.Width);
currentLineSize = desiredSize;
// 如果控件过宽,则使用最合适的宽度
if (desiredSize.Width > constraint.Width)
{
// Make the width of the element the new desired width.
panelSize.Width = Math.Max(desiredSize.Width, panelSize.Width);
}
}
else
{
//直接给当前行的Width累加
currentLineSize.Width += desiredSize.Width;
// 高度不需要叠加,因为没有换行,则对行的高度和测量的高度进行对比
currentLineSize.Height = Math.Max(desiredSize.Height, currentLineSize.Height);
}
}
//比较最后一行的宽度和当前容器的宽度
panelSize.Width = Math.Max(currentLineSize.Width, panelSize.Width);
//叠加最后一行的高度
panelSize.Height += currentLineSize.Height;
return panelSize;
}

/// <summary>
/// 进行排列处理
/// </summary>
/// <param name="arrangeBounds">父级中此对象应用来排列自身及其子元素的最终区域</param>
/// <returns></returns>
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (Orientation != Orientation.Horizontal)
throw new NotImplementedException("The WrapBreakPanel only supports horizontal orientation.");

//每一行的大小
Size currentLineSize = new Size();
double totalHeight = 0;

// 遍历所有子对象
foreach (UIElement element in this.Children)
{
//获取此 UIElement 在布局过程的测量处理过程中计算的大小。
Size desiredSize = element.DesiredSize;

// 判断当前行的大小加上控件的测量大小是否大约设置给子对象的大小,如果小于,则直接换行
//或者判断属性LineBreak是否手动换行
if ((currentLineSize.Width + desiredSize.Width > arrangeBounds.Width)|| (WrapBreakPanel.GetLineBreakBefore(element)))
{
// 累加高度
totalHeight += currentLineSize.Height;
currentLineSize = new Size();
}

// 确保行的高度是最高的
currentLineSize.Height = Math.Max(desiredSize.Height, currentLineSize.Height);

//定位子对象并确定 UIElement 的大小。为其子元素实现
//自定义布局的父对象应从其布局重写实现调用此方法以形成递归布局更新。
element.Arrange(new Rect(currentLineSize.Width, totalHeight,
element.DesiredSize.Width, element.DesiredSize.Height));

// 叠加宽度,到下一个元素
currentLineSize.Width += desiredSize.Width;
}

// 获得容器的总高度
totalHeight += currentLineSize.Height;
return new Size(arrangeBounds.Width, totalHeight);
}


private void arrangeLine(double y, double lineHeight, int start, int end)
{
double x = 0;
UIElementCollection children = this.Children;
for (int i = start; i < end; i++)
{
UIElement child = children[i];
child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineHeight));
x += child.DesiredSize.Width;
}
}

}

}


使用方法如下:

<UserControl x:Class="ProSilverlight.CustomerPanel.WrapBreakPanelTest"
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:ProSilverlight.CustomerPanel;assembly=ProSilverlight"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Grid x:Name="LayoutRoot" Background="White" >
<local:WrapBreakPanel Orientation="Horizontal" >
<Button Height="25" Width="120" Content="First" local:WrapBreakPanel.LineBreakBefore="True"></Button>
<Button Height="25" Width="120" Content="First" local:WrapBreakPanel.LineBreakBefore="True"></Button>

</local:WrapBreakPanel>
</Grid>
</UserControl>

是不是很简单啊,赶快做一个吧.

posted @ 2011-09-21 14:22  wangyafei_it  阅读(282)  评论(0编辑  收藏  举报