使用sqlcmd连接数据库出错 一、问题
使用sqlcmd客户端工具连接数据时,出现以下错误:
C:\>sqlcmd
HResult 0x2,级别 16,状态 1
命名管道提供程序: 无法打开与 SQL Server 的连接 [2].
Sqlcmd: 错误: Microsoft SQL Native Client : 建立到服务器的连接
允许远程连接这个事实可能会导致失败。。
Sqlcmd: 错误: Microsoft SQL Native Client : 登录超时已过期。

二、原因
1、网络协议及相关端口未打开
2、未指明使用何种身份认证方式登录

三、解决过程
1、开启网络协议
SQL Server Configuration Manager -> 网络配置 -> 协议
TCP/IP属性
保持活动状态 30000
全部侦听 否
无延迟 否
已启用 是

IP地址
IP地址 数据库服务器IP
TCP动态端口 不填
TCP端口 1433
活动 是
已启用 是
posted @ 2008-06-01 22:36 天使爱比目鱼 阅读(63) | 评论 (0)编辑

图片我已删去,我不忍心再看到他们那悲惨的样子,此时此刻,无论什么语言都难以表达出我心中的悲伤。

孩子们,一路走好...........


中国红十字会与央视网倡议募捐救援震区

CCTV.com  2008年05月13日 22:00  来源:央视网  
专题:四川汶川县发生7.8级地震

  据国家地震台网测定,北京时间5月12日14时28分,四川汶川县(北纬31度,东经103.4度)发生7.8级地震。一瞬间,一条条鲜活的生命被无情吞噬。当此危难,每一个中国人都心意相连。震灾将我们系于一脉。中国红十字会总会与央视网向网民发出呼吁,让我们伸出援手,与灾区的同胞同渡难关。

  ■ 中国红十字会总会救灾专用账号和热线

  开户单位:中国红十字会总会

  人民币开户行:中国工商银行北京分行东四南支行

  人民币账号:0200001009014413252

  外币开户行:中信银行酒仙桥支行

  外币账号:7112111482600000209

  热线电话:(8610)65139999

  ■ 捐款有如下几种途径:

  银行汇款:账号见上文

  邮局汇款:

  地址:北京市东城区北新桥三条8号

  邮编:100007

  网上捐款:登陆中国红十字会总会网站:www.redcross.org.cn,点击捐款热线栏目进行在线捐款。

  (通过银行、邮局和网上捐款在捐款时请注明捐款人姓名、通信地址、捐款意向如:四川地震捐款等信息,以便邮寄捐赠收据和感谢信)

  通过短信捐款:中国移动、中国联通手机用户以及中国电信、中国网通小灵通用户均可编辑短信1或2,发送至1069999301,即向“红十字救援行动”捐款1元钱或2元钱。

  通过短信咨询:中国移动、中国联通手机用户以及中国电信、中国网通小灵通用户均可编辑短信“中国红十字会”,发送至12114,即可了解中国红十字会有关情况。

  ■ 中国红十字基金会同时也接受社会各界捐赠:

  地址: 北京市东城区东单北大街干面胡同53号

  邮编:100010

  银行汇款:

  户名: 中国红十字基金会

  开户银行: 中国银行北京分行

  账号: 800100921908091001

  开户银行:中国工商银行北京东四南支行

  账号:0200001019014483874

  开户银行:中国建设银行北京朝内大街支行

  账号:11001070300059000427

  外币开户银行:中国银行

  账号: 800100086608091014


 
posted @ 2008-05-14 09:29 天使爱比目鱼 阅读(79) | 评论 (0)编辑
前几天下载了VS2008PRO光盘镜像文件,有一个疑问:为什么安装盘里的SQL版本是2005呢?难道说是SQL2008还有什么问题?
posted @ 2008-05-03 10:19 天使爱比目鱼 阅读(40) | 评论 (1)编辑
1、原材料:盒装478针CPU一块,要求全新的(频率大于2.66);
                缝衣针408根,要求直径<=0.5mm;
                鸡蛋一个。
2、步骤:(1)、将缝衣针焊接在CPU上,详细步骤请参见焊接工艺大全。
             (2)、将缝衣针箭成CPU针脚长短。
             (3)、这时你就得到一个单核的886针CPU。
             (4)、将做好的CPU安装到886针的主板上,开机。(886针主板制作方法另附文。)
             (5)、将鸡蛋的蛋黄取出,放在CPU上,
             (6)、待加热至熟后,恭喜你,你就得到了一块双核的886针CPU。
              PS:如果你一不小心遇上一个双黄鸡蛋,恭喜你,你将得到一块3核886针CPU

另:以上制作方法有一定风险,请在专业人士指导下完成。如有任何损失,本文概不负责。
posted @ 2008-04-29 08:13 天使爱比目鱼 阅读(269) | 评论 (1)编辑

做人不能CNN!(这不算涉及政治吧?)

posted @ 2008-04-17 18:06 天使爱比目鱼 阅读(18) | 评论 (0)编辑
数据库三范式:
1、第一范式:删除表中的重复数据组,为每组相关数据创建一个表,用一个主键标识每个表,该主键唯一的标识了每个数据行。
2、第二范式:为应用于多个记录的值集合创建表,并用外键关联这些表。
3、第三范式:删除了不依赖于主键的列。

