您的位置:首页 > 其它

(WPF学习记录)第十二章 自定义面板

2009-06-04 14:59 405 查看
Panel最大的好处是,它有Children属性可以进行孩子的排序。此属性的类别是UIElementCollection,负责处理对AddVisualChild、AddLogicalChild、RemoveVisualChild和RemoveLogicalChild,以进行孩子的增删。

第01个程序:统一的Grid

最简单的面板是UniformGrid。每个格子都是一样的宽和高。下面的类仿造UniformGrid的功能。

第1个文件:UniformGridAlmost.cs。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace Chapter12
{
class UniformGridAlmost : Panel
{
public static readonly DependencyProperty ColumnsProperty;

static UniformGridAlmost()
{
ColumnsProperty = DependencyProperty.Register("Columns", typeof(int), typeof(UniformGridAlmost),
new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsMeasure));

}

public int Columns
{
set { SetValue(ColumnsProperty, value); }
get { return (int)GetValue(ColumnsProperty); }
}

public int Rows
{
get { return (InternalChildren.Count + Columns - 1) / Columns; }
}

protected override Size MeasureOverride(Size sizeAvailable)
{
// 根据统一的行与列,计算孩子的尺寸
Size sizeChild = new Size(sizeAvailable.Width / Columns, sizeAvailable.Height / Rows);

// 用来累积最大宽度和高度的变量
double maxwidth = 0;
double maxheight = 0;

foreach (UIElement child in InternalChildren)
{

// 调用每个孩子的Measure
child.Measure(sizeChild);

// 为孩子设置适合的尺寸
maxwidth = Math.Max(maxwidth, child.DesiredSize.Width);
maxheight = Math.Max(maxheight, child.DesiredSize.Height);
}
// 为grid本身计算尺寸
return new Size(Columns * maxwidth, Rows * maxheight);
}

protected override Size ArrangeOverride(Size sizeFinal)
{
Size sizeChild = new Size(sizeFinal.Width / Columns,
sizeFinal.Height / Rows);

for (int index = 0; index < InternalChildren.Count; index++)
{
int row = index / Columns;
int col = index % Columns;

// 计算每个孩子在sizeFinal内的矩形
Rect rectChild = new Rect(new Point(col * sizeChild.Width, row * sizeChild.Height), sizeChild);

// 然后调用Arrange更新布局
InternalChildren[index].Arrange(rectChild);
}
return sizeFinal;
}
}

}


第2个文件:DuplicateUniformGrid.cs。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace Chapter12
{
public class DuplicateUniformGrid : Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new DuplicateUniformGrid());
}
public DuplicateUniformGrid()
{
Title = "完全相同的grid";
Width = 480;
Height = 288;
WindowStartupLocation = WindowStartupLocation.CenterScreen;

UniformGridAlmost unigrid = new UniformGridAlmost();
unigrid.Columns = 5;
Content = unigrid;

// 用随机尺寸的按钮,填入UniformGridAlmost
Random rand = new Random();

for (int index = 0; index < 25; index++)
{
Button btn = new Button();
btn.Name = "按钮" + (index + 1);
btn.Content = btn.Name;
btn.FontSize += rand.Next(10);
unigrid.Children.Add(btn);
}
AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonOnClick));
}
void ButtonOnClick(object sender, RoutedEventArgs args)
{
Button btn = args.Source as Button;
MessageBox.Show(btn.Name + "被点击!", Title);
}
}

}


运行效果如下:



第02小程序:Canvas上绘图(PaintOnCanvasClone.cs)

Canvas在第七章介绍过,此面板类似传统的图形环境,利用坐标位置,指定元素的摆放地点。当有多个元素时,越早加入其中的元素出现在越下层(可被后加入的元素覆盖)。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Chapter12
{
public class PaintOnCanvasClone : Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new PaintOnCanvasClone());
}
public PaintOnCanvasClone()
{
Title = "Canvas上的矩形";
Width = 480;
Height = 320;
WindowStartupLocation = WindowStartupLocation.CenterScreen;

Canvas canv = new Canvas();
Content = canv;

SolidColorBrush[] brushes = { Brushes.Red, Brushes.Green, Brushes.Blue };

for (int i = 0; i < brushes.Length; i++)
{
Rectangle rect = new Rectangle();
rect.Fill = brushes[i];
rect.Width = 100;
rect.Height = 100;
canv.Children.Add(rect);
Canvas.SetLeft(rect, 50 * (i + 1));
Canvas.SetTop(rect, 50 * (i + 1));
}
}
}

}


运行结果:



第03个小程序:依对角线排列按钮

