WPF拖动总结

WPF拖动总结

 

这篇博文总结下WPF中的拖动,文章内容主要包括:

1.拖动窗口

2.拖动控件 Using Visual Studio

  2.1thumb控件

  2.2Drag、Drop(不连续,没有中间动画)

  2.3拖动一个控件

  2.4让一个窗口内的所有(指定的)控件可拖动

3.Expression Blend X实现拖动(Best Practice)

小结

1.拖动窗口                        

我们知道,鼠标放在窗口的标题栏上按下就可以拖动窗体。我们要实现在窗口的全部地方或特定地方按下鼠标左键实现拖动。

Winform的做法是,获取鼠标的位置信息,从而设置窗体的位置。

WPF也可以采用Winform类似的方法,但是没有必要,因为有更加单的方法。

复制代码
<Window x:Class="WpfApplicationDrugMove.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="窗体拖动" Height="350" Width="525">

    <Grid Background="Green" MouseLeftButtonDown="Grid_MouseLeftButtonDown">

        <Canvas Height="65" Background="Gray" HorizontalAlignment="Left" Margin="284,110,0,0" Name="canvas1" VerticalAlignment="Top" Width="74" MouseLeftButtonDown="canvas1_MouseLeftButtonDown">

            

        </Canvas>

    </Grid>

</Window>
复制代码

WPF拖动总结

有Grid布局的窗口,里面放置了一个Canvas。
要实现在Grid内按下鼠标左键实现窗体拖动/或是Canvas内实现按下鼠标左键实现窗体拖动,代码如下:

复制代码
private void canvas1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

   base.DragMove();//实现整个窗口的拖动

}



private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

   base.DragMove();

}
复制代码

从上面的代码我们可以看到,DragMove()方法仅用来实现窗体的拖动。

2.拖动控件               

 2.1thumb控件

thumb控件MSDN的描述非常简单:Represents a control that can be dragged by the user.(表示可由用户拖动的控件)。

由DragStarted、DragDelta、DragCompleted着三个事件完成控件的拖动。

给个例子:我们在Canvas中加入如下thumb控件

<Thumb Name="thumb1" Background="Red" Height="50" Width="100" DragDelta="DragDelta" DragStarted="DragStarted" DragCompleted="DragCompleted" Canvas.Left="335" Canvas.Top="121" />  

WPF拖动总结

实现相应的事件,即可完成该控件的拖动工作。

复制代码
private void DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)

{

   Canvas.SetLeft(thumb1,Canvas.GetLeft(thumb1)+e.HorizontalChange);

   Canvas.SetTop(thumb1, Canvas.GetTop(thumb1) + e.VerticalChange);

}



private void DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)

{

   thumb1.Background = Brushes.White;

}



private void DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)

{

   thumb1.Background = Brushes.Red;

}
复制代码

这只是一个简单的示例,我们知道thumb有拇指的意思,代表着很棒的意思。
在2008-08-23在codeproject上发表的WPF Diagram Designer(WPF图形设计器)系列文章(共3篇),被国内很多人Copy过来说是他自己弄的(吐槽:这里省去3K字),其中关于thumb的运用可供参考,thumb可以实现控件的拖动。

2.2 drag、drop(不连续,没有中间动画)

很多控件都有AllowDrop属性:允许放下;和Drop事件。

给出两个例子。

例1:

复制代码
<Grid>

  <Label Name ="label1" Content="TestDrop" Background="Red" Height ="28" HorizontalAlignment="Left" Margin="70,35,0,0"  VerticalAlignment="Top" MouseDown="label1_MouseDown"  />

  <Label Name="label2"  Content="ToHere"  Background="Green" Height="28" HorizontalAlignment="Left" Margin ="342,107,0,0"  VerticalAlignment="Top" AllowDrop ="True" Drop="tagert_drop"   />

</Grid>
复制代码

WPF拖动总结

现在,拖拽label1到label上,把label1的text赋值给label2.实现如下:

