为WPF和Silverlight的Grid添加边框线

为WPF和Silverlight的Grid添加边框线,第1张

概述为WPF和Silverlight的Grid添加边框线 Grid是WPF和Silverlight中的一个重要的布局元素,其他的布局元素还有StackPanel, Canvas, Border等等。从字面上说,Grid是一个表格的意思,它的使用也确实很方便,从视觉上很像一个表格的样式,有行,有列的概念,这种效果很适合于需要多多个子控件进行布局,并希望保持左边或者上对齐的效果。 我们来看一个最简单的例子 为WPF和Silverlight的Grid添加边框线

GrID是WPF和Silverlight中的一个重要的布局元素,其他的布局元素还有StackPanel,Canvas,border等等。从字面上说,GrID是一个表格的意思,它的使用也确实很方便,从视觉上很像一个表格的样式,有行,有列的概念,这种效果很适合于需要多多个子控件进行布局,并希望保持左边或者上对齐的效果。

我们来看一个最简单的例子(本文采用Silverlight做演示,在WPF中也是一样的)

使用GrID的时候,一般先定义GrID的行和列的设置,然后在其放置其他控件并且设置他们的行号和列号即可,语法和语义都很简单和清晰

<UserControl    x:Class="SilverlightApplicationGrIDborderSample.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/Expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    mc:Ignorable="d"    d:DesignHeight="300"    d:DesignWIDth="400">    <GrID        x:name="LayoutRoot"        Background="White">                <GrID.Resources>            <Style                targettype="TextBlock">                <Setter                    Property="FontSize"                    Value="30"></Setter>                <Setter                    Property="VerticalAlignment"                    Value="Center"></Setter>            </Style>        </GrID.Resources>                <GrID.RowDeFinitions>            <RowDeFinition></RowDeFinition>            <RowDeFinition></RowDeFinition>        </GrID.RowDeFinitions>        <GrID.ColumnDeFinitions>            <ColumnDeFinition></ColumnDeFinition>            <ColumnDeFinition></ColumnDeFinition>        </GrID.ColumnDeFinitions>        <TextBlock            Text="左上角"></TextBlock>        <TextBlock            Text="左下角"            GrID.Row="1"></TextBlock>        <TextBlock            Text="右上角"            GrID.Column="1"></TextBlock>        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>    </GrID></UserControl>

嗯,看起来很好理解的。但是,几乎所有人(包括我在内)在最开始学习的时候,马上就会想到一个问题:

既然是用一个表格形状进行布局了,那么能不能显示出来表格的边框线呢?

没想到这会是一个问题,对吧?或者你想到过了,但没有找到如何解决这个问题。

如果是这样,那么请继续往下看吧

 

本文完整源代码,可以通过这里下载

http://files.cnblogs.com/chenxizhang/SilverlightApplicationGridBorderSample.rar

 

第一步:使用ShowGrIDlines属性

根据经验,我们会先从GrID这个元素上面去想办法。我们确实可以找到一个与我们需求很相近的属性:ShowGrIDlines,好吧,将它设为true之后,会怎么样呢

<UserControl    x:Class="SilverlightApplicationGrIDborderSample.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/Expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    mc:Ignorable="d"    d:DesignHeight="300"    d:DesignWIDth="400">    <GrID        x:name="LayoutRoot"        Background="White" ShowGrIDlines="True" >                <GrID.Resources>            <Style                targettype="TextBlock">                <Setter                    Property="FontSize"                    Value="30"></Setter>                <Setter                    Property="VerticalAlignment"                    Value="Center"></Setter>            </Style>        </GrID.Resources>                <GrID.RowDeFinitions>            <RowDeFinition></RowDeFinition>            <RowDeFinition></RowDeFinition>        </GrID.RowDeFinitions>        <GrID.ColumnDeFinitions>            <ColumnDeFinition></ColumnDeFinition>            <ColumnDeFinition></ColumnDeFinition>        </GrID.ColumnDeFinitions>        <TextBlock            Text="左上角"></TextBlock>        <TextBlock            Text="左下角"            GrID.Row="1"></TextBlock>        <TextBlock            Text="右上角"            GrID.Column="1"></TextBlock>        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>    </GrID></UserControl>

 

哦,看起来确实有边框了。但是,效果却不理想。这个边框线是虚线,坦白说,不是那么好看。从MSDN文档中我们了解到,这个边框线只是用来辅助我们做调试用的,而不适宜于在真正的产品中用。

私下里说,我并不认为这是一个好的设计,为什么不提供实线(甚至可以由开发人员配置)的边框呢

 

第二步:使用手工定义的方式实现GrID边框线

我们希望给GrID自动添加边框,应该怎么实现呢?其实,如果仅仅是给一个GrID添加的话,手工写一点代码就可以了

