您的位置:首页 > 其它

WPF程序设计 :第三章 内容的概念(The Concept of Content)

2008-09-05 09:41 435 查看
Window类的Content是从ContentControl类继承来的。ContentControl继承自Control,而Window直接继承自ContentControl。ContentControl类存在的意义几乎就是为了定义Content property以及几个相关的property和方法。

Content property被定义成Object类型,这似乎暗示着它可以是任何对象,事实也相去不远。"相去不远"是因为你不可以把Content property设定成另一个Window类型的对象。运行时会报错,异常信息提醒开发者:Window必须是"树根",而不可以是另一个Window对象的分支。

实例程序-1:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Media;

namespace PartThree

{

public class DisplaysomeText : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new DisplaysomeText());

}

public DisplaysomeText()

{

Title = "Display some Text";

Content = new int[57];//EventArgs.Empty;//DateTime.Now;//Math.PI;

Brush brush = new LinearGradientBrush(Colors.Black, Colors.White, new Point(0, 0), new Point(1, 1));

Background = brush;

Foreground = brush;

FontFamily = new FontFamily("Comic Sans MS");

FontSize = 50;

SizeToContent = SizeToContent.WidthAndHeight;

BorderBrush = Brushes.SaddleBrown;

BorderThickness = new Thickness(25, 50, 75, 100);

}

}

}

SizeToContent = SizeToContent.WidthAndHeight;

Window类的SizeToContent property造成窗口大小可以根据内容的尺寸作调整。

实例程序 - 2:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

namespace PartThree

{

public class RecordKeyStrokes : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new RecordKeyStrokes());

}

StringBuilder build = new StringBuilder("text");

public RecordKeyStrokes()

{

Title = "Record Key Strokes";

Content = build;

}

protected override void OnTextInput(System.Windows.Input.TextCompositionEventArgs e)

{

base.OnTextInput(e);

//string str = Content as string;

if (e.Text == "\b")

{

if (build.Length > 0)

{

build = build.Remove(build.Length - 1, 1);

}

}

else

{

build.Append(e.Text);

}

Content = null;

Content = build;

}

}

}

Content Property 真正需要的是本质上更图形化的东西,由UIElement继承而来的类的实例。

在WPF中,UIElement是一个极为重要的类。它实现键盘、鼠标以及手写笔(stylus)事件的处理。UIElement类也包含一个很重要的方法,名为OnRender。这个方法被调用,以显示对象的外观。(待会有实例程序)



(你可以看到在DrawingContent对象中包含很多Draw..的方法)

在Content property的世界中,分成两组对象:一组继承自UIElement,另一组则不是。后面一组的显示结果,就是ToString方法的返回值;前面这一组,则是利用OnRender来显示。继承自UIElement的类(以及对象)被称为element。对这句话的理解参见实例程序-1和实例程序-2。

唯一直接继承UIElement的类是FrameworkElement,在WPF中所有的element都是继承自FrameworkElement。理论上,UIELement提供关于关于用户界面和屏幕显示的必要结构。可以支持各种各样的编程框架(programming framework)。WPF正是这样的框架,它包含继承自FrameworkElement的所有类。

一个重要的类 Image,它是继承自FrameworkElement的一个很常见的类型。下面是它的继承层次:

Object

DispatcherObject(abstract)

DependencyObject

Visual(abstract)

UIElement

FrameworkElement

Image

Image(图像)类让你可以轻易地在文件(document)或者应用内包含影像。

实例程序 - 3,image

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Media.Imaging;

using System.Windows.Controls;

using System.Windows.Media;

namespace PartThree

{

public class ShowMyFace : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new ShowMyFace());

}

public ShowMyFace()

{

Title = "Show my face";

Uri uri = new Uri(@"D://MyFace.jpg");

BitmapImage bitmap = new BitmapImage(uri);

Image img = new Image();

img.Stretch = Stretch.None;

img.HorizontalAlignment = HorizontalAlignment.Center;

img.VerticalAlignment = VerticalAlignment.Center;

img.Source = bitmap;

img.Margin = new Thickness(192, 96, 48, 0);

//img.Width = 300;

//img.Height = 500;

Content = img;

//SizeToContent = SizeToContent.WidthAndHeight;

img.Opacity = 0.5;

Background=new LinearGradientBrush(Colors.Red,Colors.Blue,new Point(0,0),new Point(1,1));

img.LayoutTransform = new RotateTransform(45);

}

}

}

