2024年3月11日发(作者:)

维普资讯

NET>>> 

实战_N ET Fra m ewo rk 3_0 

之W P F用户控件设计篇 

口文/陈康 

前不久 Microsoft正式把WinFx更 

1.棋盘的大小可以改变:通常标准棋盘的大 

名为.NET Framework 3 0,标志着这一新的框架 

小是1 9X1 9,但有时也需要其他的尺寸,比如说初 

开始正式进入NET的舞台,从早期的CTP发布至 

学者可能需要一个11x11的棋盘; 

今已经有一年多了,其问技术名称也有过数次变 

2.棋盘需要能够显示坐标的标记来帮助棋子 

更,I;P_, ̄[IWPF(Windows Presentation Foundation)的 

的定位(不需要的时候可以隐藏显示); 

原名是Avalon 而WCF(Windows Communication 

3.在棋盘上方便地放置棋子(通过鼠标操作); 

4.棋子可以显示手序(如果需要的话); 

Foundatien)就叫Indigo。为了探究NET Framework 3.0 

究竞为我们带了什么 我前一段时间特意用它写 

5.可以从棋盘上删除一个已有的棋子(比如 

的一个围棋程序.程序是在Windows2003上开发 

说在悔棋、提子等场合)。 

的,使用的是.NET Framework 3.0六月份的CTP。 

棋盘之所以设计为控件,它的目的就是能够被 

‘‘ 

本文作者以 

虽然,我从一开始就对Avalon发生兴趣,但真正 

更多的程序使用(支持WPF) 所以在设计的时候 

用它写一个完整的程序还是第一次。如果要我说 要考虑到相对的独立性.对外界的依赖越少越好。 

有什么感受的话,那么首先是在桌面应用设计上 

基于以上的功能和原则,我们的对这个控件 

引入了XAML,使它的开发过程更加接近ASP,NET, 的实现如下分述。 

编写一个围棋程 

这样做的一个好处就是设计人员和开发人员之间 

序棋盘控件的过 

可以同步的工作而不会互相干扰(为此,Microsoft 

essitm套件).就 

程为范例,从依 

还专门为设计人员准备了一套Expr

实现 

在Visual Studio 2005新建一个WinFx的WinFx 

Custom Control项目GoBoard.更改UserControl 1.CS 

赖属性、画图E 

是说把界面的表现和底层的实现在设计的层次上 

dControl CS.修改相应的地方。将 

F文、可视对象 

进行分离 其次WPF使用了一个全新的不同于 

的名称为Boar

等角度来介绍如何 

利片|.NET Frame— 

work 3.0的WPF 

WinForm的构架.新的构架使得界面更加富有表现 BoardControl的基类从UserControI改为Canvas. 

能力.如3D窗体.动画等.从而给用户带来更多 

BoardControl直接从Canvas派生 这样我们可以方 

的视觉感受和全新的体验。 

下面就程序中对围棋棋盘控件的编写过程作 

便的画各种图形对象。 

用广l设计控件进 

个介绍,由于篇幅所限.无法将程序的整个开 

依赖属性(DependencyPr0perty) 

依赖属性是WPF中的重要概念之一.为了支 

持数据绑定、动画 继承、样式等特征.WPF特别 

的设计了一套依赖属性系统。一个属性被定义咸 

依赖属性.它首先是一个静态的DependencyProperty 

行JLF发实践。 

发过程一一展示.如果大家想知道有关这个程序 

更详细的内容.请访问我的Blog(http://blog 

,, 

csdn net/niwalker)。 

功能分析 

棋盘的样子.我想不外乎有下面几个功能: 

对象实例 通过向依赖属性系统注册依赖属性,那 

让一个会下围棋的朋友说出他心目中的电子 么这个属性的值就可以被其他属性所依赖。虽然 

WPF并不是每个属性都是依赖属性.但是使用依 

Programmer 1 15 

维普资讯

_

馐_

NET>>> 

 Technology 

赖属性有许多优点.依赖属性可以对本身的值提供校验 同时 至此 我们基本完成了棋盘部分的显示.下面介绍棋子的 

也提供属性被修改后.自动发出变更通知。有关依赖属性的具 显示设计。 

体细节.参阅Vista SDK文档。我们打算把棋盘的BoardSize定义 

成一个依赖属性.这样任何依赖Bo a rd Si z e属性的属性在 

BoardSize值发生改变的时候.都会自动改变.在Visual Studio中 

棋子与可视对象 

相对于静态的棋盘.棋子的显示是动态的.因为棋子的数‘ 

定义一个依赖属性很容易.因为Cider(.NET Framework3.0 Visual 量和位置以及状态都是不确定.随时都有可能发生改变。你仍 

Studio扩展)提供了一个snippet 然可以通过把每一个棋子的信息保存在一个链表中.然后在 

切换 ̄lJBoardControl源代码编辑模式.在BoardControl类你要 OnRender方法中遍历该链表读取棋子的信息.然后把它们画到 

定义属性的空白处.右键鼠标.在右键菜单中选Insert Snippet. 棋盘上面。不过.我们将利用WPF新的特性来达到同样的目的。 

然后选WinFx.选Define a dependencyPToperty。 棋子可以分成两个部分来设计.一个是棋子的显示部分.另一 

输入类型为:int:输入属性名称:BoardSize 属性的拥有 个部分就是如何保存棋子的信息 

类为:BoardControl: 默认值:1 9。定义后的代码如下所示: 

DubH。i t BoardSize 

棋子显示最简单的方法就是画一个圆 然后用棋子的颜色来 

填充这个圆。但是为了使得棋子的外观更加生动.我们需要给棋 

子加上一些光线和明暗的效果 这可以通过WPF提供的渐变笔刷 

来完成.有两种渐变笔刷我们可以使用.它们是线性渐变笔刷 

(LinearGradientBrush)和辐射渐变笔刷(RadialGradientBrush).下面 

{ 

set(SetVa!ue(BoardSizeProperty,value)¨ 

pub 。。D epe姐tndein。c dyProopnelr Dt yepe“dencyP:。perty Boar,.dSi距Pr。pert 

Register(”BoardSiza”,typeof(int7, 

的代码展示了在指定位置画棋子的方法:

 ’,J、 。上 日 0工|詈Ll=。。 n 

1)rivatevoidDra ̄Stcne( 

f 

ng。。nt昏c匕dc,Pointpos,st研好。。 color7 

typeof(BoardContr01),newUIPropertyMetadata(19 7 7 

画图上下文(DrawingContext) 址 。 博 

如果你熟悉NET Framework2 0,那么你一定知道可以使用 

Graphics对象来画图。在WPF中.类似的对象是DrawingContext, 

DrawingContext[; ̄,Graphics的功能更强.比如它除了画普通的图形 

篙 七r 。 。n 一El ip 。。 tr ‘p ,。TONER, 

E11ipseGeometry highLight newEllipseGeometry() .Il l

po8 new Ve。t。r(HIGH LIGHT OFFSET x, … 

对象,还可以画视频对象。下面是我们通过重载。nRender方法来 

画出棋盘的网格和坐标(2.0中则是通过重载OnPaint方法)。 

rotec七ed oV。 ride V。id 0nRender(DrawingContex七dc) 

景告 dFiF SE。Tx__ Y 7 ;GH GHT RX; 

“ ghLigh ・RadlusY 眦。 L IGH 

LinearGradientBrush highLightBrush 

new LinearGradientBrush(c。lor8

Transparent, 

 。

wn!t ̄,co ̄0rs. 

Point pt0 this PointToScreen(new Point(0・0,0-0)) 

Point ptl孟this.POintToS0 een(new Point(1.0,0.0 7 7 

new Point(0,0 7,new Point(1,0)) 

RadiaIGradi由ntBrl13h自t0neBru8h n1】11 scale=1.0/(pt1.X—pt0.X) 

//画棋盘背景 

dc・DrawRectangle(this-Background,null,new Rect 

if(c。lor鞋螭stonecoforiBlack】 

(一0-5,一0・5,BoardSize,BoardSize)7, Colors

Gray) 

 。

stoneBrush new RadialG adientBrush(Color8

Black. 

DrawBoard(de 7 el8e 

if(showCoordinata 7 

Drawcoordinate(dc) 

. 

8toneBrush掣new RadiaIGradientBrush(Co ̄ors,white, 

Colors

Gray), 

一 

l 

stoneBrush.GradientOrigin一 

代码中的scale保存当前的控件和实际屏幕的比率.用于控 

制后面画图的比例尺寸(如线条的宽度)。D r a w B O a r d和 

DrawC00rdinate方法的实现 参见程序代码.这里就不列出了。 

new Point(STONE_BRUSH_ORIGIN X, 

—RUSH

stoneBrush. ̄adiusx STONE

。 。“eB 。 ・Ra STONE RU。”—RY 

_

_

 ̄X; 一 

demarkc。um字段是一个用来设置棋盘上”星”的位置.它 

的定义如下:

readonlyint【,]demarkCount f 2,3},f 3,3),f 3,4},f 3,5J, 

{3,日}, 

dc’Pu8hT ∞fo : ’ : 盆 p。。 po。.Y】), 

dc・DrawGeometry(stoneBruah,null ̄atone) 

dc・DrawGeometry(highLightBrush,r【ull,highLight) 

¨ 

CoordinateC。I。r是我们定义的另外一个依赖属性.用来表示 

坐标颜色。 

代码没有难懂的地方・其中常量的定义见源程序。 

棋子的第二部分设计.涉及到wPF的VisuaI对象.Visual ̄: 

.If6 瑶序赛 

维普资讯

所有可视对象的基类,它提供了可视对象的公共实现.如:坐 

标变换、边框计算、点击检测等。DrawingVisual是Visual的一个 

有了可视化的棋子对象.我们可以利用WPF的机制.定义 

个VisuaICollection的集合来保存每一个棋子对象.然后通过重 

派生类.它提供在屏幕上画出几何形状的功能.它的内容还可 

载GetViusalc d和Visuafch_farencount让wPF能够正确的得到我 

以被系统持久化 所以从DrawingVisual派生出我们的棋子对象. 

们的可视化对象。这部分的代码如下: 

也许是一个比较好的选择 

cla ss VisualStone:DrawinqVisual ~ 

渤 

) 

{ 

‘Point position: 

StoneColor color; 

int handOrder; 

DrawingVisual handOrderVisual—null; 

public VisualStone(Point pO8,StoneColor colorr 

int handOrder1 

f 

protected override Visual GetVisualChild(int index) 

this.position pos; 

this color color; 

return base。G eVi¥ualChild(index) 

this.handOrder=handOrder; 

if(handOrder! 0) 

} 

return tondVig 

、 

index], 

CreateHandOrderVisual 1)j 

pr9 q ed 唧rride i札vi ualchu《l= encDunt 

{get{。re≈u S ̄oneVisuai乳c。nn } 

public void ShowHandOrder(bool isShow) 

{ 

if(handOrderVlsual=一null、 

return; 

值得一提的是.如果是在NET Framework2 0或者之前的版 

if(isShow) 

handOrderVisua1.Opacity=1 

else 

及控件的树形组织结构.必须自己处理缩放 重绘等工作。 

我们的最后一部分工作向外部暴露必要的调用接El 这些 

handOrderVisua1.Opacity 0} 

方法是:AddStone、RemoveStone等.一个显示和隐藏手序的开 

pub!ic int Hand0rder 

(ge七{return handOrder;}} 

关属性 我们同时提供一个鼠标点击棋盘的事件,这不一定需 

要.但是考虑到Canvas的鼠标事件返回的鼠标位置不是整型. 

为了让我们的控件更显用户友好.我们将浮点进行四舍五入取 

整.上述过程的实现代码见源代码。 

pi ̄blic stoneColor color 

{get{return color;}】 

public point Position 

{get{return position;}l 

private void CreateHand。rderVisual 0 

Ra'rDve_,Stone有两个重载的版本.一个用于提子的场合.一个用 

于悔棋的场合。我们还提供了一个Reset方法用于重置fg-a的状态。 

{ 

handOrderVisual—new DrawingVisual() 

棋盘控件的设计暂时告一段落 如果你要测试这个控件. 

可以创建一个WinFx的Windows Application项目.然后将控件 

Draw土ngc0ntext dc=handOrderVisua1.RenderOpen 0 

Brush brush=Brushes.Black; 

if(color 

brush 

放置到一个ViewBox的元素中。具体的做法详见源代码。 

¥toneColor.Black) 

Brushes.Whitej 

FormattedText fmtText=new FormattedText 

(handOrder.ToString(),CultureInfo.CurrentCulture, 

FlowDirection.LeftToRight,new Typeface(”A ial ), 

小结 

控件虽然完成了.不过故事并没有结束.如果你愿意的话. 

你还可以继续改进和完善 增加它的功能.比如3D的棋子棋盘、 

棋盘的翻转.落子时的音效等。同 NET Framework2.O相比,NET 

5,brush) 

fmtText.TextAlignment;TextAlignment。Center; 

dc.DrawText(fmtText,new Point 

(position.X,position.Y—fmtText.Height 

dc.Close(), 

this.Children.Add(handOrderVisua1), 

.5)) 

Framework 3.O在图形设计方面.无论是代码的编写还是界面的 

表现力都有很大程度的提高。本文涉及到的仅仅是WPF的一部 

分.还有许多新的特性和功能.如3D动画、多媒体等等.一方 

面功能在增强.而另一方面设计难度在降低.这就是.N E T 

amework 3.O给我的总体感觉。NET Framework 3 O决不会是 

如果你很有耐心的读到这里.我想上面的代码理解起来应 

Fr

该没有问题.其中CreateHandOrderVisual是创建一个子对象.目 

NET Framework的终结.但它肯定是新一代的软件创革的开始。一 

的是为了在棋子上显示落子手序。 

一责任编辑:常政(changzheng@csdn net) 

Programmer 1 17