.Net Framework3.0 实践纪实(2)

用户控件——棋盘 

 

显示棋盘可能想上去并不太难,首先使用一个Canvas(画布)控件,然后在上面画上我们需要的水平和垂直线条,它们的Xaml代码如下:
<Grid>
<Canvas Name=”board” Width=”400” Height=”400” Grid.Row=”1” Grid.Column=”1” >
<Line X1=”20” Y1=”20” X2=”380” Y2=”20” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”40” X2=”380” Y2=”40” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”60” X2=”380” Y2=”60” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”80” X2=”380” Y2=”80” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”100” X2=”380” Y2=”100” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”120” X2=”380” Y2=”120” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”140” X2=”380” Y2=”140” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”160” X2=”380” Y2=”160” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”180” X2=”380” Y2=”180” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”200” X2=”380” Y2=”200” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”220” X2=”380” Y2=”220” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”240” X2=”380” Y2=”240” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”260” X2=”380” Y2=”260” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”280” X2=”380” Y2=”280” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”300” X2=”380” Y2=”300” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”320” X2=”380” Y2=”320” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”340” X2=”380” Y2=”340” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”360” X2=”380” Y2=”360” Stroke=”Black” StrokeThickness=”1” />
<Line X1=”20” Y1=”380” X2=”380” Y2=”380” Stroke=”Black” StrokeThickness=”1” />
 
//交换X1 Y1 和X2 Y2的值将画出19道平行的垂直线,代码略
</Canvas>
编译确认你没有错误,然后运行程序。你将看到一个棋盘出现在窗体的右边。检查任务表,我们完成了1.1的任务,但是1.2的任务只能说完成了一部分。我们首先必须解决一个问题,那就是棋盘的大小应能够随着窗体的缩放作相应的缩放,并且它的高宽比必须保持不变。我们可以通过代码来设置,但是最简单的方法就是把棋盘放在一个ViewBox控件中,下面是代码:
<Grid>
      < Viewbox Grid.Row = "1"Grid.Column="1">
           < Canvas Name = "board"Width="400"Height="400" >
            …
           </ Canvas >
      </ Viewbox >
</Grid>
把Grid.Row和Grid.Column的属性从Canvas元素中移到Viewbox元素,因为现在Viewbox是Canvas的容器。
再次运行程序,缩放窗体,观察棋盘的显示。
1.2任务中还有一个要求,那就是棋盘不总是19*19道纵横线,但是从我们的xaml代码中可以看出我们的纵横线是固定的,也就是说它是硬编码,为了满足我们的需求,我们需要重构我们的棋盘。我决定从Canvas派生一个定制控件,这样我就可以根据我的需要来显示不同的棋盘。下面是操作步骤:
1、鼠标右键TopGo项目,选添加新项,然后在类型模板对话框选WinFx User Control, 在名称输入框输入BoardControl.xaml, 确定。
2、打开BoardControl.xaml文件,切换到xaml方式,把根元素从UserControl改为Canvas。删除<Grid></Grid>标签。
3、切换到Source方式,把BoardControl类改成从Canvas派生:
public partial class BoardControl : Canvas
为了能够动态的修改棋盘的大小(不是外观的大小,这里是指棋盘的纵横线数),我们需要定义一个属性BoardSize。你可以定义一个int的字段,然后声明一个读写属性,但这里我们采用WPF的依赖性属性(DependencyProperty)对象来定义BoardSize。依赖性属性更容易被使用在动画、风格等上面,具体请阅读文档。声明一个DependencyProperty,你需要定义一个静态只读DependencyProperty字段,并对属性进行注册。幸运的是,WPF的开发者们已经为我们设计了一个code snnipet,这样我们就可以很方便的插入一个依赖性属性:
l         在BoardControl类构造器后面的空白处(如果没有空白,就回车留出至少一行的空白)点击鼠标右键,在右键菜单中选Insert Snnipet, 然后选WinFx, 选Define a dependencyProperty。
l         输入属性名称:BoardSize; 属性的拥有类为:BoardControl; 默认值:19。定义后的代码如下所示:
 
public int BoardSize
{
     get { return (int)GetValue(BoardSizeProperty); }
   set { SetValue(BoardSizeProperty, value); }
}
 
// Using a DependencyProperty as the backing store for BoardSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BoardSizeProperty =
    DependencyProperty.Register("BoardSize", typeof(int), typeof(BoardControl), new UIPropertyMetadata(19));
 