Uri uri = new Uri(System.IO.Path.Combine(Enviroment.GetEnviromentVariable("windir"),"Gone Fishing.bmp"));

Enviroment.GetEnviromentVariable("windir")方法取出"windir"环境变量,其值看起来像是"C:\\WINDOWS"这样的字符串。Path Combine方法结合图片文件的路径名称和文件名,这样就不用费心正确地插入斜杠了。

Image类不具有自己的Background和Foreground property,因为这两个property是由Control类所定义的,而Image并非继承自Control。在早期Winows API中,几乎屏幕上所有的东西,都被认为是控件(Control),现在却不是如此。

控件是视觉对象(visual object),其特征是对用户的输入有反应,像Image这样的element,当然可以得到用户的输入,因为所有的键盘、鼠标、手写笔的输入事件都是由UIElement所定义的。

下面是System.Windows.Shapes这个命名空间,它包含名为Shape的抽象类,以及6个子类。

Object

DispatcherObject(abstract)

DependencyObject

Visual(abstract)

UIElement

FrameworkElement

Shape(abstract)

Ellipse

Line

Path

Polygon(多边形)

Polyline(多叉线)

Rectangle

虽然Image是现实点阵图像(raster图像)的标准做法,这些Shape类实现了简单的二维(two-dimensional)矢量图(Vector graphic) 。线面的程序创建一个椭圆(Ellipse)类的对象。

实例程序 - 4, Ellipse

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Shapes;

using System.Windows.Media;

namespace PartThree

{

public class ShapeAnEllipse : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new ShapeAnEllipse());

}

Ellipse elips;

public ShapeAnEllipse()

{

Title = "Shape an Ellipse";

elips = new Ellipse();

elips.Fill = Brushes.AliceBlue;

elips.StrokeThickness = 24;

elips.Width = 300;

elips.Height = 300;

elips.Stroke = new LinearGradientBrush(Colors.CadetBlue, Colors.Chocolate, new Point(1, 0), new Point(0, 1));

//elips.HorizontalAlignment = HorizontalAlignment.Stretch;

//elips.VerticalAlignment = VerticalAlignment.Stretch;

Content = elips;

}

protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)

{

base.OnMouseDown(e);

double a = elips.ActualHeight;

}

}

}

该椭圆会填满客户区。周长是1/4英寸粗且使用了渐变画刷。(椭圆内部使用的是AliceBlue画刷来着色,这个颜色是以美国总统罗斯福的女儿来命名的)。

注意的是:不管是Shape类还是Ellispe类,都没有定义任何property可以让我们用来设定椭圆的尺寸,但是Ellispe类从FrameworkElement继承到的Width和Height property,这两个property正式作此用的:

elips.Wdith = 300;

elips.Height = 300;

上面实例程序,如何将Window的Conteng property 设定为字符串,以及如何设定此文字的font。然而,你直接设定到Content property的文字,总具有相同的格式,比方说,你无法将其中几个字设定格式如粗体或斜体。

如果你需要这样,就不要将Content property设定成字符串,而不是设定成一个TextBlock类型的对象,请看下面实例程序:

实例程序 - 5:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

namespace PartThree

{

public class FormatTheText : Window

{

public FormatTheText()

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new FormatTheText());

}

Title = "Format The Text";

TextBlock txt = new TextBlock();

txt.FontSize = 32;

txt.Inlines.Add("This is some");

txt.Inlines.Add(new Italic(new Run("italic")));

txt.Inlines.Add("text ,and this is some ");

txt.Inlines.Add(new Bold(new Run("bold")));

txt.Inlines.Add("text,and let's cap it off with some ");

txt.Inlines.Add(new Bold(new Italic(new Run("bold italic"))));

txt.Inlines.Add("text.");

