Surface中如何触发DataTemplate中的事件

今年刚开始接触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 Code
public 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 Code
private 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 Code
public 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;
        }

上面的代码中添加的部分我已经加了标记,很大的重点两个字;-)。但是还有缺点就是这几行代码必须每次都要写出,还在想更通用的方法,希望各位达人如果看到了可以帮帮忙。

 

posted @ 2012-07-18 13:13  Frank Qin  阅读(490)  评论(0编辑  收藏  举报