3、有了BoardSize,我们就可以根据它来画出棋盘的纵横线了。做过WinForm的朋友一定知道可以在窗体的Paint方法中画图。不过WPF的窗体并没有Paint,类似的方法是OnRender, 所以我们重载OnRender方法:
        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);
        }
 
一个DrawingContext参数对象可以用来画图,画线的代码如下:
        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);
 
 
            Pen pen =new Pen(Brushes.Black, 1);
 
            for (double d = 0; d < BoardSize; d += 1)
            {
                dc.DrawLine(pen, new Point(d, 0), new Point(d, BoardSize - 1));
                dc.DrawLine(pen, new Point(0, d), new Point(BoardSize - 1, d));
            }
 
        }
 
1、 回到MainWindows.xaml, 用我们新设计的BoardControl替换<Canvas Name=”board” …>, 但是我们还需要做一件事,为我们的控件定义一个xml的命名空间:在<Window的元素中加上属性如下:
< Window x:Class = "TopGo.MainWindow"
    xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:uc = "clr-namespace:TopGo"
    Title = "TopGo" MinHeight="600"MinWidth="800"WindowState="Maximized"
    >
 
修改后的棋盘部分的xaml如下:
      < Viewbox Grid.Row = "1"Grid.Column="1">
        < uc:BoardControl BoardSize = "19"Width="19"Height="19" />
      </ Viewbox >
一个很不幸的事情,那就是因为Cider还是CTP阶段,所以功能还没有完善,从此刻开始,我们再也不能在Design方式下预览和编辑界面了,包括对控件的拖放。同时在xaml中也会出现一些不被识别的元素或者属性。
编译运行程序,你看到什么?对,显示的不是我们想象中的棋盘,而是一块”黑板”。这是因为我们的棋盘的长度和宽度设置为19,在BoardControl中,我们线条的间隔设置为1,线条的宽度也是1,当使用Viewbox的时候,这样的一个图片就被放大到ViewBox的大小,所以线条的宽度因此也被放大。我们只要修改线条的宽度即可,通常我们需要计算被放大的比率,然后线条的宽度除以这个比率即可,但这里我采用一个固定的比率,为了便于计算,这个比率假设棋盘最终的实际大小为500*500,500/19约等于26,也就是说假设的棋盘被放大26倍。线条的宽度= 1/26  约等于0.03。虽然不是精确的计算,但可以满足我们程序的要求,差别也不是很大,所以我们设置线条的宽度为0.03, 其他的比例也按照这个比率进行。
切换到BoardControl的Source方式,声明一个私有字段scale, 变动如下:
    public partial class BoardControl : Canvas
    {
        double scale = 0.03;
          … …
        protected override void OnRender(DrawingContext dc)
        {
            base.OnRender(dc);
 
            Pen pen =new Pen(Brushes.Black, scale);
          … …
重新编译运行,这一次黑板变成棋盘了。
接下来,我们测试BoardSize的其他值,修改BoardSize=”19” 成BoardSize=”10”; 运行程序,线条显示正确了,但是棋盘变小了,其实棋盘大小没有变,还是19,在尺寸为19的棋盘上画间距等于1的10道纵横线,当然就是你看到的样子,所以我们在修改BoardSize的同时也要修改BoardControl的Width和Height。设置Width=”10” Height=”10”, 结果显示正确。
线条粗了近一倍,但是不影响我们的需求。
到目前为止,我们已经完成了任务1.1和1.2。你可以把这给这两个任务打上标记。如果你仔细观察实际的围棋棋盘,你会发现围棋棋盘上除了纵横线还有9个小点(围棋术语称之为“星”),所以我们有了一个新的任务,就是画出棋盘上的星,任务表现在看上去是这样:
1           TopGo必须能够显示一个棋盘;
1.1          棋盘在界面上的位置
1.2          画棋盘的纵横线(标准为19*19 ),棋盘的大小必须可以动态设置比如说(10*10 )
1.3          画出棋盘上的星(星的数量应该和棋盘大小一致)
 
2           TopGo的棋盘必须可以在一个指定的位置显示指定颜色的棋子;
3           棋子的位置可以通过鼠标来指定(准确地说是鼠标左键)
还有其他新的需求吗?下一次我们继续。
(待续)

你可能感兴趣的:(.net,Class,animation,任务,WPF,WinForm)