通俗易懂。
posted @ 2008-03-31 22:09 天使爱比目鱼 阅读(44) | 评论 (0)编辑
今天家里终于安装了宽带,心情好高兴,以后终于有时间写博客了,庆祝一下先
posted @ 2008-03-24 22:16 天使爱比目鱼 阅读(14) | 评论 (0)编辑
好久没有上来写点东西了,没有别的原因,就一个字~懒
今天晚上实在是忍不住了决定写点东西,因为最近出现了很多新东西,对我来说主要的就是Windows Server 2008(简称WS2008)和VS2008。
奋斗了一个星期(下载了3天,用了2个晚上安装,N遍痛苦回忆),终于使用上了WS最新的操作系统和VS2008,第一印象:就是快~,这里的快是指WS2008安装的快和VS2008运行的快,WS2008完成安装大约用了30分钟左右(C2.66+1G),感觉比XP都快,运行也一样。VS2008安装比较痛苦,大约2个小时左右(包括MSDN),但运行速度比VS2003快不少。
先给出下载地址:
Windows Server 2008 RC1 Enterprise(英文版)
windows server 2008 激活 安装 设置 (独立中文语言包与必备程序下载)
VS2008 下载(简体中文试用版)
Microsoft Visual Studio 2008 概述白皮书下载

其他体验待续~
posted @ 2008-02-26 20:39 天使爱比目鱼 阅读(469) | 评论 (3)编辑

突然发现自己竟然不会编程了~  

     以前用VB2003 +SQL 2000 写了一个数据库应用程序,一直自我感觉良好,用着也很顺手,最近升级到VB2005+SQL2005 ,因为数据库的结构需要有些改动,所以打算重写,可是坐到计算机前却突然发现自己无从下手,整个软件结构竟然无法组织(以前从未想过软件结构,写出来的程序恐怕只有我自己看得懂,还是只看懂50%那种~  =_=! ),痛苦中~看来真的需要好好的学习一下,可是应该从哪里学起呢?
        求各位老大给点意见啊~(最好有具体的指导啊,应该从哪里学起,都应该看些什么书啊,书名啊)望不吝赐教~






         PS:新疆之旅归来,大脑缺氧中~呵呵

posted @ 2007-08-21 21:22 天使爱比目鱼 阅读(118) | 评论 (1)编辑
        7月26日我们一家三口踏上了前往新疆喀什的旅途,先到济南,在济南住了一晚,7月27日登上开往乌鲁木齐的火车,感觉就一个字:热。
       7月29日中午,顺利到达乌鲁木齐,并不像传说中的那么热,倒是乌鲁木齐的羊肉比想象中的还要好吃,饱餐一顿后休息一晚,30日继续前进。
       7月31日中午,在经过了5天5夜的长途跋涉后,我们终于到达旅行的目的地:喀什,下了火车,啥也不说,休息先。
       8月1日,喀什第一印象:羊肉比乌鲁木齐的还好吃,哈哈。今天正式开始旅行。
      献上照片先:
     大雪山~


雪山下的河流~



流沙~



美丽的塔湖~


红其拉普~界碑



雪山裸照~ ;-)


待续~


















posted @ 2007-08-21 20:56 天使爱比目鱼 阅读(118) | 评论 (4)编辑
最近看到同事们都忙着炒股,心动ing~兜里无钱~痛苦~
                                                    痛苦ing~股市大跌~庆幸~
                                                    庆幸ing~股市大涨~后悔~
股市,不以股民(应该说:小股民)的个人意志而改变~
人生,不以个人意志而改变~
工资,什么时候以我的意志而改变呢?
    
