随笔 - 44  文章 - 0 评论 - 304 trackbacks - 88
<2007年5月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

本博客上的所有文章如非特别说明均为原创,如果要转载请注明文章出处。

与我联系

搜索

 

常用链接

留言簿(20)

我参加的小组

我参与的团队

随笔分类(40)

随笔档案(44)

文章分类

联系我

友情链接

积分与排名

  • 积分 - 173135
  • 排名 - 207

最新评论

阅读排行榜

评论排行榜

    最近真的真的太忙了,以至于一个多月都没哟更新我的blog。昨天晚上,一个网上的朋友看了我的ToolBox的文章,问我一个问题,他说如何让ToolBox控件也能响应键盘操作,也就是用Updown按键来选择工具箱控件里的Item,他添加了键盘事件,但是不起作用。一开始做这个控件的时候也只是演示一下控件的制作过程,只用了很短的时间做了一个,只考虑了用鼠标选取,没有考虑键盘操作,我想要添加键盘操作无非重载KeyDown事件,针对UpDown做一些响应就可以了。可是添加了重载了OnKeyDown事件后,结果和那位朋友所说的一样,没有任何作用,我设了断点,调试了一下,发现KeyDown根本捕获不到UpDown按键的点击,是什么原因呢,是不是忘记设控件的风格以便让它能够获得焦点?于是,我使用了语句:
   
SetStyle(ControlStyles.Selectable, true);
依然没有效果,当我们在控件上按下Down键的时候,另一个控件获得了焦点。这时UpDown按钮只是起到了导航的作用就像Tab键一样。
      接下来,我在测试工程的窗体上放置了一个ListBox控件做一个对比,其实ToolBoxListBox在界面表现上有相似之处,就是都有子Item,并且在ListBox上点击Down是起作用的,ListBox并没有失去焦点,这说明这时UpDown按键没有成为导航键。我想Windows一定是对默认的导航键UpDown,Left,Right有默认的处理,除非你希望你的控件希望自己处理这些键。用反汇编工具看了一下ListBoxControl控件的源代码,发现一个有趣的函数:
protected override bool IsInputKey(Keys keyData)
{
    
if ((keyData & Keys.Alt) == Keys.Alt)
    
{
        
return false;
    }

    
switch ((keyData & Keys.KeyCode))
    
{
        
case Keys.Prior:
        
case Keys.Next:
        
case Keys.End:
        
case Keys.Home:
            
return true;
    }

    
return base.IsInputKey(keyData);
}


在这里面,ListBoxControl允许PriorNextEndHome成为有效的输入键,接着一路跟下去,看看WinForm控件的基类Control的这个函数是如何处理的:
[UIPermission(SecurityAction.InheritanceDemand, Window=UIPermissionWindow.AllWindows)]
protected virtual bool IsInputKey(Keys keyData)
{
    
if ((keyData & Keys.Alt) != Keys.Alt)
    
{
        
int num = 4;
        
switch ((keyData & Keys.KeyCode))
        
{
            
case Keys.Left:
            
case Keys.Up:
            
case Keys.Right:
            
case Keys.Down:
                num 
= 5;
                
break;

            
case Keys.Tab:
                num 
= 6;
                
break;
        }

        
if (this.IsHandleCreated)
        
{
            
return ((((intthis.SendMessage(0x8700)) & num) != 0);
        }

    }

    
return false;
}


      注意这一行return ((((int) this.SendMessage(0x87, 0, 0)) & num) != 0);0x87是什么windows消息呢,打开WinUser.h文件,发现是WM_GETDLGCODE,MSDN中的描述是这样的:
      The WM_GETDLGCODE message is sent to the window procedure associated with a control. By default, the system handles all keyboard input to the control; the system interprets certain types of keyboard input as dialog box navigation keys. To override this default behavior, the control can respond to the WM_GETDLGCODE message to indicate the types of input it wants to process itself.
      也就是说windows用这个消息来判断哪些类型的输入交给控件本身来处理。然后,我注意到,对于方向导航键,函数都给于一个值5this.SendMessage(0x87, 0, 0))的返回值进行与操作,那么this.SendMessage(0x87, 0, 0))的返回值都可能是什么值呢,WinUser.h中是这样声明的:
   
