前几天遇到个touch事件的坑,记录下来以增强理解。
具体是 想把一个listview嵌套到另一个listview,这时候如果list view(子listview)的内容过多超过容器高度,它是不会出现滚动条压缩内容区域的,反而会将滚动区域转移到外面的list view(父listview),这个无可争议,但这个问题开始没留意,为待会的坑埋下伏笔。
因为 然后就是设置鼠标滚轮。
首先我使用了MouseWheel事件,奇怪的是它明明是个路由事件,然而listview似乎做了处理,没有冒泡到父级。
于是我改写PreviewMouseWheel事件,吧从父级传过来的时间再冒泡回去。
在子类的listview中,我在滚动事件里写了个向上传递的触发事件:
private void BodyList_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = UIElement.MouseWheelEvent;
eventArg.Source = sender;
BodyList.RaiseEvent(eventArg);
}
这样我就能实现当鼠标焦点在子listview时,能触发父级的滚动事件
这时候为了触摸屏操作,我如是写了touchdown .touchmove touchup 三个事件,可是,当手势在子listview做滑动操作的时候,父级不滑动。
即使我完全伪造一个source为上级的touch事件,父级仍然岿然不动,如图:
private void UIElement_OnTouchMove(object sender, TouchEventArgs e)
{
var eventArg = new TouchEventArgs(e.TouchDevice,e.Timestamp);
eventArg.RoutedEvent = UIElement.TouchMoveEvent;
eventArg.Source = HAHAListBox;
(sender as UIElement).TryFindParent<ListBox>().RaiseEvent(e);
e.Handled = true;
}
于是我开始找原因,touch事件同样是路由事件,于MouseWheel不同的是,我可以在父listview触发它。
那么是什么原因呢,通过可视化树工具,可以发现,在嵌套的listview中实际上嵌套了两个scrollview, 当touch事件在 子listview中触发时,实际上事件被子级中的scrollview吸收了。但是为何伪造后仍然无法反应?
那是由于touch事件是一个特殊的事件,至少有别于滚轮事件,控件需要对手势在触摸屏上的坐标做出响应,两个相互嵌套的滑动控件,无法对同一手势坐标做出反应,否则他们之间的相对位置就会发生改变,也就是说两个scrollview不能同时依赖一个手势源,只有最上层的scrollview才能响应目标源。
不知道微软为何这么设定,至少我的理解是这样的
由于开始的设置,子listview中的scrollview永远不会有效果。因此容易被忽略
所以我们只要重写底层listview的template就好了,把原来包裹ItemsPresenter的Scrollview控件给删掉
touch事件就能被父一级的listview触发了!
希望这个绕坑的经历能帮助到大家,谢谢!