Silverlight:利用Panel实现自定义布局
虽然Silverlight提供了几种基本的布局方式,比如Canvas,Grid,StackPanel,Border...,但有时候可能仍然会觉得不够用。
这时候我们可以通过继承Panel,并重写MeasureOverride 和ArrangeOverride 方法,以实现自己的布局,事实上Canvas,Grid,StackPanel就是继承自Panel,并自行实现这二个方法实现的。
布局过程中,有二个关键的步骤:测量和排列子元素,正好对应MeasureOverride 与ArrangeOverride 二个方法.
MeasureOverride 中必须遍历所有子元素,并调用子元素的Measure 方法,以便让布局系统确定每个子元素的 DesiredSize(即:子元素自身希望占据的空间大小),这是在调用 Measure 之后在系统内部发生的(开发人员无法干预),该值将在后面排列过程期间使用。
ArrangeOverride 中同样也必须遍历所有子元素,并调用子元素的Arrange方法,以便让布局系统会告诉 Panel 可用于它及其子元素的 finalSize(即最终实际占据的空间大小)。
下面的示例,修改自SL3的官方文档,它将一个400 * 400大小的Panel,划分成16个小单元格(每个正好100*100),放置在其中的元素,将自动依次填充到这些单元格的正中央,如果元素多于16个,将被忽略。
MyPanel.cs代码:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System;
namespace CustomPanel
{
public class MyPanel : Panel
{
const int _CellSize = 100;//每个单元格宽高均为100
const int _CellCount = 4;//4*4的正方形单元格
public MyPanel() : base()
{
}
/// <summary>
/// 先测量
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
protected override Size MeasureOverride(Size availableSize)
{
for (int i = 0; i < this.Children.Count; i++)
{
if (i < _CellCount * _CellCount)
{
this.Children[i].Measure(new Size(_CellSize, _CellSize));
}
else
{
//this.Children[i].Measure(new Size(0, 0));//超出元素不分配空间,其实这行也可以不要,因为ArrangeOverride中对超出元素最终并未安排空间
}
}
return availableSize;
}
/// <summary>
/// 再排列
/// </summary>
/// <param name="finalSize"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Size finalSize)
{
for (int i = 0; i < this.Children.Count; i++)
{
if (i < _CellCount * _CellCount)
{
Point cellOrigin = GetCellOriginPoint(i, _CellCount, new Size(_CellSize, _CellSize));
double dw = this.Children[i].DesiredSize.Width;
double dh = this.Children[i].DesiredSize.Height;
this.Children[i].Arrange(new Rect(cellOrigin.X + (_CellSize - dw) / 2, cellOrigin.Y + (_CellSize - dh) / 2, dw, dh));//每个子元素都放在单元格正中央
}
else
{
this.Children[i].Arrange(new Rect(0, 0, 0, 0));//超出的元素,不安排空间(即不显示)
}
}
return new Size(_CellSize * _CellCount, _CellSize * _CellCount);
//下面这二行,演示了如何将可视区域设置为仅100*100大小
//this.Clip = new RectangleGeometry() { Rect = new Rect(0,0,100,100) };
//return new Size(100, 100);
}
/// <summary>
/// 取得每个单元格的(左上)起始点坐标
/// </summary>
/// <param name="cellIndex"></param>
/// <param name="cellCount"></param>
/// <param name="itemSize"></param>
/// <returns></returns>
protected Point GetCellOriginPoint(int cellIndex, int cellCount, Size itemSize)
{
int row = (int)Math.Floor(cellIndex / cellCount);
int col = cellIndex - cellCount * row;
Point origin = new Point(itemSize.Width * col, itemSize.Height * row);
return origin;
}
}
}
测试页MainPage.Xaml
<UserControl x:Class="CustomPanel.MainPage"
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:l="clr-namespace:CustomPanel"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<l:MyPanel x:Name="myPnl" Background="#FFEFEFEF" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Button Content="01"></Button>
<Button Content="02"></Button>
<TextBlock Text="03"></TextBlock>
<Button Content="04"></Button>
<Button Content="05"></Button>
<TextBox Text="06"></TextBox>
<Button Content="07"></Button>
<Button Content="08"></Button>
<Button Content="09"></Button>
<Button Content="10"></Button>
<Button Content="11"></Button>
<Button Content="12"></Button>
<TextBlock Text="13"></TextBlock>
<Button Content="14"></Button>
<Button Content="15"></Button>
<Button Content="16"></Button>
<Button Content="17"></Button>
</l:MyPanel>
</UserControl>
运行效果图:
上面我们把Panel的尺寸定死为400 * 400了,如果想实现自由扩展,还要稍微再修改一下:
自由扩展的4 * 4格Panel
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System;
namespace CustomPanel
{
public class MyPanel : Panel
{
int _CellHeight = 100;//每个单元格的高度(初始为100)
int _CellWidth = 100;//每个单元格的宽度(初始为100)
const int _CellCount = 4;//4*4的正方形单元格
public MyPanel() : base()
{
}
/// <summary>
/// 先测量
/// </summary>
/// <param name="availableSize"></param>
/// <returns></returns>
protected override Size MeasureOverride(Size availableSize)
{
//重新每个单元格的尺寸
_CellWidth = (int)availableSize.Width / _CellCount;
_CellHeight = (int)availableSize.Height / _CellCount;
for (int i = 0; i < this.Children.Count; i++)
{
if (i < _CellCount * _CellCount)
{
this.Children[i].Measure(new Size(_CellWidth, _CellHeight));
}
else
{
//this.Children[i].Measure(new Size(0, 0));//超出元素不分配空间,其实这行也可以不要,因为ArrangeOverride中对超出元素最终并未安排空间
}
}
return availableSize;
}
/// <summary>
/// 再排列
/// </summary>
/// <param name="finalSize"></param>
/// <returns></returns>
protected override Size ArrangeOverride(Size finalSize)
{
for (int i = 0; i < this.Children.Count; i++)
{
if (i < _CellCount * _CellCount)
{
Point cellOrigin = GetCellOriginPoint(i, _CellCount, new Size(_CellWidth, _CellHeight));
double dw = this.Children[i].DesiredSize.Width;
double dh = this.Children[i].DesiredSize.Height;
this.Children[i].Arrange(new Rect(cellOrigin.X + (_CellWidth - dw) / 2, cellOrigin.Y + (_CellHeight - dh) / 2, dw, dh));//每个子元素都放在单元格正中央
}
else
{
this.Children[i].Arrange(new Rect(0, 0, 0, 0));//超出的元素,不安排空间(即不显示)
}
}
return new Size(_CellWidth * _CellCount, _CellHeight * _CellCount);
//下面这二行,演示了如何将可视区域设置为仅100*100大小
//this.Clip = new RectangleGeometry() { Rect = new Rect(0,0,100,100) };
//return new Size(100, 100);
}
/// <summary>
/// 取得每个单元格的(左上)起始点坐标
/// </summary>
/// <param name="cellIndex"></param>
/// <param name="cellCount"></param>
/// <param name="itemSize"></param>
/// <returns></returns>
protected Point GetCellOriginPoint(int cellIndex, int cellCount, Size itemSize)
{
int row = (int)Math.Floor(cellIndex / cellCount);
int col = cellIndex - cellCount * row;
Point origin = new Point(itemSize.Width * col, itemSize.Height * row);
return origin;
}
}
}
CodeProject上还有几篇扩展Panel的不错文章:
http://www.codeproject.com/KB/WPF/Panels.aspx 鱼眼效果布局的实现 http://www.codeproject.com/KB/silverlight/advancedcanvas.aspx 9宫格的实现 这一篇更强大:
http://dotnet.org.za/rudi/archive/2008/04/15/why-wpf-rocks-custom-layout-panel-showcase.aspx
- 不伦不类的Action Script 3.0
- Asp.Net Mvc中的一些初级问题整理
- Pandas Series笔记
- Asp.Net4.0/VS2010新变化(6):内置的图表控件
- Asp.Net4.0/VS2010新变化(5):可扩展的(分布式)缓存
- Pandas对行情数据的预处理
- 上市公司*ST华泽官网打不开,域名已被挂出售卖
- Asp.Net4.0/VS2010新变化(4):SEO的改进
- Pandas DataFrame笔记
- 让控件填满整个页面
- 用多个类别来进行微调
- Asp.Net4.0/VS2010新变化(2):网站自动预热
- Asp.Net4.0/VS2010新变化(1):web.config与publish
- 任天堂将推出Nintendo Labo 域名保护意识墙
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 如何发布自己的项目到Maven中央仓库?
- 《闲扯Redis八》Redis字典的哈希表执行Rehash过程分析
- 为什么说在Android中请求权限从来都不是一件简单的事情?
- 小知识:如何赋予用户查看所有存储过程和触发器的权限
- ZCU106使用VCU TRD的MIPI的例子
- 一款功能简约到可怜的SQL 客户端
- Kotlin---data class
- sqlmap的使用方法
- 从0开始做播放器---音频播放有杂音且音调异常
- 线上故障实录-一大早服务就不可用了?
- mapboxGL中popup遮挡的优化
- SQL注入的基本步骤
- JS 变量作用域导致的一个坑
- 池化技术到达有多牛?看了线程和线程池的对比吓我一跳!
- Nginx 跨域 add_header 403状态下无效