/*
 * Dialog Codes
 
*/

#define DLGC_WANTARROWS     0x0001      /* Control wants arrow keys         */
#define DLGC_WANTTAB        0x0002      /* Control wants tab keys           */
#define DLGC_WANTALLKEYS    0x0004      /* Control wants all keys           */
#define DLGC_WANTMESSAGE    0x0004      /* Pass message to control          */
#define DLGC_HASSETSEL      0x0008      /* Understands EM_SETSEL message    */
#define DLGC_DEFPUSHBUTTON  0x0010      /* Default pushbutton               */
#define DLGC_UNDEFPUSHBUTTON 0x0020     /* Non-default pushbutton           */
#define DLGC_RADIOBUTTON    0x0040      /* Radio button                     */
#define DLGC_WANTCHARS      0x0080      /* Want WM_CHAR messages            */
#define DLGC_STATIC         0x0100      /* Static item: don't include       */
#define DLGC_BUTTON         0x2000      /* Button item: can be checked      */
      5最贴切的表达就是DLGC_WANTMESSAGE | DLGC_WANTARROWS,也就是将方向键发送给控件处理,对于6呢,也就是DLGC_WANTMESSAGE| DLGC_WANTTAB,将Tab键发送给控件处理。
   
    从这段代码里和控件实际的行为我们可以得出一个结论,那就是,控件本身是不处理方向键和Tab键的,因为他们有默认的行为,也就是支持焦点在窗体的控件之间转换。如果你想要处理这些导航键,那么结论很简单,就是重载IsInputKey方法,它是一个保护类型的虚方法。       
      在ToolBox控件的代码里重载IsinputKey方法:
        protected override bool IsInputKey(Keys keyData)
        
{
            
if ((keyData & Keys.Alt) == Keys.Alt)
            
{
                
return false;
            }

            
switch ((keyData & Keys.KeyCode))
            
{
                
case Keys.Up:
                
case Keys.Down:                
                    
return true;
            }

            
return base.IsInputKey(keyData);

        }

       

       当用户点击的键是UpDown的时候,返回true,这时我们的OnKeyDown方法里就可以捕获到UpDown的点击事件了。
posted on 2007-05-11 22:51 纶巾客 阅读(4651) 评论(11)  编辑 收藏 所属分类: WinForm Control

FeedBack:
#1楼  2007-05-12 09:05 宁宁 [未注册用户]
你的有关控件开发的资料昨天开始拜读,觉得很有启发,以后会经常来这里
  回复  引用    
#2楼  2007-05-12 09:20 ACCP [未注册用户]
请问在Winform中,怎样与短信猫接口相接
  回复  引用    
#3楼  2007-05-21 21:52 asboy      
麻烦了,我在做一个皮肤控件的时候不知道怎么处理这个问题。
当 FORM 变化的时候怎么样这个 SKIN也跟着变化呢,用哪个事件处理比较好呢?
  回复  引用  查看    
#4楼 [楼主] 2007-05-22 08:53 纶巾客      
@asboy
你用SizeChanged 事件试试吧。
  回复  引用  查看    
#5楼  2007-05-27 21:35 loxiro [未注册用户]
非常感谢
  回复  引用    
#6楼  2007-08-06 19:23 smart-liu      
up,启发很发,感谢
  回复  引用  查看    
#7楼  2007-09-12 11:16 IAmPeter [未注册用户]
关注中^o^
期待下一篇……
  回复  引用    
