首 页 网络编程
网页制作 图形图象 操作系统 冲浪宝典
软件教学 认证考试

网络安全 网络办公 行业资讯 评测对比
您当前位置:站长天空 -> 网页制作-> Dreamweaver教程
.Net Framework3.0 实践纪实(4)-.NET教程,.NET Framework
作者:网友供稿 点击:77
推荐
西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!可在线rar解压,自动数据恢复设置虚拟目录等.免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金
站内搜索
文章页数:[1] 
可视对象和棋子
任务1.6的实现和画棋纵横线没有很大的差别,设置好字体、要显示的坐标的文字,然后调用drawingcontext的drawtext方法在指定的位置画出对象即可。为了满足任务中的要求,我们设置一个开关字段和对应的属性,代码如下:
   public partial class boardcontrol : canvas
    {
        double scale = 0.03;
        readonly int[,] demarkcount ={ { 2, 3 }, { 3, 3 }, { 3, 4 }, { 3, 5 }, { 3, 6 } };
        bool showcoordinate = true;
        brush coordinatecolor = brushes.white;
 
       ……
 
        public bool showcoordinate
        {
            get { return showcoordinate; }
            set
            {
                if (showcoordinate != value)
                {
                    showcoordinate = value;
                    invalidatevisual();
                }
            }
        }
 
……..
        protected override void onrender(drawingcontext dc)
        {
           ……
            if (showcoordinate)
            {
                double fontsize = 0.4;
 
                for (int i = 1; i <= boardsize; i++)
                {
                    formattedtext fmttext = new formattedtext(i.tostring(), cultureinfo.currentculture, flowdirection.lefttoright, new typeface("arial"), fontsize, coordinatecolor);
                    fmttext.textalignment = textalignment.right;
                    dc.drawtext(fmttext, new point(-0.5, (i - 1 - fmttext.height * .5)));
                    fmttext =new formattedtext(((char)(i + 64)).tostring(), cultureinfo.currentculture, flowdirection.lefttoright, new typeface("arial"), fontsize, coordinatecolor);
                    fmttext.textalignment = textalignment.center;
                    dc.drawtext(fmttext, new point(i - 1, boardsize - 0.5));
                }
            }
        }
……
编译运行。为了测试显示隐藏坐标的功能,切换到mainwindow的xaml方式,插入如下代码:
      <stackpanelgrid.row="1"grid.column="0"orientation="vertical"margin="10">
        ……
                <checkboxname="coordinatecheckbox"click="showcoordinateclick"ischecked="true"
                  fontsize="14"foreground="white"fontweight="bold"margin="10">show coordinate</checkbox>
 
      </stackpanel>
切换到source方式,添加showcoordinateclick方法,代码如下:
……
private void showcoordinateclick(object sender, routedeventargs e)
{
    boardcontrol.showcoordinate = coordinatecheckbox.ischecked == true ? true : false;
}
……
重新运行程序,测试一下显示隐藏坐标的功能。
给任务1.6做上标记,到目前任务1中全部的子任务现在都已经完成,所以我们给任务1也做上标记。检查任务表,还有两个任务,那就是任务2和任务3。任务2明显要先于任务3。所以我们先处理任务2。
任务2:topgo的棋盘必须可以在一个指定的位置显示指定颜色的棋子;
要完成任务2,同样我们可以把它分解成2个小任务:
2.1 在指定位置画黑色棋子
2.2 在指定位置画白色棋子
我们开始任务2.1:棋子怎么画?最简单的方法就是画一个黑色的圆,但为了更好的显示效果,我们可以给棋子加上光线效果,让棋子有立体感。因为棋盘背景的光线是从左上角到右下角,所以棋子的光线方向应该和棋盘一致。受目前的cider版本的限制,我们无法可视化的在xaml中设计棋子,但你可以使用sdk自带的xmalpad来进行这项工作,具体的操作就不详细描述了,我只是提一下设计思路:先画一个黑色的圆,然后在这个圆的右上角附近画一个小的椭圆,用白色到透明的辐射渐变填充这个小的椭圆来表示高光,调整小椭圆的位置。
在xmal的棋盘控件元素的子元素中,放置显示棋子的xaml代码,就可以显示棋子。不过进一步考虑之后我们发现:棋子的位置我们事先是不知道的。它必须先是在用户指定的地方,要解决这个问题,有几种方法,我们可以把棋子的显示绑定到一个棋子的集合中,但这种方法效率不高,因为每一次数据源发生变化,棋子就会重画,也就是说一盘棋右上百个棋子,如果每次都重画它,效果肯定好不了。如果我们在onrender方法中像画棋盘纵横线那样画棋子,也存在其他方面的问题,比如窗体大小发生改变的时候,也需要手工的调用代码来重画每个棋子。在wpf中,对象是按照树型结构组织的,当显示父控件的时候,子控件是自动被计算并显示。所以我们可以把画出表示棋子的对象插入到棋盘的子控件集合中。不过,我们还是一步一步来进行,给我们的任务表插入如下任务:
2.1.1 画出黑色圆形
2.1.2 画出高光
任务2.1.1:
首先在onrender方法中添加语句(仅仅为了开始我们的任务),然后添加drawstone的方法:
 
        protected override void onrender(drawingcontext dc)
        {
            ……
            drawblackstone(dc, new point(0,0));
      
        }
 
        private void drawblackstone(drawingcontext dc, point pos)
        {
            ellipsegeometry stone = new ellipsegeometry(pos, 0.49, 0.49);
            dc.drawgeometry(brushes.black, null, stone);
        }
 
我们这里没有使用ellipse类,而是使用geometry几何对象,主要的好处是我们可以直接指定圆的中点和半径,因为棋盘的网格的大小是1*1,我们的棋子必须略小于网格,这里设置为0.98,所以半径就是0.49。
编译运行。
任务2.1.2:
画棋的高光,我们先在左边的边缘画出一个椭圆高光,然后在旋转45度到正确的位置。在drawstone方法中添加代码:
      private void drawblackstone(drawingcontext dc, point pos)
        {
            ellipsegeometry stone = new ellipsegeometry(pos, 0.49, 0.49);
 
            ellipsegeometry highlight = new ellipsegeometry();
            highlight.center = pos - new vector(0.3, 0);
            highlight.radiusx = 0.15;
            highlight.radiusy = 0.25;
 
            lineargradientbrush highlightbrush = new lineargradientbrush(colors.white, colors.transparent, new point(0, 0), new point(1, 0));
           
            dc.pushtransform(new rotatetransform(45, pos.x, pos.y));
            dc.drawgeometry(brushes.black, null, stone);
            dc.drawgeometry(highlightbrush, null, highlight);
        }
编译运行,观看效果,基本能够达到我们的要求。通过设置pos参数,可以在不同的位置显示棋子(自行测试)。如果没有问题,就给任务2.1.1和任务2.1.2以及任务2.1做上标记。
能够画出黑棋,用同样的原理,我们也可以画出白棋,不过有一点不同就是白棋的底色我们做成了辐射渐变,具体参见代码:
在onrender方法中添加代码:
        protected override void onrender(drawingcontext dc)
        {
           ……
            drawblackstone(dc, new point(0,0));
            drawwhitestone(dc, new point(1, 0));
        }
 
       private void drawwhitestone(drawingcontext dc, point pos)
        {
            ellipsegeometry stone = new ellipsegeometry(pos, 0.49, 0.49);
 
            ellipsegeometry highlight = new ellipsegeometry();
            highlight.center = pos - new vector(0.3, 0);
            highlight.radiusx = 0.15;
            highlight.radiusy = 0.25;
 
            lineargradientbrush highlightbrush = new lineargradientbrush(colors.white, colors.transparent, new point(0, 0), new point(1, 0));
 
            radialgradientbrush stonebrush =new radialgradientbrush(colors.white, colors.gray);
            stonebrush.gradientorigin = new point(0.25, 0.5);
            stonebrush.radiusx = 1.5;
            stonebrush.radiusy = 1.7;
 
            dc.pushtransform(new rotatetransform(45, pos.x, pos.y));
            dc.drawgeometry(stonebrush, null, stone);
            dc.drawgeometry(highlightbrush, null, highlight);
            dc.pop();
        }
编译运行。
给任务2.2做标记。似乎也可以给任务2做标记,但是还不能,看一下代码,我们发现两个画棋子的方法有许多重复的代码,而且也有许多硬编码的数字。这是应该对代码进行重构的信号。
通过分析两个画棋子的代阿,我们发现除了黑白棋子使用的笔刷不同,其他的都一样,所以我们可以合并两个方法为一个方法,为了区分画的是白棋还是黑棋,我们引入一个枚举类型:
首先,在项目中添加一个新的文件:stonecolor.cs
代码如下:
    public enum stonecolor
    {
        none,
        black,
       white
}
 
接着,我们在boardcontrol类中,创建一个新的方法drawstone:
       private void drawstone(drawingcontext dc, point pos, stonecolor color)
        {
            if(color==stonecolor.none)
                return;
 
            ellipsegeometry stone = new ellipsegeometry(pos, 0.49, 0.49);
 
            ellipsegeometry highlight = new ellipsegeometry();
            highlight.center = pos - new vector(0.3, 0);
            highlight.radiusx = 0.15;
            highlight.radiusy = 0.25;
 
            lineargradientbrush highlightbrush = new lineargradientbrush(colors.white, colors.transparent, new point(0, 0), new point(1, 0));
 
            brush stonebrush = null;
            if (color == stonecolor.black)
                stonebrush = brushes.black;
            else
            {
                stonebrush =new radialgradientbrush(colors.white, colors.gray);
                ((radialgradientbrush)stonebrush).gradientorigin = new point(0.25, 0.5);
                ((radialgradientbrush)stonebrush).radiusx = 1.5;
                ((radialgradientbrush)stonebrush).radiusy = 1.7;
            }
 
            dc.pushtransform(new rotatetransform(45, pos.x, pos.y));
            dc.drawgeometry(stonebrush, null, stone);
            dc.drawgeometry(highlightbrush, null, highlight);
            dc.pop();
        }
修改onrender中的两个调用方法为:
            drawstone(dc, new point(0,0),stonecolor.black);
            drawstone(dc, new point(1, 0),stonecolor.white);
……
删除原来的drawblackstone和drawwhitestone
最后我们把硬编码的数字全部替换为常量:
重构的一个原则就是不能对原来的功能作修改。重构后的代码如下:
 
        const double stone_r = 0.49;
    
        const double high_light_offset_x = 0.3;
        const double high_light_offset_y = 0;
        const double high_light_rx = 0.15;
        const double high_light_ry = 0.25;
 
        const double stone_brush_origin_x = 0.25;
        const double stone_brush_origin_y = 0.5;
        const double stone_brush_rx = 1.5;
        const double stone_brush_ry = 1.7;
        const double stone_rotate_angle = 45;
 
        private void drawstone(drawingcontext dc, point pos, stonecolor color)
        {
            if(color==stonecolor.none)
                return;
 
            ellipsegeometry stone = new ellipsegeometry(pos, stone_r, stone_r);
 
            ellipsegeometry highlight = new ellipsegeometry();
            highlight.center = pos - new vector(high_light_offset_x, high_light_offset_y);
            highlight.radiusx = high_light_rx;
            highlight.radiusy = high_light_ry;
 
            lineargradientbrush highlightbrush = new lineargradientbrush(colors.white, colors.transparent, new point(0, 0), new point(1, 0));
 
            brush stonebrush = null;
            if (color == stonecolor.black)
                stonebrush = brushes.black;
            else
            {
                stonebrush =new radialgradientbrush(colors.white, colors.gray);
                ((radialgradientbrush)stonebrush).gradientorigin = new point(stone_brush_origin_x, stone_brush_origin_y);
                ((radialgradientbrush)stonebrush).radiusx = stone_brush_rx;
                ((radialgradientbrush)stonebrush).radiusy = stone_brush_ry;
            }
 
            dc.pushtransform(new rotatetransform(stone_rotate_angle, pos.x, pos.y));
            dc.drawgeometry(stonebrush, null, stone);
            dc.drawgeometry(highlightbrush, null, highlight);
            dc.pop();
        }
现在可以对任务2做上标记了。
任务3:棋子的位置可以通过鼠标来指定(准确地说是鼠标左键)
任务3看上去好像并不难,你只要注册boardcontrol的鼠标事件,就可以得到鼠标的位置,然后把位置和颜色的信息传递回boardcontrol就可以画出棋子,不过有些问题需要解决,让我们边做边看会是什么问题。
首先,我们不能在xaml文件中像其他控件那样写mouseleftbuttondown=”…”, cider并不是别用户控件的事件,除非是重载这个事件,所以我们可以显式的在代码中进行注册:
在mainwindow的构造器中添加一行代码:
        public mainwindow()
        {
            initializecomponent();
            boardcontrol.mouseleftbuttondown +=new mousebuttoneventhandler(boardcontrol_mouseleftbuttondown);
        }
 
通过智能提示,自动生成下面的方法签名:
        void boardcontrol_mouseleftbuttondown(object sender, mousebuttoneventargs e)
        {
            throw new exception("the method or operation is not implemented.");
        }
 
为了传递数据给棋盘控件,我们也设计一个新的类stone来表示我们的棋子对象,对项目添加一个新的类stone, 代码如下:
    public class stone
    {
        stonecolor color = stonecolor.none;
        point position;
 
        public stone() : this(stonecolor.none, new point(-1, -1)) { }
 
        public stone(stonecolor color, point position)
        {
            this.color = color;
            this.position = position;
        }       
       
        public stonecolor color
        {
            get { return color; }
            set { color = value; }
        }
 
        public point position
        {
            get { return position; }
            set { position = value; }
        }
}
向boardcontrol传递数据,我们设想boardcontrol有一个方法叫做addstone(stone stone)提供我们调用,所以boardcontrol_mouseleftbuttondown中的代码如下:
        void boardcontrol_mouseleftbuttondown(object sender, mousebuttoneventargs e)
        {
            point pos = e.getposition(boardcontrol);
            pos.x=(int)math.floor(pos.x+0.5);
            pos.y=(int)math.floor(pos.y+0.5);
 
            stone stone =new stone(stonecolor.black, pos);
            boardcontrol.addstone(stone);
     }
代码中,我们必须鼠标返回的位置做四舍五入取整。否则棋子的位置可能会出现偏差。
然后我们在boardcontrol中添加新的方法:
        public void addstone(stone stone)
        {
            drawingvisual dv = new drawingvisual();
            drawingcontext dc = dv.renderopen();
            drawstone(dc, stone.position, stone.color);
            dc.close();
        }
drawingvisual是我们第一次见到的wpf的可视对象,它派生于visual类,有关的资料参阅文档中的描述。
addstone通过dv对象打开一个drawingcontext, 这样我们就可以调用drawstone来画出棋子。删除onrender中的最后两个调用drawstone的语句,我们不需要它了。如果你现在编译运行,当你点击棋盘,期待出现黑色的棋子,结果一定让你失望。因为什么都没有发生!
这是因为在addstone的方法中,创建的dv没有加入到棋盘的子控件中,很不幸的是,你不能把dv添加到children中,因为children要求uiememt类型或者派生类的对象。 解决的方法就是我们自己定义一个集合,然后重载boardcontrold的getvisualchild和visualchildrencount两个方法。
在boardcontrol类中添加字段:
        visualcollection stonevisuals = null;
在构造器的开始创建stonevisuals的实例:
        public boardcontrol()
        {
            stonevisuals =new visualcollection(this);
            initializecomponent();
        }
在addstone方法的最后面添加一行代码:
       public void addstone(stone stone)
        {
            ……
            stonevisuals.add(dv);
        }
两个重载方法的代码:
        protected override visual getvisualchild(int index)
        {
            if (index < 0 || index > stonevisuals.count)
                return base.getvisualchild(index);
 
            return stonevisuals[index];
        }
 
        protected override int visualchildrencount
        {
            get{ return stonevisuals.count;}
        }
 
重新编译运行。
你可以通过修改调用addstone方法的stone参数的color属性来看白色棋子的显示。
我们可以模拟下棋的时候黑白棋子交替的场景,只要做些许的修改:
切换到mainwindow的source方式,添加一个字段:
        stonecolor currentcolor = stonecolor.black;
添加一个方法:
        private void exchangcolor()
        {
            currentcolor = currentcolor == stonecolor.white ? stonecolor.black : stonecolor.white;
        }
 
最后修改boardcontrol_mouseleftbuttondown方法中的代码如下:
        void boardcontrol_mouseleftbuttondown(object sender, mousebuttoneventargs e)
        {
            point pos = e.getposition(boardcontrol);
            pos.x=(int)math.floor(pos.x+0.5);
            pos.y=(int)math.floor(pos.y+0.5);
 
            stone stone =new stone(currentcolor, pos);
            boardcontrol.addstone(stone);
 
            exchangcolor();
        }
通过模拟,我们发现了新的任务,我们注意到了几个问题,那就是不允许在已经有棋子的地方落子;第二个问题那就是我们可以落子,也必须能够删除已有的棋子,这在以后的功能开发中是必要的,比如在undo或者提子(吃子)的场合。第三,为了方便打谱,必须有一个选项能够在棋子上面显示该棋子的手序。为此我们添加新的任务到任务表。
5、 检测禁止落子点
6、 显示和隐藏棋子的手序数
7、 提供回退和自动提子功能
别忘了给任务3做上标记。
(待续)

文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

文章页数:[1] 


放大字体显示 缩小字体显示 打印文章 推荐给朋友
热门文章
·用正则表达式过滤脚本的一些研究(asp.net + C#)-.NET教程,C#语言
·射频电路设计的困境及对策
·ASP.NET2.0下为GridView添加鼠标滑过(onmouseover、onmouseout)的行颜色高亮效果!-.NET教程,Asp.Net开发
·Java程序性能测试-JSP教程,Java技巧及代码
·利用JSP 2.0开发Web应用程序-JSP教程,Jsp/Servlet
·PHP模板引擎SMARTY-PHP教程,PHP应用
·.NET中的设计模式五:观察者模式-.NET教程,.NET Framework
·ultradev实例教程:5 做一个相对简单的网站后台(3)
·ASP.net 2.0 自定义控件的开发之数据分页 第二章-.NET教程,Asp.Net开发
·我的DataGrid嵌套DataList(未解决)-ASP教程,ASP应用
最新文章
·桌面壁纸站的建设_站长心得
·dotmore的法宝: 文中关联广告的未来_网赚技巧
·如何用10几个页面的e文小站做到月入350刀_网赚技巧
·fackbook创始人:脸谱是社交工具,而非社区_站长访谈
·搜搜客ceo:分类信息是人与人的价值流动_站长访谈
·丫客网李松:把实业工作和资本市场分开看_站长访谈
·windows xp中巧妙去掉多余的安全删除硬件图标_windows xp
·个人如何建站与站建好后期的运作_站长心得
·google沙盒效应产生的原因及其避免方法_google推广
·提高google adsense广告单价的投放方法 _google推广
相关主题
  • .net 2.0(c#)下简单的ftp应用程序_asp.net实例
  • .net 1.1 下不使用 system.web.mail.smtpmail 发送邮件的其它选择_asp.net技巧
  • .net 后台的小偷程序_asp.net技巧
  • .net 2.0中新增的nullable类型_asp.net技巧
  • .net 2.0 sqldependency快速上手指南_数据库技巧
  • 西部数码虚拟主机

    友情链接
    CNNIC 西部数码
    万网 自助建站
    虚拟主机 asp空间
    域名注册 域名
    域名申请 主页空间
    论坛空间 网站空间
    国际域名 虚拟空间
    空间租用 DDOS防火墙
    成都主机托管 四川主机托管
    主机租用 服务器租用
    网站目录 自助建站
    虚拟主机 网址大全
    软件下载
    自助链接
    虚拟主机资讯 特价虚拟主机
    版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
    关于我们:站长天空:专业提供最新的站长资讯、在线教程、虚拟主机权威评测、虚拟主机性能对比、网站制作教程,开发教程,站长工具。包括网页制作教程、冲浪宝典、编程参考、操作系统、软件教学、行业动态等。
    特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
    发表评论 打印  刷新     关闭