在Panel内添加Child时,一般的做法是:pnl.Children.Add(child),这样有件事不对劲,定义此child字段和属性的类,不知道有什么东西被加入或者删除了,也不知道什么时候调用的AddVisualChild、AddLogiclChild等。

一个可能的解决之道是写一个类似UIElementCollection的类,处理这件事,或者通知面板去做这件事。

第1个代码文件:DiagonalizeTheButtons.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;

namespace Chapter12
{
class DiagonalPanel : FrameworkElement
{
List<UIElement> children = new List<UIElement>();

Size sizeChildrenTotal;

public static readonly DependencyProperty BackgroundProperty;

// 静态构造函数创建依赖项属性Background
static DiagonalPanel()
{
BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(DiagonalPanel),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
}

public Brush Background
{
set { SetValue(BackgroundProperty, value); }
get { return (Brush)GetValue(BackgroundProperty); }
}

// 添加孩子元素
public void Add(UIElement el)
{
children.Add(el);
AddVisualChild(el);
AddLogicalChild(el);
InvalidateMeasure();  //更新元素布局
}
public void Remove(UIElement el)
{
children.Remove(el);
RemoveVisualChild(el);
RemoveLogicalChild(el);
InvalidateMeasure();
}
public int IndexOf(UIElement el)
{
return children.IndexOf(el);
}

protected override int VisualChildrenCount
{
get { return children.Count; }
}
protected override Visual GetVisualChild(int index)
{
if (index >= children.Count)
throw new ArgumentOutOfRangeException("index");

return children[index];
}
protected override Size MeasureOverride(Size sizeAvailable)
{
sizeChildrenTotal = new Size(0, 0);

foreach (UIElement child in children)
{
child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

sizeChildrenTotal.Width += child.DesiredSize.Width;
sizeChildrenTotal.Height += child.DesiredSize.Height;
}
return sizeChildrenTotal;
}
protected override Size ArrangeOverride(Size sizeFinal)
{
Point ptChild = new Point(0, 0);

foreach (UIElement child in children)
{
Size sizeChild = new Size(0, 0);

sizeChild.Width = child.DesiredSize.Width * (sizeFinal.Width / sizeChildrenTotal.Width);
sizeChild.Height = child.DesiredSize.Height * (sizeFinal.Height / sizeChildrenTotal.Height);
child.Arrange(new Rect(ptChild, sizeChild));
// 当前元素的右下角位置为下一个元素的起点位置
ptChild.X += sizeChild.Width;
ptChild.Y += sizeChild.Height;
}
return sizeFinal;
}
protected override void OnRender(DrawingContext dc)
{
dc.DrawRectangle(Background, null, new Rect(new Point(0, 0), RenderSize));
}
}

}


有了这个类,加入孩子的时候就可以直接写pal.Add(child)。

第2个代码文件:DiagonalPanel.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace Chapter12
{
public class DiagonalizeTheButtons : Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new DiagonalizeTheButtons());
}
public DiagonalizeTheButtons()
{
Title = "对角线式排列按钮";
Width = 608;
Height = 288;
WindowStartupLocation = WindowStartupLocation.CenterScreen;

DiagonalPanel pnl = new DiagonalPanel();
Content = pnl;

Random rand = new Random();

for (int i = 0; i < 5; i++)
{
Button btn = new Button();
btn.Content = "按钮" +(i + 1);
btn.FontSize += rand.Next(5);
pnl.Add(btn);
}
}
}

}


运行效果:



第04个小程序:圆形排列按钮

一个圆可以被切割成多个扇形的小块。想将element排列成圆形,或许最直接的方式是让第个element都摆设在自己的小块内。

如下图所示,是一个小的扇形块,矩形表示按钮,从中心连到element右上角的这一条线,就是半径。





计算公式如下:





第01个代码文件:RadialPanelOrientation.cs

因为按钮通常都是宽(W)大于高(H),若按钮摆放方式不同(如旋转90度),这会影响半径的计算,不同的摆放方式会表现出来“以高度为主要决定因素”和“以宽度为主要决定因素”计算的半径。

using System;
using System.Collections.Generic;
using System.Text;

namespace Chapter12
{
public enum RadialPanelOrientation
{
ByWidth,
ByHeight
}
}


第02个代码文件:RadialPanel.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;

