WPF编程设计指南读书笔记(3-2)
看一下System.Windows.Shapes命名空间,它包含了名为Shape的抽象类以及它的六个子类(Ellipse,Line,Path,Polygon,Polyline,Rectangle)。这些类也继承自UIElement、FrameworkElement:
Image实现了对点阵图的显示,而这些形状类实现了简单二维矢量图的显示。下面的程序创建了一个椭圆(Ellipse):
//*********************************************************
//ShapeAnEllipse.cs 2010 9th Auguest by mouyong
//*********************************************************
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace part1.ch03
{
class ShapeAnEllipse:Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new ShapeAnEllipse());
}
public ShapeAnEllipse()
{
Title = "圆一个椭圆";
Ellipse elips = new Ellipse();
elips.Fill = Brushes.AliceBlue;
elips.StrokeThickness = 24;
elips.Stroke = new LinearGradientBrush(Colors.CadetBlue,Colors.Chocolate,new Point(1,0),new Point(0,1));
Content = elips;
}
}
}
该椭圆会填满客户区。有一个较粗的边框,使用了渐变画刷。椭圆内部使用了AliceBlue画刷进行了填充。这是罗斯福总统的女儿的名字。
Ellipse类从FrameworkElement类继承到Width和Height属性,可以用来设定椭圆的大小。也可以使用HorizontalAlignment和VerticalAlignment属性来设定椭圆的位置。如果你没有设置过Width和Height,还可以通过设定MinWidth、MaxWidth、MinHeight和MaxHeight属性来限制椭圆的尺寸范围。而任何时候你还可以通过ActualWidth和ActualHeight属性来得知椭圆的尺寸。
Ellipse没有可以将椭圆指定到一个特定位置的属性,你只能使用HorizontalAlignment和VerticalAlignment属性来完成这个目的。
前面我们演示过如何把Content属性设置为一个字符串,这些字符串只能使用统一的字体样式,如果你想指定某个特定的字符为特定的格式,你就得使用TextBlock类型的对象:
//*********************************************************
//FormatTheText.cs 2010 9th Auguest by mouyong
//*********************************************************
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Documents;
namespace part1.ch03
{
class FormatTheText:Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new FormatTheText());
}
public FormatTheText()
{
Title = "格式化文本";
TextBlock txt = new TextBlock();
txt.FontSize = 32;
txt.Inlines.Add("这是一个");
txt.Inlines.Add(new Italic(new Run("斜体")));
txt.Inlines.Add("文字,而这是一个");
txt.Inlines.Add(new Bold(new Run("粗体")));
txt.Inlines.Add("文字,我们还可以把它们加叠在一起,形成");
txt.Inlines.Add(new Bold(new Italic(new Run("粗斜体"))));
txt.Inlines.Add("文字");
txt.TextWrapping = TextWrapping.Wrap;
Content = txt;
}
}
}
虽然,这是本书第一个主动创建TextBlock对象的程序,但其实你以前就见过TextBlock了。如果你将Content属性设定成字符串,那么ContentControl会先创建一个TextBlock类型的对象,以实际显示出此字符串。TextBlock类直接继承自FrameworkElement,它定义了Inlines属性,它是InlineCollection类型的,这是Inline对象的集合。
TextBlock本身是属于System.Windows.Controls命名空间。但是Inline是属于System.Windows.Documents命名空间,而且没有继承自UIElement。
Inline类来自另一个继承树,它的祖先ContentElement和FrameworkContentElement与前面我们提到的UIElement和FrameworkElement有相似的地方,只是ContentElement不包含OnReader方法,也就是说,继承自ContentElement的类所产生的对象,不会在屏幕上把自己画出来。它们得通过继承自UIElement类的对象才能在屏幕上表现自己。
换句话说,上面程序中用到Bold和Italic对象的祖先都是ContentElement,所以它们必须通过祖先是UIElement的TextBlock对象来显示自己。
FormatTheText使用TextBlock的Inlines属性来组合各种元素,它是InlineCollection类型的,可以加入字符串对象,Inline对象以及UIElement对象(Inlines属性还可以处理其他的element对象)。
屏幕上的element,呈现树状的组织方式。窗口是TextBlock的父,而TextBlock又是数个Inline element的父。除非它们的子明确地设定了某些属性,否则直接从父继承所有文字相关的属性。在第8章,你会看到实际的效果。
类似UIElement类,ContentElement类定义许多用户输入事件,可以把事件处理器委托到由TextBlock显示出来的Inline element上。下面的程序展示了此技巧。
//*********************************************************
//ToggleBoldAndItalic.cs 2010 11st Auguest by mouyong
//*********************************************************
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Documents;
namespace part1.ch03
{
class ToggleBoldAndItalic:Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new ToggleBoldAndItalic());
}
public ToggleBoldAndItalic()
{
Title = "切换粗体与斜体";
TextBlock text = new TextBlock();
text.FontSize = 32;
text.HorizontalAlignment = HorizontalAlignment.Center;
text.VerticalAlignment = VerticalAlignment.Center;
Content = text;
string strQuote = "生存 还是 死亡, 这是个 问题";
string[] strWords = strQuote.Split();
foreach (string str in strWords)
{
Run run = new Run(str);
run.MouseDown+=new MouseButtonEventHandler(run_MouseDown);
text.Inlines.Add(run);
text.Inlines.Add(" ");
}
}
void run_MouseDown(object sender, MouseButtonEventArgs e)
{
Run run = sender as Run;
if (e.ChangedButton == MouseButton.Left)
{
run.FontStyle = run.FontStyle == FontStyles.Italic ? FontStyles.Normal : FontStyles.Italic;
}
if (e.ChangedButton == MouseButton.Right)
{
run.FontWeight = run.FontWeight == FontWeights.Bold ? FontWeights.Normal : FontWeights.Bold;
}
}
}
}
此程序将哈姆雷特中的一句话分割成为多个单词,然后根据每个单词创建一个Run对象,再将这些单词放在TextBlock对象的Inlines集合中。在此过程中,程序也会将run_MouseDown事件处理器委托到每个Run对象的MouseDown事件中。
Run类从TextElement类继承了FontStyle和FontWeight属性,且事件处理器会根据被按下的鼠标按钮类型,来改变这些属性。如果是鼠标左键,并且FontStyle属性目前是FontStyles.Italic,那么事件处理就会将此属性设定成FontStyle.Normal;如果此属性目前是FontStyle.Normal,那么事件处理器会将它改为FontStyle.Italic。类似地,FontWeight属性也会在FontWerights.Normal与FontWeights.Bold之间切换。
在前面曾经提到过,窗口的Content属性期待继承自UIElement的对象,因为它定义了一个名为OnRender的方法,负责在屏幕上显示此对象。下面的程序就是要自定义一个element,然后重写OnRender方法,以获得一个DrawingContext对象。通过此对象和DrawEllipse方法就可以绘制椭圆。此类单纯地模仿System.Windows.Shapes命名空间的Ellipse类。
//*********************************************************
//SimpleEllipse.cs 2010 13st Auguest by mouyong
//*********************************************************
using System;
using System.Windows;
using System.Windows.Media;
namespace part1.ch03
{
class SimpleEllipse:FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
drawingContext.DrawEllipse(Brushes.Blue,new Pen(Brushes.Red,24),new Point(RenderSize.Width/2,RenderSize.Height/2),RenderSize.Width/2,RenderSize.Height/2);
}
}
}
如果你之前就有Windows编程经验,你可能会认为此方法直接在屏幕上绘制椭圆,其实不是这样的,DrawEllipse的参数会被保留,以便将不同来源的图形集中在一起,然后在屏幕上进行组合。WPF可以变出更多这样子的图形把戏。
下面的程序实例化了SimpleEllipse对象,然后设定给了Content属性。
//*********************************************************
//RenderTheGraphics.cs 2010 13st Auguest by mouyong
//*********************************************************
using System;
using System.Windows;
namespace part1.ch03
{
class RenderTheGraphics:Window
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new RenderTheGraphics());
}
public RenderTheGraphics()
{
Title = "自定义UIElement";
SimpleEllipse elips = new SimpleEllipse();
Content = elips;
}
}
}
虽然本章使用到了System.Windows.Controls命名空间的element,但是没有使用任何继承自Control的类(当然Window类本身是个例外)。控件是设计来取得用户的输入,并作出反应的。在下一章,你会明白这是怎么回事。