Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1694056
  • 博文数量: 177
  • 博客积分: 9416
  • 博客等级: 中将
  • 技术积分: 2513
  • 用 户 组: 普通用户
  • 注册时间: 2006-01-06 16:08
文章分类

全部博文(177)

文章存档

2013年(4)

2012年(13)

2011年(9)

2010年(71)

2009年(12)

2008年(11)

2007年(32)

2006年(25)

分类:

2007-11-30 10:28:29

加班一个月了,昨天晚上终于告一段落。有了一点点收获,跟大家分享一下。
 
作为一个程序员,最忌讳两件事:只用剪刀和胶水来写程序;想到哪写到哪。copy和paste是必须用的,但不能不用脑子。东拼西凑的结果就是代码混乱,无法保证质量,甚至会有隐藏在水下的bug——网络上很多代码质量参差不齐。教科书从小就教育我们,舶来品要扬弃,去粗取精。因此,无论你找到的样例代码是多么匹配你的需求,你必须作的事情就是,研究它,然后再加以改进以融合到你的代码里。
 
关于这点,我自认为执行得很好。本月我总共分了4个任务,其中最后一个是对tabcontrol里的tab page实现拖放功能,用C#开发。MSDN是个好东西,MS的文档做的很好——但这不会降低我对MS的憎恨。翻了一天的MSDN,很多control的拖放的实现,就是没有tabcontrol的,因为它比较特殊。于是在网上找到了一个,阿良。NET,给出了实现方法和代码。其基本思想是继承一个tabcontrol,自己做一个带拖放功能的tabcontrol,主要代码及我做的改动一一列出(不同之处用绿色标明。当小tab移动到大tab上时,由于交换tab位置导致不停的闪烁,蓝色代码就是为了消除这种闪烁,当然还有别的实现):
=======================================================================
// Raise the MouseDown event
protected override void OnMouseDown(MouseEventArgs e)
{
   base.OnMouseDown(e);
   Point pt = new Point(e.X, e.Y);
   TabPage tp = GetTabPageByTab(pt);

   
   if(tp != null)
   {
       DoDragDrop(tp, DragDropEffects.All);
   }
}
因为我不能创建新类——这需要很高层的领导批准的,所以就把该方法的实现放到MouseDown事件处理方法中,如下:
// My code
private List rectList = new List();
private void tabControl1_MouseDown(object sender, MouseEventArgs e)
{
   if (e.Button == MouseButtons.Left) // 只响应左键事件
   {
      TabPage aTabPage = tabControl1.SelectedTab; // 不必像阿良的实现那么麻烦
      if (aTabPage != null)
      {
         tabControl1.DoDragDrop(aTabPage, DragDropEffects.Move);
         for (int i = 0; i < tabControl1.TabPages.Count; ++i)
         {
            rectList.Add(tabControl1.GetTabRect(i);
         }
      }
   }
}
=======================================================================
// Raise the DragOver event
protected override void OnDragOver(System.Windows.Forms.DragEventArgs e)
  {
   base.OnDragOver(e);
  
   Point pt = new Point(e.X, e.Y);
   //We need client coordinates.
   pt = PointToClient(pt);
   
   //Get the tab we are hovering over.
   TabPage hover_tab = GetTabPageByTab(pt);
   //Make sure we are on a tab.
   if(hover_tab != null)
   {
    //Make sure there is a TabPage being dragged.
    if(e.Data.GetDataPresent(typeof(TabPage)))
    {
     e.Effect = DragDropEffects.Move;
     TabPage drag_tab = (TabPage)e.Data.GetData(typeof(TabPage));
     int item_drag_index = FindIndex(drag_tab);
     int drop_location_index = FindIndex(hover_tab);
     //Don't do anything if we are hovering over ourself.
     if(item_drag_index != drop_location_index)
     {
      ArrayList pages = new ArrayList();
      
      //Put all tab pages into an array.
      for(int i = 0; i < TabPages.Count; i++)
      {
       //Except the one we are dragging.
       if(i != item_drag_index)
        pages.Add(TabPages[i]);
      }
      //Now put the one we are dragging it at the proper location.
      pages.Insert(drop_location_index, drag_tab);
      //Make them all go away for a nanosec.
      TabPages.Clear();
      //Add them all back in.
      TabPages.AddRange((TabPage[])pages.ToArray(typeof(TabPage)));
      
      //Make sure the drag tab is selected.
      SelectedTab = drag_tab;
     }
    }
   }
   else
   {
    e.Effect = DragDropEffects.None;
   }
  }
 
// My code. Implement DragOver event handler
private void tabControl1_DragOver(object sender, DragEventArgs e)
{
    Point aPoint = new Point(e.X, e.Y);
    //We need client coordinates.
    aPoint = tabControl1.PointToClient(aPoint);
    //Get the tab we are hovering over.
    TabPage hoveringTabPage = GetTabPageByTab(aPoint);
    //Make sure we are on a tab.
    if (hoveringTabPage != null)
    {
        //Make sure there is a TabPage being dragged.
        if (e.Data.GetDataPresent(typeof(TabPage)))
        {
            e.Effect = DragDropEffects.All;
            TabPage draggingTabPage = (TabPage)e.Data.GetData(typeof(TabPage));
            int item_drag_index = FindIndex(draggingTabPage);
            int drop_location_index = FindIndex(hoveringTabPage);
            //Don't do anything if we are hovering over ourself.
            if (item_drag_index != drop_location_index)
            {
                tabControl1.TabPages.Remove(draggingTabPage);
                tabControl1.TabPages.Insert(drop_location_index, draggingTabPage);
            }
        }
    }
    else
    {
        e.Effect = DragDropEffects.None;
    }
}
最关键的改动在“if (item_drag_index != drop_location_index)”块里。阿良用了一个list来存放所有的移动之后的tab page,然后清除原先的tabcontrol中的tab page,最后将list里的tab page再添回去。这样每次鼠标移动到另一个tab上时,都要重新操作很多tab page。当tab page数量巨大,就会有性能问题。而且,我的tab page需要动态加载很多control,并且执行许多操作来加载数据,这样的话,就会带来巨大的性能损失。修改之后只剩下两行代码,性能也提高了许多。对于这两行,给个图示:
当要拖动的tab page在drop地点之前:
选定时:
-----------------------------
| tab1 | tab2 | tab3 | tab4 |
-----------------------------
 ︿
 |拖动tab1对应的tab page
DragOver,鼠标在tab3上:
-----------------------------
| tab1 | tab2 | tab3 | tab4 |
-----------------------------
                ︿
                |dragover()得到tab3,insert index = 2
-----------------------------
| tab2 | tab3 | tab4 |      |
-----------------------------
                ︿
                |tab1被移出
-----------------------------
| tab2 | tab3 | tab1 | tab4 |
-----------------------------
                ︿
                |tab1被插入到index = 2的位置
当要拖动的tab page在drop地点之后:
选定时:
-----------------------------
| tab1 | tab2 | tab3 | tab4 |
-----------------------------
                       ︿
                       |拖动tab4对应的tab page
DragOver,鼠标在tab2上:
-----------------------------
| tab1 | tab2 | tab3 | tab4 |
-----------------------------
          ︿
          |dragover()得到tab2,insert index = 1
-----------------------------
| tab1 | tab2 | tab3 |      |
-----------------------------
           ︿
           |tab2被移出
-----------------------------
| tab1 | tab4 | tab2 | tab3 |
-----------------------------
          ︿
          |tab4被插入到index = 1的位置
=======================================================================
// My code to implement DragDrop event handler.
private void tabControl1_DragDrop(object sender, DragEventArgs e)
{
    tabControl1.SelectedTab = (TabPage)e.Data.GetData(typeof(TabPage));
    rectList.Clear();
}
 
private void tabControl1_MouseUp(object sender, MouseEventArgs e)
{
    if (rectList.Count != 0)
    {
        rectList.Clear();
    }
}
=======================================================================
private TabPage GetTabPageByTab(Point aPoint)
{
    TabPage aTabPage = null;
    for (int i = 0; i < tabControl1.TabPages.Count; ++i)
    {
        if (rectList[i].Contains(aPoint))
        {
            aTabPage = tabControl1.TabPages[i];
        }
    }
    return aTabPage;
}
阿良将上面的处理放到了DragOver中,其实这是正常处理。只不过,tabcontrol1实现了SelectedIndexChanged(),而这个方法里的操作也是非常影响性能的。这样我就把它挪到了DragDrop完成事件处理中。
 
好了,说完了copy和paste代码的问题,该说说另一个体会了。
 
大多数程序员,从菜鸟级到骨灰级,最容易犯的一个毛病就是:写代码时想到哪,写到哪,尽管犯这种毛病的原因很多,且不尽相同。菜鸟级一般急于看到结果以验证自己写的代码是否正确运行,骨灰级的一般可以在很短的时间内将脑袋里的想法转换成代码。
 
想到哪写到哪得结果就是,对于骨灰来说,顶多是加些修饰罢了,对于菜鸟来说就是灾难。在windows平台app开发领域,我就是一个不折不扣地菜鸟。前三个任务是UI方面的,第四个任务既有UI方面,又有Library的实现。对于windows GUI app的开发绝对超菜,但是时间紧迫,而MS的.NET Framework又非常庞大复杂,不容许有过多的时间去研究考虑,于是,犯毛病了——结果就是,写出来的代码一团糟,功能倒是对了,可是bug一大堆。我也想考虑好了再动手,可是MS的那套东西我实在是没概念,按照自己想法做的东西,在MS里根本不能跑。所以,痛恨MS,把windows开发人员的思维方式限定到他们的思维方式上。
 
后来做Library,除了编程语言,基本与MS无关,于是,在“小眼瞪大眼”研究了3天之后,花了2天时间,将代码写完并测完,一切OK!比计划还早一天完成!
 
总结一下,就是两点:
copy & paste代码时用点脑子。
写代码之前一定要想好。再小的代码量也要研究好,做好设计——当然,修改style之类超简单的事情就不用了,别浪费时间。
 
再补充一点,C#区分值类型和引用类型实在是一个很差的决定,因为没有显式的告诉programmer,哪些对象是value type,哪些是reference type,尤其是把structure作为value type,让人很容易将其和类混淆。尽管我已经很熟悉value type和reference type的概念,且完全理解了他们,但是编码过程中不可能每次都去查看某个对象是那个类型——虽然IDE会有提示——因为这种混淆,我花了30%的时间来调试问题。这可以说是C#的一个败笔吧。JAVA中所有的都是引用类型,除了native的类型如int、float。而C/C++将引用与值明显区分开(指针)。再次讨厌MS!
 
Copyleft (C) 2007 raof01. 本文及其代码可以用于任何用途,且不必事先征得作者同意。
阅读(2910) | 评论(4) | 转发(0) |
给主人留下些什么吧!~~

feiyinglinux2009-12-24 10:16:50

很多东西不是拿来就能用的