复制代码
private void label1_MouseDown(object sender, MouseButtonEventArgs e)

{

    Label lbl = (Label)sender;

    DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);

}

private void tagert_drop(object sender, DragEventArgs e)

{

    ((Label)sender).Content = e.Data.GetData(DataFormats.Text);

}
复制代码

例2:

 界面上有两个Canvas,右面的Canvas里面有一个Rectangle。拖动右面的Rectangle把它拖到左边来,并且保留右边的Rectangle。 

复制代码
<Window x:Class="WpfApplicationDrugMove.Windowdragdrop"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="Windowdragdrop" Height="369" Width="559">

    <Grid>

        <Canvas Background="ForestGreen" Height="282" HorizontalAlignment="Left" Margin="22,20,0,0" Name="canvas1" VerticalAlignment="Top" Width="226" />

        <Canvas Background="ForestGreen" Height="282" HorizontalAlignment="Left" Margin="278,20,0,0" Name="canvas2" VerticalAlignment="Top" Width="232">

            <Rectangle Fill="Yellow" Canvas.Left="35" Canvas.Top="36" Height="100" Name="rectangle1" Stroke="Black" Width="150" />

        </Canvas>

    </Grid>

</Window>
复制代码
复制代码
namespace WpfApplicationDrugMove

{

    /// <summary>

    /// Interaction logic for Windowdragdrop.xaml

    /// </summary>

    public partial class Windowdragdrop : Window

    {

        public Windowdragdrop()

        {

            InitializeComponent();



            canvas1.AllowDrop = true;

            rectangle1.PreviewMouseMove += new MouseEventHandler(rectangle1_PreviewMouseMove);

            canvas1.DragOver += new DragEventHandler(canvas1_DragOver);

            canvas1.Drop += new DragEventHandler(canvas1_Drop);

        }     



        void rectangle1_PreviewMouseMove(object sender, MouseEventArgs e)

        {

            if (e.LeftButton == MouseButtonState.Pressed)

            {

                DataObject data = new DataObject(typeof(Rectangle), rectangle1);

                DragDrop.DoDragDrop(rectangle1, data, DragDropEffects.Copy);

            }

        }



        void canvas1_Drop(object sender, DragEventArgs e)

        {

            IDataObject data = new DataObject();

            data = e.Data;

            if (data.GetDataPresent(typeof(Rectangle)))

            {

                Rectangle rect = new Rectangle();

                rect = data.GetData(typeof(Rectangle)) as Rectangle;

                //canvas2.Children.Remove(rect);

                //canvas1.Children.Add(rect);

                //序列化Control,以深复制Control!!!!

                string rectXaml = XamlWriter.Save(rect);

                StringReader stringReader = new StringReader(rectXaml);

                XmlReader xmlReader = XmlReader.Create(stringReader);

                UIElement clonedChild = (UIElement)XamlReader.Load(xmlReader);

                canvas1.Children.Add(clonedChild);

            }

        }

       



        void canvas1_DragOver(object sender, DragEventArgs e)

        {

            if(!e.Data.GetDataPresent(typeof(Rectangle)))

            {

                e.Effects = DragDropEffects.None;

                e.Handled = true;

            }

            

        }



    }

}
复制代码

效果如下:
WPF拖动总结

 这个也就回答了博客园的一篇博问:WPF拖拽实现

 虽然这个问题被标记为解决,但是其解决的方法过于丑陋,具体请看DebugLZQ本文代码实现。

 2.3拖动一个控件

 实现和thumb一样的效果,不同于drag/drop,拖动的时候控件跟随鼠标移动。

<Canvas x:Name="canvas1" Background="Green">        

  <Canvas  Background="Yellow" Canvas.Left="85" Canvas.Top="51" Height="100" Name="canvas2" Width="105" MouseLeftButtonDown="canvas2_MouseDown"   MouseMove="canvas2_MouseMove" MouseLeftButtonUp="canvas2_MouseLeftButtonUp"></Canvas>

</Canvas>

WPF拖动总结