<UserControl    x:Class="SilverlightApplicationGrIDborderSample.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/Expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    mc:Ignorable="d"    d:DesignHeight="300"    d:DesignWIDth="400">    <GrID        x:name="LayoutRoot"        Background="White">                <GrID.Resources>            <Style                targettype="TextBlock">                <Setter                    Property="FontSize"                    Value="30"></Setter>                <Setter                    Property="VerticalAlignment"                    Value="Center"></Setter>            </Style> <Style targettype="border"> <Setter Property="borderBrush" Value="lightGray"></Setter> <Setter Property="borderThickness" Value="1"></Setter> </Style>        </GrID.Resources>                <GrID.RowDeFinitions>            <RowDeFinition></RowDeFinition>            <RowDeFinition></RowDeFinition>        </GrID.RowDeFinitions>        <GrID.ColumnDeFinitions>            <ColumnDeFinition></ColumnDeFinition>            <ColumnDeFinition></ColumnDeFinition>        </GrID.ColumnDeFinitions>        <TextBlock            Text="左上角"></TextBlock>        <TextBlock            Text="左下角"            GrID.Row="1"></TextBlock>        <TextBlock            Text="右上角"            GrID.Column="1"></TextBlock>        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>                <!--添加4个边框--> <border></border> <border GrID.Row="1"></border> <border GrID.Column="1"></border> <border GrID.Row="1" GrID.Column="1"></border>    </GrID></UserControl>

 

所以,其实我们可以手工添加border(文章开头提到几个布局元素中,只有border有边框线)实现我们的需求。

但问题在于,如果有很多GrID,都要这么去添加总是不好的吧,有没有办法更好地实现这样功能呢

 

第三步:使用附加依赖属性(Attached Dependency Property)实现GrID边框线

既然官方并没有提供我们需要的边框线,那么我们就自己来实现一个吧。事实上,这并没有多难,尤其是你理解了WPF和Silvelight中一些核心的概念的情况下,这些概念包括依赖属性和附加依赖属性。

对这两个概念,大家可以参考上面我给出的两个链接,这里我就简单地说几句吧,

依赖属性(Dependency Property)是WPF和Silverlight较之前的编程模型的一个核心改变,它使得基于绑定的编程变得可能,这个大家多少有些体会了,在WPF和Silverlight中,绑定无处不在,而且功能确实强大,尤其是双向绑定及自动通知,减少了很多很多的用户代码。 附加属性(Attached Property),则是另外一种场景,它一般用来对现有控件或者元素进行扩展。其实,我们之前的XAML中已经用到了附加属性,请看下面的部分
        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>

 

TextBlock这个元素,其实并没有行和列的概念,或者说它也不需要,除非它是放在一个GrID里面的时候。所以,行和列并不是TextBlock的属性,但是如果将它放在GrID里面,不提供这些信息又不行,所以附加属性就应运而生了。GrID.Row和GrID.Column就是附加属性,很显然,有了附加属性,我们就可以在不改变TextBlock的前提下,为它添加很多特性或者功能。

 

理解了附加属性,我们来说说现在我们要解决的问题:

我们能不能自动给每个GrID添加一个属性,让它可以为自己添加必要的边框线呢?

答案就是附加属性。

请添加一个代码文件,将下面代码粘贴进去

