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处直接跳出异常,
原来出于安全性的考虑,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都可能会碰到类似的需求,并给出类似的解决方法.
 
原文地址:https://www.cnblogs.com/bloodish/p/1990386.html