Canvas中又一个控件(Canvas2),实现canvas2的拖动。

实现canvas2的MouseLeftButtonDown、MouseMove、MouseLeftButtonUp事件。

复制代码
Point oldPoint = new Point();

bool isMove = false;

private void canvas2_MouseMove(object sender, MouseEventArgs e)

{

   if (isMove)

   {

       canvas2.Background = Brushes.White;



       FrameworkElement currEle = sender as FrameworkElement;

       double xPos = e.GetPosition(null).X - oldPoint.X + (double)currEle.GetValue(Canvas.LeftProperty);

       double yPos = e.GetPosition(null).Y - oldPoint.Y + (double)currEle.GetValue(Canvas.TopProperty);

       currEle.SetValue(Canvas.LeftProperty, xPos);

       currEle.SetValue(Canvas.TopProperty, yPos);

                

       oldPoint = e.GetPosition(null);

   }

}



private void canvas2_MouseDown(object sender, MouseButtonEventArgs e)

{

   isMove = true;

   oldPoint = e.GetPosition(null);

}



private void canvas2_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

{

   isMove = false;

   canvas2.Background = Brushes.Yellow;

}
复制代码

 2.4让一个窗口内的所有(指定的)控件可拖动

有2.3的基础,现在我们就可以很方便的实现容器内所有控件拖动了。不仅仅局限于Canvas其实Canvas的绝对定位和其他的容器(如Grid)没多好差别,只不过Canvas使用Left/Top来定位;Grid是用Margin,仅此而已!

1.还是Canvas中的拖动

WPF拖动总结

复制代码
<Window x:Class="WpfApplicationDrugMove.WindowWPFALLControlDragInCanvas"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="WindowWPFALLControlDragInCanvas" Height="418" Width="642">

    <Canvas x:Name="LayoutRoot" Background="Violet">

        <Label Canvas.Left="330" Canvas.Top="151" Content="Label" Height="28" Name="label1" />

        <TextBlock Canvas.Left="437" Canvas.Top="154" Height="23" Name="textBlock1" Text="TextBlock" />

        <Image Canvas.Left="206" Canvas.Top="231" Height="64" Name="image1" Stretch="Fill" Width="73" Source="/WpfApplicationDrugMove;component/1.jpg" />

        <Canvas Canvas.Left="358" Canvas.Top="233" Height="100" Name="canvas1" Width="200"  Background="Red"></Canvas>

        <Button Canvas.Left="227" Canvas.Top="38" Content="Button" Height="23" Name="button1" Width="75" />

        <TextBox Canvas.Left="113" Canvas.Top="125" Height="23" Name="textBox1" Width="120" />

    </Canvas>

</Window>
复制代码
复制代码
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;



namespace WpfApplicationDrugMove

{

    /// <summary>

    /// Interaction logic for WindowWPFALLControlDrag.xaml

    /// </summary>

    public partial class WindowWPFALLControlDragInCanvas:Window

    {

        public WindowWPFALLControlDragInCanvas()

        {

            InitializeComponent();



            foreach (UIElement uiEle in LayoutRoot.Children)

            {

                //WPF设计上的问题,Button.Clicked事件Supress掉了Mouse.MouseLeftButtonDown附加事件等.

                //不加这个Button、TextBox等无法拖动

                if (uiEle is Button||uiEle is TextBox)

                {

                    uiEle.AddHandler(Button.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Element_MouseLeftButtonDown), true);

                    uiEle.AddHandler(Button.MouseMoveEvent, new MouseEventHandler(Element_MouseMove),true);

                    uiEle.AddHandler(Button.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Element_MouseLeftButtonUp), true);

                    continue;

                }

                //

                uiEle.MouseMove += new MouseEventHandler(Element_MouseMove);

                uiEle.MouseLeftButtonDown += new MouseButtonEventHandler(Element_MouseLeftButtonDown);

