Sliverlight Tip -- 让DataGrid支持Scroll事件

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处直接跳出异常, Sliverlight Tip -- 让DataGrid支持Scroll事件_第1张图片
原来出于安全性的考虑,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都可能会碰到类似的需求,并给出类似的解决方法.
 

你可能感兴趣的:(datagrid)