txt.TextWrapping = TextWrapping.Wrap;

Content = txt;

}

}

}

其实,当你将Content property设定成字符串,那么ContentControl(这是Window的祖先类)会先创建一个Text Block类型的对象,以实际显示出此字符串,Text Block类直接继承自FrameworkElement,它定义了Inlines property(类型为InlineCollection,这是Inline对象的collection)。

TextBlock本省属于System.Windows.Controls命名空间。但是 Inline是属于System.Windows.Document命名空间,而且甚至不是继承自UIElement。下面是部分的类继承图,显示出Inline和它的 后代:

Object

DispatcherObject(abstract)

DependencyObject

ContentElement

FrameworkContentElement

TextElement(abstract)

Inline(abstract)

Run

Span

Bold

Italic

UnderLine

你可能注意到这个类层次与之前的似乎有类似的结构 ---- ContentElement 和 FrameworkContentElement 类对比于 UIElement和FrameworkElement类。然而,ContentElement类不包含OnRender方法。继承自ContentElement的类所产生的对象,不会在屏幕上把自己画出来。它们却是要通过"继承自UIElement的类"才能在屏幕上达到视觉的演示,由"继承自UIElement的类"提供他们所欠缺的OnRender方法。

还有一点,不要把ContentElement 类和ContentControl混为一谈。ContentControl是控件,像Window一样,可以具有Content property。就算 Content property是空的,ContentControl对象还是会在屏幕上显示自己。而ContentElment对象一定要是其他可以显示的对象的一部分(也就是说,必须是控件的内容),才能得以显示。

类似UIElement类,ContentElement类定义了许多用户输入事件,可以把事件处理器(event handler)安装到"由TextBlock显示出来的" Inline elements上.下面的程序展示此技巧。

实例程序 - 6:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

namespace PartThree

{

public class ToggleBoldAndOtalic : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new ToggleBoldAndOtalic());

}

public ToggleBoldAndOtalic()

{

Title = "Toggle Bold & Italic";

TextBlock txt = new TextBlock();

txt.FontSize = 32;

txt.HorizontalAlignment = HorizontalAlignment.Center;

txt.VerticalAlignment = VerticalAlignment.Center;

Content = txt;

string strQuote = "To be, or Not to be, That is the question";

string[] strWords = strQuote.Split();

foreach (string str in strWords)

{

Run run = new Run();

run.Text = str;

run.MouseDown += new System.Windows.Input.MouseButtonEventHandler(run_MouseDown);

txt.Inlines.Add(run);

txt.Inlines.Add(" ");

}

}

void run_MouseDown(object sender, System.Windows.Input.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;

}

}

}

前面其实提到,窗口的Content property其实想要的是继承自UIElement类的实例,因为此类定义了一个名为OnRender的方法,负责在屏幕上显示此对象。

下面这个实例程序,让SimpleEllipse继承自FrameworkElement(这是直接继承自UIElement类的唯一类别),他相当重要的OnRender方法予以override,以获得一个DrawingContent对象。通过此对象和DrwingEllipse方法就可以绘制椭圆了。

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows;

using System.Windows.Media;

using System.Windows.Shapes;

namespace PartThree

{

public class SimpleEllispe : FrameworkElement

{

protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)

{

drawingContext.DrawEllipse(Brushes.Blue, new Pen(Brushes.Blue, 24), new Point(RenderSize.Width / 2, RenderSize.Height / 2), RenderSize.Width / 2, RenderSize.Height / 2);

drawingContext.

}

}

public class RenderTheGraphic : Window

{

[STAThread]

public static void Main()

{

Application app = new Application();

app.Run(new RenderTheGraphic());

}

public RenderTheGraphic()

{

Title = "Render the Graphic";

SimpleEllispe elips = new SimpleEllispe();

elips.Width = 120;

elips.Height = 100;

Content = elips;

}

}

}

总结:虽然本章使用到了System.Windows.Controls命名空间的element,但是没有使用任何继承自Control的类(当然Window本省是个例外)。控件是设计来获得用户的输入,并作出反应的。下一章,我们来展开这一范畴。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