                uiEle.MouseLeftButtonUp += new MouseButtonEventHandler(Element_MouseLeftButtonUp);                

            }         

        }



        bool isDragDropInEffect = false;

        Point pos = new Point();



        void Element_MouseMove(object sender, MouseEventArgs e)

        {

            if (isDragDropInEffect)

            {

                FrameworkElement currEle = sender as FrameworkElement;

                double xPos = e.GetPosition(null).X - pos.X + (double)currEle.GetValue(Canvas.LeftProperty);

                double yPos = e.GetPosition(null).Y - pos.Y + (double)currEle.GetValue(Canvas.TopProperty);

                currEle.SetValue(Canvas.LeftProperty, xPos);

                currEle.SetValue(Canvas.TopProperty, yPos);

                pos = e.GetPosition(null);

            }

        } 



        void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {

            

            FrameworkElement fEle = sender as FrameworkElement;

            isDragDropInEffect = true;

            pos = e.GetPosition(null);

            fEle.CaptureMouse();

            fEle.Cursor = Cursors.Hand;

        }



        void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

        {

            if (isDragDropInEffect)

            {

                FrameworkElement ele = sender as FrameworkElement;

                isDragDropInEffect = false;

                ele.ReleaseMouseCapture();

            }

        } 



    }

}
复制代码

注意:需要用AddHandler添加Button.MouseLeftButtonDown等事件,不然无法触发,因为Button.Clicked事件Supress掉了MouseLeftButtonDown。
这样页面上的所有控件就可以随意拖动了。

今天在CodeProject上看到了这篇文章:WPF - Catch Events Even if they are Already Handled,说的是一个事情。

2.Canvas换成Grid。Grid中所有控件可拖动。

复制代码
<Window x:Class="WpfApplicationDrugMove.WindowWPFALLControlDragMoveInGrid"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="WindowWPFALLControlDragMoveInGrid" Height="382" Width="552">

    <Grid x:Name="LayoutRoot" Background="GreenYellow">

        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="60,42,0,0" Name="button1" VerticalAlignment="Top" Width="75" />

        <Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="305,89,0,0" Name="label1" VerticalAlignment="Top" />

        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="204,45,0,0" Name="button2" VerticalAlignment="Top" Width="75" />

        <TextBlock Height="23" HorizontalAlignment="Left" Margin="363,42,0,0" Name="textBlock1" Text="TextBlock" VerticalAlignment="Top" />

        <TextBox Height="23" HorizontalAlignment="Left" Margin="60,140,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />

        <Image Height="56" HorizontalAlignment="Left" Margin="173,229,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="62" Source="/WpfApplicationDrugMove;component/1.jpg" />

        <Image Height="150" HorizontalAlignment="Left" Margin="291,159,0,0" Name="image2" Stretch="Fill" VerticalAlignment="Top" Width="177" Source="/WpfApplicationDrugMove;component/2.gif" />

    </Grid>

</Window>
复制代码

WPF拖动总结

复制代码
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Shapes;



namespace WpfApplicationDrugMove

{

    /// <summary>

    /// Interaction logic for WindowWPFALLControlDragMoveInGrid.xaml

    /// </summary>

    public partial class WindowWPFALLControlDragMoveInGrid : Window

    {

        public WindowWPFALLControlDragMoveInGrid()

        {

            InitializeComponent();



            foreach (UIElement uiEle in LayoutRoot.Children)

            {

                if (uiEle is Button || uiEle is TextBox)

                {

                    uiEle.AddHandler(Button.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Element_MouseLeftButtonDown), true);

                    uiEle.AddHandler(Button.MouseMoveEvent, new MouseEventHandler(Element_MouseMove), true);

                    uiEle.AddHandler(Button.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Element_MouseLeftButtonUp), true);

                    continue;

                }

                uiEle.MouseMove += new MouseEventHandler(Element_MouseMove);

                uiEle.MouseLeftButtonDown += new MouseButtonEventHandler(Element_MouseLeftButtonDown);