posted @ 2007-06-18 16:04 天使爱比目鱼 阅读(47) | 评论 (0)编辑
打开Msdn,发现以下内容:
  • Windows Server 2008 Beta 3
  • 代号为“Orcas”的 Visual Studio 的 Beta 1
    郁闷到死~
  • posted @ 2007-06-18 07:20 天使爱比目鱼 阅读(143) | 评论 (4)编辑
    昨天下载了VB.NET SP1 ,在Windows Server 2003 SP2 下安装,提示文件错误,怀疑系统文件错误,重做系统.....
    安装,继续提示文件错误,郁闷ing~
    又在Vista下安装,没有问题,难道是兼容性错误?
    posted @ 2007-06-16 16:30 天使爱比目鱼 阅读(250) | 评论 (6)编辑
    冬天过去了,春天回来了;我的博客,我又回来了。
    离开已经太久,当再次打开,发现虽然很久没有更新,却依然是熟悉的文章,熟悉的风格。
    久违了,我的博客。
    posted @ 2007-04-14 16:26 天使爱比目鱼 阅读(94) | 评论 (0)编辑
    昨晚奋战一夜,终于解决了Combobox控件的问题(http://www.cnblogs.com/lxzhangying/archive/2006/10/21/535483.html
    就是在保存数据之前加上一条:Combobox.text=combobox.selecttext
    问题就解决啦
    posted @ 2006-11-04 15:40 天使爱比目鱼 阅读(472) | 评论 (0)编辑

    简介

    几个月以前,当我初到 Microsoft 工作时,我的经理走进我的办公室,并且向我详细说明了我将在随后两个星期内将要从事的一个项目。我需要设想出一个应用程序,用于为 MSDN 内容策划人员整合衡量标准。其中一个功能要求是需要一个类似于 DataGrid 的控件,该控件使用户可以在将数据导出到 Microsoft Excel 电子表格之前,按照他们喜欢的顺序排列所有列。他在离开我的办公室之前说的最后一句话是:“将它变为有趣的用户体验。”

    我知道为了能够重新排列 DataGrid 列,我必须操纵 DataGrid 的 DataGridColumnStyle 属性以反映新的列排序,但这并没有什么吸引人之处。我想要的是对整个拖动操作实现可视化表示。我在开始时使用了一些 System.Drawing 功能,并且达到了能够在屏幕间拖动图形的程度。我断定我需要让它更进一步。我可以让它看起来更像是用户在拖动列,而不是仅仅在 DataGrid 绘图表面上拖动枯燥乏味的矩形进行绘制。我对本机 GDI 库进行了一番寻根究底,经过几个小时的试验后,我终于弄清楚为了实现这一技巧而需要完成的工作。

    dragdrop_datagrid-fig1

    1. 拖动操作

    入门

    我需要做的第一件事是弄清如何获得将要拖动列的屏幕快照。我完全清楚自己需要什么以及希望做什么,但是我不知道如何 去做。在发现 System.Drawing 命名空间下的类没有为我提供执行屏幕捕获所需的功能之后,我浏览了本机 GDI 库并且发现 BitBlt 函数正是我在寻找的东西。

    下一步是编写该函数的托管包装。我将在本文中讨论的第一点是,我该如何实现 ScreenImage 类。

    ScreenImage 类

    为了跨越互操作边界进行调用,我们需要声明非托管函数并且指明它们都来自哪些库,以便 JIT 编译器在运行时知道它们的位置。在完成这一工作后,我们只需像调用托管方法一样调用它们,就象下面的代码块所示。

    public sealed class ScreenImage {
    [DllImport("gdi32.dll")]
    private static extern bool BitBlt( IntPtr
    handlerToDestinationDeviceContext, int x, int y, int nWidth, int nHeight,
    IntPtr handlerToSourceDeviceContext, int xSrc, int ySrc, int opCode);
    [DllImport("user32.dll")]
    private static extern IntPtr GetWindowDC( IntPtr windowHandle );
    [DllImport("user32.dll")]
    private static extern int ReleaseDC( IntPtr windowHandle, IntPtr dc );
    private static int SRCCOPY = 0x00CC0020;
    public static Image GetScreenshot( IntPtr windowHandle, Point location, Size size ) {
    ... }
    }
    

    该类只公开一个方法 — GetScreenshot,它是一个静态方法,返回一个含有与 windowHandle、location 和 size 参数相对应的颜色数据的图形对象。下一个代码块显示如何实现该方法。

    public static Image GetScreenshot( IntPtr windowHandle, Point location, Size size ) {
    Image myImage = new Bitmap( size.Width, size.Height );
    using ( Graphics g = Graphics.FromImage( myImage ) ) {
    IntPtr destDeviceContext = g.GetHdc();
    IntPtr srcDeviceContext = GetWindowDC( windowHandle );
    // TODO: throw exception
    BitBlt( destDeviceContext, 0, 0, size.Width, size.Height,
    srcDeviceContext, location.X, location.Y, SRCCOPY );
    ReleaseDC( windowHandle, srcDeviceContext );
    g.ReleaseHdc( destDeviceContext );
    } // dispose the Graphics object
    return myImage;
    }
    

    让我们逐行地考察一下方法实现。我做的第一件事是创建一个尺寸与参数设置的大小相对应的新位图。

    Image myImage = new Bitmap( size.Width, size.Height );
    

    下面的代码行检索与刚刚创建的新位图相关联的绘图表面。

    using ( Graphics g = Graphics.FromImage( myImage ) ) { ... }
    

    C# using 关键字定义了一个范围,在该范围的末尾将处理 Graphics 对象。因为 System.Drawing 命名空间中的所有类都是本机 GDI+ API 的托管包装,所以我们几乎总是在处理非托管资源,并且因此需要确保丢弃不再需要其服务的资源。该过程称为确定性终止,通过该过程将对象使用的资源以其他目的立即进行重新分配,而不是等待垃圾回收器到访来完成它该做的工作。每当您在处理实现了 IDisposable 接口的对象(如,这里使用的 Graphics 对象)时,都应该遵守这种习惯。

    我检索了源和目标设备上下文的句柄,以便可以继续转换颜色数据。源是与参数设置的 windowHandle 句柄相关联的设备上下文,而目标是先前创建的位图中的设备上下文。

    IntPtr srcDeviceContext = GetWindowDC(windowHandle);
    IntPtr destDeviceContext = g.GetHdc();
    

    提示设备上下文是由 Windows 在内部维护的 GDI 数据结构,它定义了一组图形对象以及影响与这些对象相关的输出的图形模式。可以将其视为 Windows 提供的、可在上面绘图的画布。GDI+ 提供了三种不同的绘图表面:窗体(通常称为显示、打印机和位图)。在本文中,我们使用窗体和位图绘图表面。

    现在,我们具有一个已定义的 Bitmap 对象 (myImage) 和一个表示该对象的画布(它在这一执行时刻是透明的)的设备上下文。本机 BitBlt 方法需要我们要向其复制位的画布部分的坐标和大小,以及我们要从源设备上下文上开始复制位的坐标。该方法还需要一个光栅操作代码值,以便定义位块的转换方式。

    这里,我将目标设备上下文的起始坐标设置为左上角,并且将光栅操作代码值设置为 SRCCOPY(它表示要将源直接复制到目标)。十六进制等效值 (00x00CC0020) 可从 GDI 头文件中检索获得。

    BitBlt( destDeviceContext, 0, 0, size.Width, size.Height,
    srcDeviceContext, location.X, location.Y, SRCCOPY );
    

    一旦用完设备上下文,我们就需要将其释放。如果不这样做,将导致无法将该设备上下文用于随后的请求,并且可能导致引发运行时异常。

    ReleaseDeviceContext( windowHandle, destDeviceContext );
    g.ReleaseHdc( srcDeviceContext );
    

    我确认了 ScreenImage 类能够按预期工作,然后,我需要做的下一件事情是创建一个简单的数据结构,以便帮助我跟踪与被拖动的列相关的所有数据。

    DraggedDataGridColumn 类

    DraggedDataGridColumn 类是一个数据结构,用于监视所拖动列的各种状态,包括初始位置、当前位置、图像表示形式以及相对于该列的初始起点的光标位置。有关所有参数的详细说明,请参阅 DraggedDataGridColumn.cs 中的代码。

    提示如果类封装了实现 IDisposable 的对象,那么您就可能间接抓住非托管资源。在这种情况下,类还应该实现 IDisposable 接口并且对每个可处置的对象调用 Dispose() 方法。DraggedDataGridColumn 类封装了一个 Bitmap 对象,该对象显式抓住非托管资源,因此我必须完成这一步骤。

    在处理好这一问题之后,我就能够集中精力来解决有关难题的最主要部分了,即操纵 DataGrid 控件以获得我需要的可视效果。

    ColumnDragDataGrid 类

    DataGrid 控件是一个功能强大的重量级控件,但它本身不会向我们提供拖放列的能力,所以我必须扩展它并且自己来添加该功能。我处理了三个不同的鼠标事件,并且重写了 DataGrid 的 OnPaint 方法来满足我的所有绘图需要。

    首先,让我们看看所有用于跟踪应该在何处以及如何进行绘制的成员字段。

    成员字段 定义

    m_initialRegion

    一个 DraggedDataGridColumn 对象,表示有关当前正在拖动的列的所有相关内容。我将在本文后面详细讨论 DraggedDataGridColumn 类的细节。

    m_mouseOverColumnRect

    一个 Rectangle 结构,用于标识一个矩形区域,该区域表示鼠标光标当前正在哪个列上方悬停。

    m_mouseOverColumnIndex

    鼠标光标当前正在其上方悬停的列的索引。

    m_columnImage

    在启动拖放操作时包含列的位图表示形式的 Bitmap 对象。

    m_showColumnWhileDragging

    一个 Boolean 值,表示拖动列时是否应该显示捕获到的列图像。通过 ShowColumnWhileDragging 属性公开。

    m_showColumnHeaderWhileDragging

    一个 Boolean 值,表示当列被拖动时是否应该显示该列的头部。这是通过 ShowColumnHeaderWhileDragging 属性公开的。

    该类中的唯一构造函数是一个不带参数的构造函数,并且相当简单。但是,我觉得有一行代码值得一提:

    this.SetStyle( ControlStyles.DoubleBuffer, true );
    

    Windows 中的绘图过程分为两步。当应用程序进行绘图请求时,系统将生成绘图消息(先是 WM_ERASEBKGND,然后是 WM_PAINT)。这些消息被发送到应用程序消息队列中,然后,应用程序将在这里检查这些消息并将它们发送到适当的控件以便进行处理。WM_ERASEBKGND 消息的默认处理方式是用当前窗口背景色填充该区域。随后将处理 WM_PAINT,这会完成所有前景绘图。当您的操作序列涉及到清除背景以及在前景中绘图时,您将产生被称为闪烁 的令人不快的效果。值得庆幸的是,可以通过使用双缓冲 来减轻这一效果。

    对于双缓冲,您有两种不同的可以写入的缓冲。一种是存储在视频 RAM 中的可见的屏幕缓冲;另一种是不可见的离屏缓冲,它由内部 GraphicsBuffer 对象表示,并且存储在系统 RAM 中。当绘图操作启动时,将在上述 GraphicsBuffer 对象上呈现所有图形对象。一旦系统确定该操作已完成,就会快速同步这两个缓冲区。

    根据 .NET Framework 文档资料,为了在应用程序中实现双缓冲,您需要将 AllPaintingInWmPaintDoubleBufferUserPaintControlStyle 位设置为真。这里,我只需慎重考虑 DoubleBuffer 位。基类 DataGrid 已经将 AllPaintingInWmPaintUserPaint 位设置为真。

    上面提到的另外两个 ControlStyle 位被定义为:

    UserPaint该位设置为真时,会告诉 Windows 应用程序将完全负责该特定窗口(控件)的所有绘图。这意味着您将处理 WM_ERASEBKGND 和 WM_PAINT 消息。如果该位被设置为假,则应用程序仍将挂钩 WM_PAINT 消息,但它会将该消息发送回系统以进行处理,而不会执行任何绘图操作。当发生这种情况时,系统将尝试呈现该窗口,但是因为它不了解有关该窗口的任何信息,所以它的工作通常不会令人感到满意。

    AllPaintingInWmPaint正如该位的名称所表明的那样,当该位被设置为真时,所有绘图都将由控件的 WmPaint 方法进行处理。即使挂钩了 WM_ERASEBKGND 消息,该消息也将被忽略,并且永远不会调用控件的 OnEraseBackground 方法。

    在深入研究该类的其余部分之前,需要回顾两个重要的概念。

    无效

    当您使控件的特定区域无效时,该区域将被添加到控件的更新区域,以便告诉系统在下一个绘图操作过程中重新绘制哪个区域。如果更新区域未定义,则将重新绘制整个控件。

    dragdrop_datagrid-fig2

    2. 触发绘图操作前后无效区域的可视表示形式。在左侧,带有虚线边框的半透明灰色方形表示已定义的无效区域。右侧的方形显示了在执行绘图操作之后的外观。

    正如前面提到的那样,当调用控件的无效方法时,系统将生成 WM_PAINT 消息并将其发送给控件。在收到该消息以后,该控件将引发 Paint 事件;如果已经注册了侦听该事件的处理程序,则会将该事件添加到该控件的事件处理队列的后面。

    需要注意的是,被引发的 Paint 事件并不总是能够立即得到处理。这有很多原因,其中最重要的一点是 Paint 事件涉及到绘图中开销比较大的操作之一,并且通常是最后得到处理的事件。

    网格样式

    DataGridTableStyle 定义了将 DataGrid 绘制 到屏幕上的方式。即使它包含的属性类似于 DataGrid 的属性,它们也是互相排斥的。许多人错误地认为更改同名属性(如 DataGrid 的 RowHeadersVisible 属性)也会更改 DataGridTableStyle 的 RowHeadersVisible 属性的值。结果,当情况没有按预期的那样发展时,需要花费始料未及的时间来进行调试(不要担心,我也会犯这样的错误)。

    您可以创建一个包含不同表格样式的集合,并且将它们交替用于不同的数据实体和源。

    每个 DataGridTableStyle 都包含一个 GridColumnStylesCollection,它是在将数据绑定到 DataGrid 控件时自动创建的 DataGridColumnStyles 对象的集合。这些对象是 DataGridBoolColumnDataGridTextBoxColumn 或由第三方实现的列(它们都派生自 DataGridColumnStyle)的实例。如果您需要一个包含标签甚至图像的列,则您将必须通过创建 DataGridColumnStyle 的子类来创建一个自定义类。

    提示您需要重写 OnDataSource 方法(该方法在 DataGrid 控件被绑定到数据源时调用)。这样,您就可以使用多个样式,并且将它们的映射名称与 DataGrid 的 DataMember 属性值(该值在控件被绑定到数据源时设置)相关联。

    列跟踪

    绝大部分的列跟踪功能都发生在 MouseDownMouseMoveMouseUp 事件处理程序中。在下面的段落中,我将重点讨论这三个事件处理程序,并且对比较重要的代码段进行解释。这些处理程序所利用的 Helper 方法未予讨论。但是,如果看了代码,您就会发现我已经提供了这些方法的摘要。

    MouseDown

    当用户在网格上方单击鼠标时,我们需要做的第一件事就是确定单击鼠标的位置。为了启动拖动操作,必须在列标头的上方单击光标。如果证明该条件为真,则将收集一些列信息。我们需要知道该列的起点、宽度和高度,以及相对于列起点的鼠标光标位置。该信息用于建立在列被拖动时要跟踪的两个不同的列区域。

    Private void ColumnDragDataGrid_MouseDown(object sender, MouseEventArgs e) {
    DataGrid.HitTestInfo hti = this.HitTest( e.X, e.Y );
    if ( ( hti.Type & DataGrid.HitTestType.ColumnHeader ) != 0 &&
    this.m_draggedColumn == null ) {
    int xCoordinate = this.GetLeftmostColumnHeaderXCoordinate( hti.Column );
    int yCoordinate = this.GetTopmostColumnHeaderYCoordinate( e.X, e.Y );
    int columnWidth = this.TableStyles[0].GridColumnStyles[hti.Column].Width;
    int columnHeight = this.GetColumnHeight( yCoordinate );
    Rectangle columnRegion = new Rectangle( xCoordinate, yCoordinate, columnWidth, columnHeight );
    Point startingLocation = new Point( xCoordinate, yCoordinate );
    Point cursorLocation = new Point( e.X - xCoordinate, e.Y - yCoordinate );
    Size columnSize = Size.Empty;
    ...
    }
    ...
    }
    
    dragdrop_datagrid-fig3

    3. 列起点、列标头高度(通过 GetColumnHeaderHeight 方法计算)、列高度、列宽度和光标位置示意图

    该事件处理程序的其余部分相当简单。执行了一个条件计算以了解是否已经将 ShowColumnsWhileDraggingShowColumnHeaderWhileDragging 属性设置为真。如果是,则计算列大小并且调用 ScreenImage 的 GetScreenshot 方法。我传递了 DataGrid 控件的句柄(记住,控件是子窗口)、起始坐标和列大小,而该方法返回一个包含所需捕获区域的图形对象。然后,所有信息都被存储在一个 DraggedDataGridColumn 对象中。

    Private void ColumnDragDataGrid_MouseDown(object sender, MouseEventArgs e) {
    ...
    if ( ( hti.Type & DataGrid.HitTestType.ColumnHeader ) != 0 &&
    this.m_draggedColumn == null ) {
    ...
    if ( ShowColumnWhileDragging || ShowColumnHeaderWhileDragging ) {
    if ( ShowColumnWhileDragging ) {
    columnSize = new Size( columnWidth, columnHeight );
    } else {
    columnSize = new Size( columnWidth, this.GetColumnHeaderHeight( e.X, yCoordinate ) );
    }
    Bitmap columnImage = ( Bitmap ) ScreenImage.GetScreenshot(
    this.Handle, startingLocation, columnSize );
    m_draggedColumn = new DraggedDataGridColumn( hti.Column,
    columnRegion, cursorLocation, columnImage );
    } else {
    m_draggedColumn = new DraggedDataGridColumn( hti.Column,
    columnRegion, cursorLocation );
    }
    m_draggedColumn.CurrentRegion = columnRegion;
    }
    ...
    }
    

    MouseMove

    每当鼠标光标在 DataGrid 上方移动时,都会引发 MouseMove 事件。在处理该事件的过程中,我首先跟踪被拖动的列当前在其上方悬停的列,以便可以向用户提供一些可视反馈。其次,我跟踪该列的新位置并发出无效指令。

    让我们进一步考察一下代码。我需要做的第一件事是确保列被拖动,然后我通过从相对于控件的鼠标坐标中减去相对于列起点的鼠标坐标来获得该列的 x 坐标(图 4,刻度线标志 #1)。这可以为我提供该列的 x 坐标。因为 y 坐标永远不会更改,所以我不必花费功夫来检查它。

    private void ColumnDragDataGrid_MouseMove(object sender, MouseEventArgs e) {
    DataGrid.HitTestInfo hti = this.HitTest( e.X, e.Y );
    if ( m_draggedColumn != null ) {
    int x = e.X - m_draggedColumn.CursorLocation.X;
    ...
    }
    }
    
    dragdrop_datagrid-fig4

    4. 刻度线标志 #1 显示了 m_draggedColumn.CursorLocation.X 中存储的值。该值从当前光标位置(其坐标相对于控件)中减去。

    然后,我检查光标是否悬停在单元格的上方(列标头也被视为单元格)。如果不是,则我假设用户希望中止拖动操作。

    private void ColumnDragDataGrid_MouseMove(object sender, MouseEventArgs e) {
    ...
    if ( m_draggedColumn != null ) {
    if ( hti.Column >= 0 ) {
    ...
    } else {
    InvalidateColumnArea();
    ResetMembersToDefault();
    }
    }
    }
    

    接下来,我希望向用户提供某种反馈,以便他们知道所拖动的列将在他们释放鼠标按键时放置到何处。

    这是通过 m_mouseOverColumnIndex 成员字段进行跟踪的,该字段存储了以下列的索引:该列的边界包含光标在处理最后一个 MouseMove 事件之后的当前位置。如果该值不同于点击测试为我们提供的列索引,则用户正在将鼠标悬停在不同列的上方。如果是这样,则将使 m_mouseOverColumnRect 成员字段指示的区域无效,并且记录新区域的坐标。然后,使该新区域无效,以便 Windows 知道这一等待它关注区域的新绘图指令。

    private void ColumnDragDataGrid_MouseMove(object sender, MouseEventArgs e) {
    ...
    if ( m_draggedColumn != null ) {
    ...
    if ( hti.Column >= 0 ) {
    if ( hti.Column != m_mouseOverColumnIndex ) {
    // NOTE: moc = mouse over column
    int mocX = this.GetLeftmostColumnHeaderXCoordinate( hti.Column );
    int mocWidth =
    this.TableStyles[0].GridColumnStyles[hti.Column].Width;
    // indicate that we want to invalidate the old rectangle area
    if ( m_mouseOverColumnRect != Rectangle.Empty ) {
    this.Invalidate( m_mouseOverColumnRect );
    }
    // if the mouse is hovering over the original column, we do not want to
    // paint anything, so we negate the index.
    if ( hti.Column == m_draggedColumn.Index ) {
    m_mouseOverColumnIndex = -1;
    } else {
    m_mouseOverColumnIndex = hti.Column;
    }
    m_mouseOverColumnRect = new Rectangle( mocX,
    m_draggedColumn.InitialRegion.Y, mocWidth,
    m_draggedColumn.InitialRegion.Height );
    // invalidate this area so it gets painted when OnPaint is called.
    this.Invalidate( m_mouseOverColumnRect );
    }
    ...
    } else { ... }
    }
    }
    

    随后,将变换焦点以有助于跟踪被拖动列的位置。我需要弄清楚是向左还是向右拖动它,以便我可以获得最左边的 x 坐标。在获得该值后,将使列的旧区域和新区域无效,并且将与新位置相关的数据存储在 m_draggedColumn 中。

    private void ColumnDragDataGrid_MouseMove(object sender, MouseEventArgs e) {
    ...
    if ( m_draggedColumn != null ) {
    ...
    if ( hti.Column >= 0 ) {
    ...
    int oldX = m_draggedColumn.CurrentRegion.X;
    Point oldPoint = Point.Empty;
    // column is being dragged to the right
    if ( oldX < x ) {
    oldPoint = new Point(  oldX - 5, m_draggedColumn.InitialRegion.Y );
    // to the left
    } else {
    oldPoint = new Point( x - 5, m_draggedColumn.InitialRegion.Y );
    }
    Size sizeOfRectangleToInvalidate = new Size( Math.Abs( x - oldX )
    + m_draggedColumn.InitialRegion.Width +
    ( oldPoint.X * 2 ), m_draggedColumn.InitialRegion.Height );
    this.Invalidate( new Rectangle( oldPoint, sizeOfRectangleToInvalidate ) );
    m_draggedColumn.CurrentRegion = new Rectangle( x,
    m_draggedColumn.InitialRegion.Y,
    m_draggedColumn.InitialRegion.Width, m_draggedColumn.InitialRegion.Height );
    } else { ... }
    }
    }
    

    MouseUp

    当用户在单元格上方松开鼠标按键时,将执行条件计算,以确保将拖动的列放置到除了其发送方之外的列的上方。如果列索引中表达式计算为真(该列索引不是产生它的列的索引),则切换列。否则,将重新绘制该网格。

    private void ColumnDragDataGrid_MouseUp(object sender, MouseEventArgs e) {
    DataGrid.HitTestInfo hti = this.HitTest( e.X, e.Y );
    // is column being dropped above itself? if so, we don't want
    // to do anything
    if ( m_draggedColumn != null && hti.Column != m_draggedColumn.Index ) {
    DataGridTableStyle dgts = this.TableStyles[this.DataMember];
    DataGridColumnStyle[] columns = new DataGridColumnStyle[dgts.GridColumnStyles.Count];
    // NOTE: csi = columnStyleIndex
    for ( int csi = 0; csi < dgts.GridColumnStyles.Count; csi++ ) {
    if ( csi != hti.Column && csi != m_draggedColumn.Index ) {
    columns[csi] = dgts.GridColumnStyles[csi];
    } else if ( csi == hti.Column ) {
    columns[csi] = dgts.GridColumnStyles[m_draggedColumn.Index];
    } else {
    columns[csi] = dgts.GridColumnStyles[hti.Column];
    }
    }
    // update TableStyle
    this.SuspendLayout();
    this.TableStyles[this.DataMember].GridColumnStyles.Clear();
    this.TableStyles[this.DataMember].GridColumnStyles.AddRange( columns );
    this.ResumeLayout();
    } else {
    InvalidateColumnArea();
    }
    ResetMembersToDefault();
    }
    

    在跨越了该功能的难点之后,触发必要的绘图操作将非常容易。

    重写 DataGrid 的 OnPaint 方法

    迄今为止,您可能已经注意到没有在任何鼠标事件处理程序中执行任何绘图逻辑。这完全归结为个人喜好。我已经看到其他开发人员将他们的绘图逻辑与其余逻辑和并在一起使用,但我发现将所有绘图逻辑都放在 OnPaint 方法或 Paint 事件处理程序中会更为简单、更有条理。

    需要重写 DataGrid 的 OnPaint 方法,以便容纳附加的绘图操作。首先要确保调用基本的 OnPaint 方法,以便绘制基础 DataGrid。这为我提供了可用来进行绘制的画布。

    请记住,当您在画布上绘制对象时,z 排序要视对象的绘制顺序而定。了解这一点以后,我们需要首先绘制最底层的形状。

    得到绘制的第一个图形是用于指示正在拖动哪个列的矩形( 5,刻度线标志 #1)。

    dragdrop_datagrid-fig5

    5. 不同的绘制步骤

    通过使用 Graphics 对象的 m_draggedColumn 方法,我们在产生拖动操作的列的上方绘制了一个矩形。该区域信息是从 DraggedDataGridColumn 对象中检索到的。使用了半透明的画笔,以便使底层的列仍然可见。然后,在上述矩形的边框周围绘制了一个黑色矩形,以使其具有更为完整的修饰。

    protected override void OnPaint( PaintEventArgs e ) {
    ...
    if ( m_draggedColumn != null ) {
    SolidBrush blackBrush = new SolidBrush( Color.FromArgb( 255, 0, 0, 0 ) );
    SolidBrush darkGreyBrush = new SolidBrush( Color.FromArgb( 150, 50, 50, 50 ) );
    Pen blackPen = new Pen( blackBrush, 2F );
    g.FillRectangle( darkGreyBrush, m_draggedColumn.InitialRegion );
    g.DrawRectangle( blackPen, region );
    ...
    }
    }
    

    GDI 中的颜色被分解为四个 8 位的成分,其中的三个成分代表三原色:红色、绿色和蓝色。Alpha 成分(同样是 8 位)确定了颜色的透明度 — 它影响颜色与背景的融合方式。通过 Color.FromArgb 方法可以创建具有特定值的颜色。

    Color.FromArgb( 150, 50, 50, 50 ) // dark grey with alpha translucency level set to 150
    

    我在本文前面提到的列反馈是以半透明的浅灰色矩形的形式完成的(图 5,刻度线标志 #2)。首先,我检查列索引以确保它不是 -1,然后使用 m_mouseOverColumnRect 中存储的矩形区域数据在该列上方填充一个矩形。

    protected override void OnPaint( PaintEventArgs e ) {
    ...
    if ( m_draggedColumn != null ) {
    // user feedback indicating which column the dragged column is over
    if ( this.m_mouseOverColumnIndex != -1 ) {
    using ( SolidBrush b = new SolidBrush( Color.FromArgb( 100, 100, 100, 100 ) ) ) {
    g.FillRectangle( b, m_mouseOverColumnRect );
    }
    }
    }
    }
    

    下一个焦点区域是正在拖动的列。如果用户已经选择在拖动操作发生时显示列或列标头,则绘制该图像。捕获的图像存储在 m_draggedColumn 中,并且可以通过 ColumnImage 属性访问。

    protected override void OnPaint( PaintEventArgs e ) {
    ...
    if ( m_draggedColumn != null ) {
    ...
    // draw bitmap image
    if ( ShowColumnWhileDragging || ShowColumnHeaderWhileDragging ) {
    g.DrawImage( m_draggedColumn.ColumnImage,
    m_draggedColumn.CurrentRegion.X, m_draggedColumn.CurrentRegion.Y );
    }
    ...
    }
    }
    

    最后,填充一个半透明矩形以代表拖动操作。这将使用与第一个图形类似的方式完成。从 m_draggedColumn 中读取列区域信息。然后,再绘制一个矩形以进一步增强前面的矩形( 5,刻度线标志 #3)。

    protected override void OnPaint( PaintEventArgs e ) {
    ...
    if ( m_draggedColumn != null ) {
    ...
    g.FillRectangle(  filmFill, m_draggedColumn.CurrentRegion.X,
    m_draggedColumn.CurrentRegion.Y, m_draggedColumn.CurrentRegion.Width,
    m_draggedColumn.CurrentRegion.Height );
    g.DrawRectangle( filmBorder, new Rectangle(
    m_draggedColumn.CurrentRegion.X, m_draggedColumn.CurrentRegion.Y +
    Convert.ToInt16( filmBorder.Width ), width, height ) );
    ...
    }
    }
    
    posted @ 2006-10-28 07:32 天使爱比目鱼 阅读(499) | 评论 (0)编辑

    昨天用到VB2005 的Combobox控件用来绑定数据,结果发现一个问题:当Combobox的ComboBoxStyle属性设置为DropDownList时,Combobox不能很好的绑定到数据。程序运行后,从Combobox的列表中选择的有效值不变时,则数据不能提交到数据集中,保存数据的时候提示Combobox绑定的字段的数据未设置。也就是说:每添加一条记录时,Combobox的选定值必须同上条记录的选定值不一样,Combobox选定的有效值才能提交到绑定的字段中,而ComboBoxStyle属性设置为DropDown时则没有上述问题。(注:Combobox的有效值绑定为另一个Table的字段)

    posted @ 2006-10-21 09:09 天使爱比目鱼 阅读(842) | 评论 (0)编辑
            近来有点感冒,昨晚写到十点半就感觉有点疲劳了,只好停下来整理整理思绪。自从用VS2005写数据库程序以来觉得用数据控件写方便是方便,可是就是有点力不从心,或者说是不能随心所欲,记得用VS2003时,写数据库程序用的都是数据代码,写的十分顺手。不过话又说回来了,代码写的程序就是没有用控件写的规范,经常出现些不可预知的错误,数据控件就不会有这些问题。
            想着想着大脑就乱套了,只好关灯休息。
    posted @ 2006-10-13 14:45 天使爱比目鱼 阅读(105) | 评论 (2)编辑
    硬盘终于好了,联想的售后服务还可以啊,第二天就把我的硬盘给换了一个,不过没能提供数据恢复服务,有点可惜。而且联想的“电脑拯救系统”也要一星期后才能恢复。真不明白为什么电脑上带的功能却没有给带光盘。
    posted @ 2006-10-12 10:49 天使爱比目鱼 阅读(29) | 评论 (0)编辑
    灾难!,我的硬盘损坏了,而且是灾难性故障,所有数据都丢失了,郁闷啊。最重要的是不能上博客了。唉,没办法,只好用WinPE开机上网上博客了 。WinPE的感觉就是不一样啊,虽然速度慢一点,但是想到没有硬盘都可以上网上博客心里就十分满足了,哈哈,我那该死的硬盘啊。。。呜呜
    posted @ 2006-10-10 16:15 天使爱比目鱼 阅读(157) | 评论 (0)编辑