using System.windows;using System.windows.Controls;using System.windows.Media;namespace SilverlightApplicationGrIDborderSample{    /// <summary>    /// 为GrID添加的一个特殊功能    /// 作者:陈希章    /// 反馈:[email protected]    /// </summary>    public class GrIDHelper    {        //请注意:可以通过propa这个快捷方式生成下面三段代码        public static bool GetShowborder(DependencyObject obj)        {            return (bool)obj.GetValue(ShowborderProperty);        }        public static voID SetShowborder(DependencyObject obj,bool value)        {            obj.SetValue(ShowborderProperty,value);        }        public static Readonly DependencyProperty ShowborderProperty =            DependencyProperty.Registerattached("Showborder",typeof(bool),typeof(GrIDHelper),new PropertyMetadata(OnShowborderChanged));        //这是一个事件处理程序,需要手工编写,必须是静态方法        private static voID OnShowborderChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)        {            var grID = d as GrID;            if((bool)e.oldValue)            {                grID.Loaded -= (s,arg) => { };            }            if((bool)e.NewValue)            {                grID.Loaded += (s,arg) =>                {                    //确定行和列数                    var rows = grID.RowDeFinitions.Count;                    var columns = grID.ColumnDeFinitions.Count;                    //每个格子添加一个border进去                    for(int i = 0; i < rows; i++)                    {                        for(int j = 0; j < columns; j++)                        {                            var border = new border() { borderBrush = new SolIDcolorBrush(colors.Gray),borderThickness = new Thickness(1) };                            GrID.SetRow(border,i);                            GrID.SetColumn(border,j);                            grID.Children.Add(border);                        }                    }                };            }        }    }}

上面的代码应该很好理解,我们是用代码实现了与手工添加border一样的功能,奥妙在于,GrID有一个Loaded事件,我们完全可以在这个事件里面,根据计算得到的行和列去添加border。

 

如何在页面中使用我们附加属性呢?我们需要在XAML中稍做修改

<UserControl    x:Class="SilverlightApplicationGrIDborderSample.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/Expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    mc:Ignorable="d"    d:DesignHeight="300"    d:DesignWIDth="400"    xmlns:ext="clr-namespace:SilverlightApplicationGrIDborderSample">    <GrID        ext:GrIDHelper.Showborder="True"        x:name="LayoutRoot"        Background="White">        <GrID.Resources>            <Style                targettype="TextBlock">                <Setter                    Property="FontSize"                    Value="30"></Setter>                <Setter                    Property="VerticalAlignment"                    Value="Center"></Setter>            </Style>        </GrID.Resources>        <GrID.RowDeFinitions>            <RowDeFinition></RowDeFinition>            <RowDeFinition></RowDeFinition>        </GrID.RowDeFinitions>        <GrID.ColumnDeFinitions>            <ColumnDeFinition></ColumnDeFinition>            <ColumnDeFinition></ColumnDeFinition>        </GrID.ColumnDeFinitions>        <TextBlock            Text="左上角"></TextBlock>        <TextBlock            Text="左下角"            GrID.Row="1"></TextBlock>        <TextBlock            Text="右上角"            GrID.Column="1"></TextBlock>        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>    </GrID></UserControl>

 

请注意,我们只需要导入命名空间,然后在GrID上面设置ext:GrIDHelper.Showborder="True" 即可。这就是附加属性的神奇之处

看起来不错对吧?先不要着急,看看另外一个情况,加入我们希望把第一行的两列进行合并(ColumnSpan)的话,会怎么样呢?

<UserControl    x:Class="SilverlightApplicationGrIDborderSample.MainPage"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/Expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    mc:Ignorable="d"    d:DesignHeight="300"    d:DesignWIDth="400"    xmlns:ext="clr-namespace:SilverlightApplicationGrIDborderSample">    <GrID        ext:GrIDHelper.Showborder="True"        x:name="LayoutRoot"        Background="White">        <GrID.Resources>            <Style                targettype="TextBlock">                <Setter                    Property="FontSize"                    Value="30"></Setter>                <Setter                    Property="VerticalAlignment"                    Value="Center"></Setter>            </Style>        </GrID.Resources>        <GrID.RowDeFinitions>            <RowDeFinition></RowDeFinition>            <RowDeFinition></RowDeFinition>        </GrID.RowDeFinitions>        <GrID.ColumnDeFinitions>            <ColumnDeFinition></ColumnDeFinition>            <ColumnDeFinition></ColumnDeFinition>        </GrID.ColumnDeFinitions> <TextBlock Text="第一行合并两个列的内容" GrID.ColumnSpan="2"></TextBlock>        <TextBlock            Text="左下角"            GrID.Row="1"></TextBlock>        <TextBlock            Text="右下角"            GrID.Row="1"            GrID.Column="1"></TextBlock>    </GrID></UserControl>
 

 

我们看到,虽然确实第一行是合并了,但是边框线却仍然有两个,也就是说,我们在添加border的时候,没有考虑到行或者列合并的情况。这样就不是特别理想了。我们下面要改进这个属性。

 

第四步:改进附加属性适应行和列的合并情况

请注意,将代码做如下的改动

using System.windows;using System.windows.Controls;using System.windows.Media;namespace SilverlightApplicationGrIDborderSample{    /// <summary>    /// 为GrID添加的一个特殊功能    /// 作者:陈希章    /// 反馈:[email protected]    /// </summary>    public class GrIDHelper    {        //请注意:可以通过propa这个快捷方式生成下面三段代码        public static bool GetShowborder(DependencyObject obj)        {            return (bool)obj.GetValue(ShowborderProperty);        }        public static voID SetShowborder(DependencyObject obj,arg) =>                {                    //改进后的做法,不是简单地根据行和列,而是根据GrID的顶层子控件的个数去添加边框,同时考虑合并的情况 var controls = grID.Children; var count = controls.Count; for(int i = 0; i < count; i++) { var item = controls[i] as FrameworkElement; var border = new border() { borderBrush = new SolIDcolorBrush(colors.lightGray),borderThickness = new Thickness(1) }; var row = GrID.GetRow(item); var column = GrID.GetColumn(item); var rowspan = GrID.Getrowspan(item); var columnspan = GrID.GetColumnSpan(item); GrID.SetRow(border,row); GrID.SetColumn(border,column); GrID.Setrowspan(border,rowspan); GrID.SetColumnSpan(border,columnspan); grID.Children.Add(border); }                };            }        }    }}

改动之后的效果明显比较理想了,它考虑到了行和列的合并的情况。那么,事情结束了么?先不要着急,我们再来做一个事情,假设我们希望每个单元格中的内容都与边框(左,上,右,下)有一定的距离,怎么实现呢?

我们会自然联想到,给border设置padding属性就可以了吧,那么试试吧

using System.windows;using System.windows.Controls;using System.windows.Media;namespace SilverlightApplicationGrIDborderSample{    /// <summary>    /// 为GrID添加的一个特殊功能    /// 作者:陈希章    /// 反馈:[email protected]    /// </summary>    public class GrIDHelper    {        //请注意:可以通过propa这个快捷方式生成下面三段代码        public static bool GetShowborder(DependencyObject obj)        {            return (bool)obj.GetValue(ShowborderProperty);        }        public static voID SetShowborder(DependencyObject obj,arg) =>                {                    //改进后的做法,不是简单地根据行和列,而是根据GrID的顶层子控件的个数去添加边框,同时考虑合并的情况                    var controls = grID.Children;                    var count = controls.Count;                    for(int i = 0; i < count; i++)                    {                        var item = controls[i] as FrameworkElement;                        var border = new border()                        {                            borderBrush = new SolIDcolorBrush(colors.lightGray),borderThickness = new Thickness(1), padding= new Thickness(10)                        };                        var row = GrID.GetRow(item);                        var column = GrID.GetColumn(item);                        var rowspan = GrID.Getrowspan(item);                        var columnspan = GrID.GetColumnSpan(item);                        GrID.SetRow(border,row);                        GrID.SetColumn(border,column);                        GrID.Setrowspan(border,rowspan);                        GrID.SetColumnSpan(border,columnspan);                        grID.Children.Add(border);                    }                };            }        }    }}

看起来是可以的,但是运行起来,情况好像没有什么变化。我们的文字与边框仍然没有任何距离。

这是为什么呢?既然border设置了padding属性,那么又为什么实现不了我们需要的效果呢?
其实很简单,border的padding属性只影响它内部的子元素或者控件。我们上面的代码,只是创建了border,并且将其添加到GrID的Chiildren里面去。但并没有将那些TextBlock移动到border里面去,所以就实现不了padding效果了。

 

第五步:移动TextBlock到相应的border
using System.windows;using System.windows.Controls;using System.windows.Media;namespace SilverlightApplicationGrIDborderSample{    /// <summary>    /// 为GrID添加的一个特殊功能    /// 作者:陈希章    /// 反馈:[email protected]    /// </summary>    public class GrIDHelper    {        //请注意:可以通过propa这个快捷方式生成下面三段代码        public static bool GetShowborder(DependencyObject obj)        {            return (bool)obj.GetValue(ShowborderProperty);        }        public static voID SetShowborder(DependencyObject obj,arg) =>                {                    //这种做法自动将控件移动到border里面来                    var controls = grID.Children;                    var count = controls.Count;                    for(int i = 0; i < count; i++)                    {                        var item = controls[i] as FrameworkElement;                        var border = new border()                        {                            borderBrush = new SolIDcolorBrush(colors.lightGray),padding = new Thickness(20)                        };                        var row = GrID.GetRow(item);                        var column = GrID.GetColumn(item);                        var rowspan = GrID.Getrowspan(item);                        var columnspan = GrID.GetColumnSpan(item);                        GrID.SetRow(border,columnspan);                  grID.Children.RemoveAt(i); border.Child = item; grID.Children.Insert(i,border);                    }                };            }        }    }}

 

为了大家看到效果,我将padding设置为20. 请注意,上述代码中,我们先从GrID中移除掉了有关的控件,然后将这些控件添加到border里面去了。此所谓移花接木也。大家可以看到,现在每个格子里面的内容都与边框有一定的距离了。

 

本文完整源代码,可以通过这里下载

http://files.cnblogs.com/chenxizhang/SilverlightApplicationGridBorderSample.rar

 

总结:

本文采用循序渐进的方式演示了如何为GrID元素添加边框线的功能,从最原始的手工方式,逐渐演化到最后的解决方案。通过本文,你可以学会这个具体的场景问题解决方法,更可以体会到如何通过附加属性实现更多特殊的功能。


总结

以上是内存溢出为你收集整理的为WPF和Silverlight的Grid添加边框线全部内容,希望文章能够帮你解决为WPF和Silverlight的Grid添加边框线所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1073256.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存