#8楼  2007-10-15 11:05 jxzhaogang [未注册用户]
感谢,学习了。很受启发。
我正在写的一个控件。出了点问题,想请教一下,不知道能不能帮忙看看。
我的目的很简单。就是在COMBOBOX的基础上增加一个Label,其它功能COMBOBOX有的都有就行了。
代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace JXERPCOMPONENT
{
public partial class JXLabelCombobox : UserControl
{
public JXLabelCombobox()
{
InitializeComponent();
this.DataBindings.CollectionChanged+=new CollectionChangeEventHandler (DataBindings_CollectionChanged);
}

void DataBindings_CollectionChanged(object sender, CollectionChangeEventArgs e)
{
if ((e.Action == CollectionChangeAction.Add) && (e.Element != null) && ((e.Element as Binding).PropertyName == "SelectValue")) //Text
{
Binding bd = new Binding("SelectedValue", (e.Element as Binding).DataSource, (e.Element as Binding).BindingMemberInfo.BindingField);
this.comboBox.DataBindings.Add(bd);
}
}
private object selectvalue;
public object SelectValue
{
get {
this.selectvalue = this.comboBox.SelectedValue;
return this.selectvalue;
}
set
{
this.selectvalue = value;
this.comboBox.SelectedValue = value;
this.Invalidate();
}
}
}
做了一个测试范例:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;

namespace Componenttest
{
public partial class Frmjxcontrol : Form
{
public Frmjxcontrol()
{
InitializeComponent();
}

private void Frmjxcontrol_Load(object sender, EventArgs e)
{
ArrayList al = new ArrayList();
al.Add(new a("a1",1));
al.Add(new a("a2",2));
al.Add(new a("a3", 3));

ArrayList b1 = new ArrayList();
b1.Add(new b("b1",1));
b1.Add(new b("b2", 2));

this.dataGridView1.DataSource = al;

this.comboBox1.DataSource = b1;
this.comboBox1.DisplayMember = "B1";
this.comboBox1.ValueMember = "C2";
this.comboBox1.DataBindings.Add("SelectedValue", al, "C2");

this.jxLabelCombobox2.DataSource = b1;
this.jxLabelCombobox2.DisplayMember = "B1";
this.jxLabelCombobox2.ValueMember = "C2";
this.jxLabelCombobox2.DataPropertyName = "C2";
this.jxLabelCombobox2.DataBindings.Add("SelectValue", al, "C2");
this.jxLabelCombobox2.Enabled = true;
}
}
public class a
{
public a(string vc1, int vc2)
{
c1 = vc1;
c2 = vc2;
}

private string c1;
public string C1
{
get { return c1; }
set { c1 = value; }
}
private int c2;
public int C2
{
get { return c2; }
set { c2 = value; }
}

}
public class b
{
public b(string vb1, int vc2)
{
b1 = vb1;
c2 = vc2;
}
private string b1;
public string B1
{
get { return b1; }
set { b1 = value; }
}
private int c2;
public int C2
{
get { return c2; }
set { c2 = value; }
}

}
}
问题:
每次从GRID的第三行跳到第二行时都回报错。
请指教。TKS。

  回复  引用    
#9楼  2007-11-09 10:33 飄lá┽蕩去      
怎么没有后续的了,写的很好啊,一看就明白了

  回复  引用  查看    
#10楼  2008-06-12 15:21 冯伟      
在开发过程中,自定义控件,自定义可绑定属性Value,通过DataBindings.add绑定到DataTable上之后,在程序运行时查看该控件时会自动把当前TataTable的DataRow为修改状态,即使用户什么也没有操作,仅仅是浏览记录,TataTable.GetChanges()也会返回用户浏览过的DataRows,不能真实反映出绑定的数据是否发生变化。
经过进一步的测试,我发现原因在于,在我的代码中,每次调用BindingSource.EndEdit()结束当前编辑的时候,都会导致value这个属性的重新赋值(set被调用)。这样的话,即使我没有改变界 面上的数据,由于重新赋值,被绑定的table就会发生改变。

如何解决这个问题?


  回复  引用  查看    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      

相关链接: