今年刚开始接触Surface的时候就遇到过这个问题,当时想了很多办法,但是一直没有成功,看到Silverlight里面可以直接新建事件实在是非常的羡慕,后来还尝试了CommandBinding的方法,虽然可以用了但还是感觉很麻烦。
今天无意中在MSDN看到有段代码里在DataTemplate中添加了事件,就又实验了一下,终于找到了原因。
先创建一个项目便于说明:
View Code<s:SurfaceWindow x:Class="ScatterViewItemScale.SurfaceWindow1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="http://schemas.microsoft.com/surface/2008" Title="ScatterViewItemScale" > <s:SurfaceWindow.Resources> <DataTemplate x:Key="ScatterViewItemDataTemplate" > <Border BorderThickness="2" BorderBrush="White" Margin="10"> <StackPanel Background="{DynamicResource {x:Static s:SurfaceColors.Accent3BrushKey}}"> <Label Content="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"/> <s:SurfaceButton Content="{Binding CanDrag}" Click="SurfaceButton_Click" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18"/> </StackPanel> </Border> </DataTemplate> </s:SurfaceWindow.Resources> <Grid> <s:ScatterView Name="DragSource" ItemTemplate="{StaticResource ScatterViewItemDataTemplate}" ItemsSource="{Binding Path=SourceItems}" PreviewMouseDown="DragSourcePreviewInputDeviceDown" PreviewTouchDown="DragSourcePreviewInputDeviceDown" s:SurfaceDragDrop.DragCanceled="DragCanceled" s:SurfaceDragDrop.DragCompleted="DragCompleted"/> </Grid> </s:SurfaceWindow>
具体后台代码我就偷个懒不贴了,有朋友需要的话可以到这里参考一下:http://msdn.microsoft.com/en-us/library/ff727736
private void SurfaceButton_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine(sender);
}
数据类:
View Codepublic class DataItem { private string name; private bool canDrag; public string Name { get { return name; } } public bool CanDrag { get { return canDrag; } } public object DraggedElement { get; set; } public DataItem(string name, bool canDrag) { this.name = name; this.canDrag = canDrag; } }
现在开始说重点,在对代码完全不做修改的情况下,点击每个ScatterViewItem中的Button,只有canDrag为false的item的事件被触发了。原因是在ScatterView的PreviewTouchDown事件中有如下定义:
// If the data has not been specified as draggable,
// or the ScatterViewItem cannot move, return.
if (data == null || !data.CanDrag || !draggedElement.CanMove)
{
return;
}
这里的return使得touch跳过了对ScatterViewItem的操作,进入了VisualTree的下一级,所以ButtonClick就可以被触发了。
所以概念上的解决办法就是当touchpoint的originalsource是需要触发事件的控件时,PreviewTouchDown返回就好了。
可以写成:
View Codeprivate void DragSourcePreviewInputDeviceDown(object sender, InputEventArgs e) { FrameworkElement findSource = e.OriginalSource as FrameworkElement; ScatterViewItem draggedElement = null; /////////////////////////////////////////////重点 SurfaceButton sb = null; sb = FindContainer<SurfaceButton>(sb, findSource); if (sb != null) { return; } //////////////////////////////////////////////重点 // Find the ScatterViewItem object that is being touched. while (draggedElement == null && findSource != null) { if ((draggedElement = findSource as ScatterViewItem) == null) { findSource = VisualTreeHelper.GetParent(findSource) as FrameworkElement; } } if (draggedElement == null) { return; } DataItem data = draggedElement.Content as DataItem; // If the data has not been specified as draggable, // or the ScatterViewItem cannot move, return. if (data == null || !data.CanDrag || !draggedElement.CanMove) { return; } // Set the dragged element. This is needed in case the drag operation is canceled. data.DraggedElement = draggedElement; // Create the cursor visual. ContentControl cursorVisual = new ContentControl() { Content = draggedElement.DataContext, Style = FindResource("CursorStyle") as Style }; // Create a list of input devices, // and add the device passed to this event handler. List<InputDevice> devices = new List<InputDevice>(); devices.Add(e.Device); // If there are touch devices captured within the element, // add them to the list of input devices. foreach (InputDevice device in draggedElement.TouchesCapturedWithin) { if (device != e.Device) { devices.Add(device); } } // Get the drag source object. ItemsControl dragSource = ItemsControl.ItemsControlFromItemContainer(draggedElement); // Start the drag-and-drop operation. SurfaceDragCursor cursor = SurfaceDragDrop.BeginDragDrop( // The ScatterView object that the cursor is dragged out from. dragSource, // The ScatterViewItem object that is dragged from the drag source. draggedElement, // The visual element of the cursor. cursorVisual, // The data attached with the cursor. draggedElement.DataContext, // The input devices that start dragging the cursor. devices, // The allowed drag-and-drop effects of the operation. DragDropEffects.Move); // If the cursor was created, the drag-and-drop operation was successfully started. if (cursor != null) { // Hide the ScatterViewItem. draggedElement.Visibility = Visibility.Hidden; // This event has been handled. e.Handled = true; } }
下面是FindContainer的代码,用来返回指定类型的控件(找不到时返回null);
View Codepublic T FindContainer<T>(T containerElement, FrameworkElement findsource) where T : DependencyObject { while (containerElement == null && findsource != null) { if ((containerElement = findsource as T) == null) { findsource = VisualTreeHelper.GetParent(findsource) as FrameworkElement; } } return containerElement; }
上面的代码中添加的部分我已经加了标记,很大的重点两个字;-)。但是还有缺点就是这几行代码必须每次都要写出,还在想更通用的方法,希望各位达人如果看到了可以帮帮忙。
转载于:https://www.cnblogs.com/icyfantasy/archive/2012/07/18/2597215.html