1. 程式人生 > >wpf,後臺觸發按鈕點擊以及拖動

wpf,後臺觸發按鈕點擊以及拖動

archive posit 多好 自己 article fda continue width partial

觸發按鈕Click                  
  MouseButtonEventArgs args = new MouseButtonEventArgs(Mouse.PrimaryDevice,
                           0, MouseButton.Left);
                    args.RoutedEvent = Button.ClickEvent;
                    btnOkCommand.RaiseEvent(args); 

觸發按鈕綁定的Command
需要添加UIAutomationProvider 引用
                    ButtonAutomationPeer bam 
= new ButtonAutomationPeer(btnOkCommand); IInvokeProvider iip = bam.GetPattern(PatternInterface.Invoke) as IInvokeProvider; iip.Invoke();

關於拖動

  //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);

WPF利用代碼觸發按鈕點擊操作

WPF拖動總結

這篇博文總結下WPF中的拖動,文章內容主要包括:

1.拖動窗口

2.拖動控件 Using Visual Studio

  2.1thumb控件

  2.2Drag、Drop(不連續,沒有中間動畫)

  2.3拖動一個控件

  2.4讓一個窗口內的所有(指定的)控件可拖動

3.Expression Blend X實現拖動(Best Practice)

Update: Move and resize controls on a form at runtime (with drag and drop)

小結

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>
技術分享

技術分享

有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" />  

技術分享

實現相應的事件,即可完成該控件的拖動工作。

技術分享
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有拇指的意思,代表著很棒的意思。
sukram在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>

技術分享

現在,拖拽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拖拽實現

雖然這個問題被標記為解決,但是其解決的方法過於醜陋,具體請看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>

技術分享

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中的拖動

技術分享

技術分享
<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>
技術分享

技術分享

技術分享
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();
            }
        } 

    }
}
技術分享

效果如下:
技術分享

Grid界面中的所有控件可隨意拖動。

3.使用Expression Blend實現拖動(Best Practice)

使用如下的一個Behavior:MouseDragElementBehavior

技術分享

實現方法非常簡單,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可隨意拖動如下:

技術分享技術分享

使用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

Update1(2014-01-14):

It provide a Winform demo, but It obviously also works for WPF projects.

技術分享

Move and resize controls on a form at runtime (with drag and drop)

Posted on CodeProject, By zomorrod.company, 13 Jan 2014

wpf,後臺觸發按鈕點擊以及拖動