                uiEle.MouseLeftButtonUp += new MouseButtonEventHandler(Element_MouseLeftButtonUp);

            } 

        }



        bool isDragDropInEffect = false;

        Point pos = new Point();



        void Element_MouseMove(object sender, MouseEventArgs e)

        {

            if (isDragDropInEffect)

            {

                FrameworkElement currEle = sender as FrameworkElement;

                double xPos = e.GetPosition(null).X - pos.X + currEle.Margin.Left;

                double yPos = e.GetPosition(null).Y - pos.Y + currEle.Margin.Top;

                currEle.Margin = new Thickness(xPos, yPos, 0, 0);

                pos = e.GetPosition(null);

            }

        }





        void Element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

        {



            FrameworkElement fEle = sender as FrameworkElement;

            isDragDropInEffect = true;

            pos = e.GetPosition(null);

            fEle.CaptureMouse();

            fEle.Cursor = Cursors.Hand;

        }



        void Element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

        {

            if (isDragDropInEffect)

            {

                FrameworkElement ele = sender as FrameworkElement;

                isDragDropInEffect = false;

                ele.ReleaseMouseCapture();

            }

        } 



    }

}
复制代码

效果如下:
WPF拖动总结

 Grid界面中的所有控件可随意拖动。

3.使用Expression Blend实现拖动(Best Practice)

使用如下的一个Behavior:MouseDragElementBehavior

WPF拖动总结

实现方法非常简单,let's say 我们有个Rectangle,无论在什么容器中,我们要实现其拖动。

直接把这个MouseDragElementBehavior 拖动到Rectangle中即可。

XAML如下:

复制代码
<Window

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"       

        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 

        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"  x:Class="WPFDragMoveBlend.MainWindow"       

        Title="MainWindow" Height="350" Width="525">

    <Grid>

        <Rectangle Fill="Red" Stroke="Black" Margin="145,82,164,50" Width="200" Height="180" >            

            <i:Interaction.Behaviors>

                <ei:MouseDragElementBehavior/>

            </i:Interaction.Behaviors>

        </Rectangle>

    </Grid>

</Window>
复制代码

(如您所见,DebugLZQ使用的是 Expression Blend 4)。
程序运行正常,Rectangle可随意拖动如下:

WPF拖动总结WPF拖动总结

使用Blend借助Behaviors不需要额外的C#代码,最为简洁。

其他的一些Behaviors也非常有用,

如播放MP3:

复制代码
<Window

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"       

        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 

        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"  x:Class="WPFDragMoveBlend.MainWindow"       

        Title="MainWindow" Height="350" Width="525">

    <Grid>

        <Rectangle Fill="Red" Stroke="Black" Margin="145,82,164,50" Width="200" Height="180" >

            <i:Interaction.Triggers>

                <i:EventTrigger EventName="MouseLeftButtonDown">

                    <ei:PlaySoundAction Source="C:\Users\Public\Music\Sample Music\Kalimba.mp3"/>

                </i:EventTrigger>

            </i:Interaction.Triggers>

                      

            <i:Interaction.Behaviors>

                <ei:MouseDragElementBehavior/>

            </i:Interaction.Behaviors>

        </Rectangle>

    </Grid>

</Window>
复制代码

程序可正常运行。

还有如CallMethodAction,ControlStoryboardAction,及MVVM中使用较多的InvokeCommandAction等。

 小结一下:

关于2.2例2中控件的序列化、反序列化! 参考:WPF控件深拷贝:序列化/反序列化

关于Button.MouseLeftButtonDown用C#代码注册的话需要用AddHandler添加,直接添加会被Button.Clicked阻止! 另一种情况是:我们如何捕获一个路由事件,即使这个路由事件已经被标记为e.handled=true。这个很重要!!!参考:WPF捕获事件即使这个事件被标记为Handled  。拖动不局限于Canvas. 

所有方法中,Blend实现最为Clearn.关于Blend 4的快捷键,请参考:A Complete Guide to Expression Blend 4 Shortcut Keys

 

老鸟绕过,轻拍~

Wish it helps. 

 
 

你可能感兴趣的:(WPF)