Silverlight4.0的DataGrid标准控件中只开放了极少的控件事件提供给开发者使用,像DoubleClick,Scroll之类比较常见的事件都没有做支持.
对于DoubleClick的处理,可以通过Behavior进行变通,这不是本次要讲解的技术内容.
这次要讲解的是如何注册Scroll事件到DataGrid的垂直或水平滚动条.
先通过Refector查看了DataGrid的实现,确实包含VerticalScrollBar和 HorizontalScrollBar这两个属性,可惜声明为internal,只能在程序集内部被调用.
因此下意识的是用反射机制拿到Scroll对象,然后注册事件,于是先尝试如下代码:
///
<summary>
///
尝试使用反射获取VerticalScrollbar属性以得到Scrollbar对象
///
</summary>
private
void
TryUseReflectProperty()
{
var propInfo
=
typeof
(DataGrid).GetProperty(
"
VerticalScrollBar
"
,
BindingFlags.Instance
|
BindingFlags.NonPublic);
if
(propInfo
!=
null
)
{
ScrollBar scroll
=
propInfo.GetValue(dataGrid,
null
)
as
ScrollBar;
}
}
可惜执行到GetValue处直接跳出异常,
原来出于安全性的考虑,Silverlight程序是无法通过反射来访问到私有的属性,成员,方法,事件,.etc
具体说明可以参考MSDN:
http://msdn.microsoft.com/en-us/library/stfy7tfc(VS.95).aspx
其实在没有找到MS的官方说明之前,我又尝试了一些其他跟Reflection有关的方法,
比如尝试先获取MethodInfo再尝试Invoke(该做法跟Property的尝试异曲同工,没有本质改进),
后来又尝试通过MethodInfo创建另一个委托,再通过DynamicInvoke去做,可惜仍然是徒劳无功.
无奈之下继续Google,发现有一种解决方案是在DataGrid的Style Template里直接绑定Scroll事件,
通过Blend打开看确实能够找到DataGrid对应的VerticalScrollBar和HorizontalScrollBar.
这样的做法给了一些启发,或者说是灵感,既然如此,直接操作VisualTree应该能够获取到ScrollBar,
于是行动之...
///
<summary>
///
通过VisualTreeHelper,递归找到ScrollBar
///
</summary>
private
ScrollBar GetScrollbar(DependencyObject dpObj,
string
name)
{
for
(var i
=
0
; i
<
VisualTreeHelper.GetChildrenCount(dpObj); i
++
)
{
var child
=
VisualTreeHelper.GetChild(dpObj, i);
ScrollBar scrollBar
=
child
as
ScrollBar;
if
(scrollBar
!=
null
&&
scrollBar.Name
==
name)
{
return
scrollBar;
}
else
{
scrollBar
=
GetScrollbar(child, name);
if
(scrollBar
!=
null
)
{
return
scrollBar;
}
}
}
return
null
;
}
结果发现果然可以,那么该在何时去调用GetScrollBar并注册Scroll事件呢?
个人推荐是在LayoutUpdated事件里做一次处理,具体代码如下:
ScrollBar scroll;
///
<summary>
///
尝试通过VistualTree来找到Scrollbar对象
///
</summary>
private
void
TryUseVisualTree()
{
EventHandler h
=
null
;
dataGrid.LayoutUpdated
+=
h
=
delegate
{
if
(scroll
==
null
)
{
scroll
=
GetScrollbar(dataGrid,
"
VerticalScrollBar
"
);
if
(scroll
!=
null
)
{
//
一旦找到注销LayoutUpdated事件
dataGrid.LayoutUpdated
-=
h;
//
注册Scroll事件
scroll.Scroll
+=
delegate
{ Debug.WriteLine(
"
scrolling...
"
); };
}
}
};
}
大功告成,这样我们就能够在代码里处理Scroll事件了.
这个例子很好扩展,对于DataGrid内含的各种UIElement如果直接访问不到,都可以通过查找VistualTree的方法找到,并执行相应的处理.
而且不仅仅限于DataGrid,任何其他的UIElement都可能会碰到类似的需求,并给出类似的解决方法.