namespace Chapter12
{
public class RadialPanel : Panel
{
public static readonly DependencyProperty OrientationProperty;

bool showPieLines;
double angleEach;       // 角度
Size sizeLargest;       // 最大孩子的尺寸
double radius;

// 圆的半径
double outerEdgeFromCenter;
double innerEdgeFromCenter;

static RadialPanel()
{
OrientationProperty = DependencyProperty.Register("Orientation", typeof(RadialPanelOrientation), typeof(RadialPanel),
new FrameworkPropertyMetadata(RadialPanelOrientation.ByWidth, FrameworkPropertyMetadataOptions.AffectsMeasure));
}

public RadialPanelOrientation Orientation
{
set { SetValue(OrientationProperty, value); }
get { return (RadialPanelOrientation)GetValue(OrientationProperty); }
}

public bool ShowPieLines
{
set
{
if (value != showPieLines)
InvalidateVisual();

showPieLines = value;
}
get
{
return showPieLines;
}
}

protected override Size MeasureOverride(Size sizeAvailable)
{
if (InternalChildren.Count == 0)
return new Size(0, 0);

angleEach = 360.0 / InternalChildren.Count;
sizeLargest = new Size(0, 0);

foreach (UIElement child in InternalChildren)
{
child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));

sizeLargest.Width = Math.Max(sizeLargest.Width, child.DesiredSize.Width);
sizeLargest.Height = Math.Max(sizeLargest.Height, child.DesiredSize.Height);
}
if (Orientation == RadialPanelOrientation.ByWidth)
{
// 计算中心到element边缘的距离
innerEdgeFromCenter = sizeLargest.Width / 2 / Math.Tan(Math.PI * angleEach / 360);
outerEdgeFromCenter = innerEdgeFromCenter + sizeLargest.Height;
// 以最大的孩子为基准,计算圆的半径
radius = Math.Sqrt(Math.Pow(sizeLargest.Width / 2, 2) + Math.Pow(outerEdgeFromCenter, 2));
}
else
{
// 计算中心到element边缘的距离
innerEdgeFromCenter = sizeLargest.Height / 2 / Math.Tan(Math.PI * angleEach / 360);
outerEdgeFromCenter = innerEdgeFromCenter + sizeLargest.Width;

// 以最大的孩子为基准,计算圆的半径
radius = Math.Sqrt(Math.Pow(sizeLargest.Height / 2, 2) + Math.Pow(outerEdgeFromCenter, 2));
}
// 返回该圆的尺寸
return new Size(2 * radius, 2 * radius);
}

protected override Size ArrangeOverride(Size sizeFinal)
{
double angleChild = 0;
Point ptCenter = new Point(sizeFinal.Width / 2, sizeFinal.Height / 2);
double multiplier = Math.Min(sizeFinal.Width / (2 * radius), sizeFinal.Height / (2 * radius));

foreach (UIElement child in InternalChildren)
{
// 重置孩子呈现位置的转换信息
child.RenderTransform = Transform.Identity;

if (Orientation == RadialPanelOrientation.ByWidth)
{
// 将孩子放在上边
child.Arrange(
new Rect(ptCenter.X - multiplier * sizeLargest.Width / 2,
ptCenter.Y - multiplier * outerEdgeFromCenter,
multiplier * sizeLargest.Width,
multiplier * sizeLargest.Height));
}
else
{
// 将孩子放在右边
child.Arrange(
new Rect(ptCenter.X + multiplier * innerEdgeFromCenter,
ptCenter.Y - multiplier * sizeLargest.Height / 2,
multiplier * sizeLargest.Width,
multiplier * sizeLargest.Height));
}

// 旋转孩子
Point pt = TranslatePoint(ptCenter, child);
child.RenderTransform = new RotateTransform(angleChild, pt.X, pt.Y);

// 增加角度,准备安置下一个孩子
angleChild += angleEach;
}
return sizeFinal;
}

// 显示饼图切线
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);

if (ShowPieLines)
{
Point ptCenter = new Point(RenderSize.Width / 2, RenderSize.Height / 2);
double multiplier = Math.Min(RenderSize.Width / (2 * radius), RenderSize.Height / (2 * radius));
Pen pen = new Pen(SystemColors.WindowTextBrush, 1);
pen.DashStyle = DashStyles.Dash;

// 显示圆
dc.DrawEllipse(null, pen, ptCenter, multiplier * radius, multiplier * radius);
// 初始化角度
double angleChild = angleEach / 2;
if (Orientation == RadialPanelOrientation.ByHeight) angleChild += 90;

// 循环走过孩子,从中心绘制放射线
foreach (UIElement child in InternalChildren)
{
dc.DrawLine(pen, ptCenter,
new Point(ptCenter.X + multiplier * radius * Math.Sin(2 * Math.PI * angleChild / 360),
ptCenter.Y - multiplier * radius * Math.Cos(2 * Math.PI * angleChild / 360)));
angleChild += angleEach;
}
}
}
}
}


第03个代码文件:CircleTheButtons.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Chapter12
{
public class CircleTheButtons : Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new CircleTheButtons());
}

RadialPanel pnl = new RadialPanel();
int btnCount;
SolidColorBrush[] brushes = { Brushes.Red, Brushes.Green, Brushes.Blue,
Brushes.Yellow,Brushes.Violet,Brushes.Orange,
Brushes.PaleGreen,Brushes.RosyBrown,Brushes.Salmon,Brushes.Silver};

public CircleTheButtons()
{
Title = "圆形排列按钮";
WindowStartupLocation = WindowStartupLocation.CenterScreen;
Width = 768;
Height = 432;
Topmost = true;
SizeChanged += OnSizeChanged;

Canvas canv = new Canvas();

pnl.Width = Width - 8;
pnl.Height = Height - 34;
pnl.Orientation = RadialPanelOrientation.ByWidth;
pnl.ShowPieLines = true;

canv.Children.Add(pnl);

// 互动按钮
Button btnIsShowPie = new Button();
btnIsShowPie.Width = 120;
btnIsShowPie.Content = "不显示饼图";
btnIsShowPie.Click += IsShowPieClick;
canv.Children.Add(btnIsShowPie);
Canvas.SetLeft(btnIsShowPie, 10);
Canvas.SetTop(btnIsShowPie, 10);

Button btnRadialPanelOrientation = new Button();
btnRadialPanelOrientation.Width = 120;
btnRadialPanelOrientation.Content = "以高度为主排列";
btnRadialPanelOrientation.Click += RadialPanelOrientationClick;
canv.Children.Add(btnRadialPanelOrientation);
Canvas.SetLeft(btnRadialPanelOrientation, 10);
Canvas.SetTop(btnRadialPanelOrientation, 50);

Button btnAddButton = new Button();
btnAddButton.Width = 120;
btnAddButton.Content = "增加按钮";
btnAddButton.Click += AddButtonClick;
canv.Children.Add(btnAddButton);
Canvas.SetLeft(btnAddButton, 10);
Canvas.SetTop(btnAddButton, 90);

Button btnCutButton = new Button();
btnCutButton.Width = 120;
btnCutButton.Content = "减少按钮";
btnCutButton.Click += CutButtonClick;
canv.Children.Add(btnCutButton);
Canvas.SetLeft(btnCutButton, 10);
Canvas.SetTop(btnCutButton, 130);

Content = canv;

Random rand = new Random();

int i;
for (i = 0; i < 9; i++)
{
Button btn = new Button();
btn.Content = "按钮" + (i + 1);
btn.Background = brushes[i];
btn.FontSize += rand.Next(10);
pnl.Children.Add(btn);
}
btnCount = i;
}

// 响应事件
void OnSizeChanged(object sender, EventArgs e)
{
pnl.Width = this.Width - 8;
pnl.Height = Height - 34;
pnl.InvalidateMeasure();
}

void IsShowPieClick(object sender, RoutedEventArgs e)
{
Button btn = e.Source as Button;
if (pnl.ShowPieLines == true)
{
pnl.ShowPieLines = false;
btn.Content = "显示饼图";
}
else
{
pnl.ShowPieLines = true;
btn.Content = "不显示饼图";
}
}

void RadialPanelOrientationClick(object sender, RoutedEventArgs e)
{
Button btn = e.Source as Button;
if (pnl.Orientation == RadialPanelOrientation.ByWidth)
{
pnl.Orientation = RadialPanelOrientation.ByHeight;
btn.Content = "以宽度为主排列";
pnl.InvalidateVisual();
}
else
{
pnl.Orientation = RadialPanelOrientation.ByWidth;
btn.Content = "以高度为主排列";
pnl.InvalidateVisual();
}
}

void AddButtonClick(object sender, RoutedEventArgs e)
{
if (btnCount < 30)
{
Random rand = new Random();
int i = rand.Next(brushes.Length - 1);
Button btn = new Button();
btn.Content = "按钮" + (btnCount + 1);
btn.Background = brushes[i];
btn.FontSize += rand.Next(10);
pnl.Children.Add(btn);
btnCount++;
pnl.InvalidateVisual();
}
}

void CutButtonClick(object sender, RoutedEventArgs e)
{
if (btnCount > 4)
{
UIElementCollection uiEleColl = pnl.Children;
uiEleColl.RemoveAt(btnCount - 1);
btnCount--;
}
pnl.InvalidateVisual();
}
}
}


运行效果如下:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: