WPF विंडो को ड्रैग करने योग्य बनाएं, चाहे कोई भी तत्व क्लिक किया गया हो


111

मेरा प्रश्न 2 गुना है, और मैं उम्मीद कर रहा हूं कि WinForms (जो क्रिस्टोफ गेर्स द्वारा प्रदान किया गया है, इससे पहले कि मैंने यह स्पष्टीकरण दिया है) से मानक समाधानों के बजाय WPF द्वारा प्रदान किए गए दोनों के लिए आसान समाधान हैं ।

पहले, क्या माउस-क्लिक + ड्रैग इवेंट्स को कैप्चर और प्रोसेसिंग के बिना विंडो को ड्रैग करने योग्य बनाने का एक तरीका है? मेरा मतलब है कि विंडो टाइटल बार द्वारा ड्रैग करने योग्य है, लेकिन अगर मैंने एक विंडो सेट की है, जिसमें एक भी नहीं है और फिर भी इसे खींचने में सक्षम होना है, तो क्या इवेंट को किसी भी तरह से री-डायरेक्ट करना है जो टाइटल बार ड्रैगिंग को हैंडल करता है ?

दूसरा, क्या विंडो में सभी तत्वों के लिए ईवेंट हैंडलर लगाने का एक तरीका है? के रूप में, खिड़की को खींचें कोई फर्क नहीं पड़ता कि उपयोगकर्ता किस तत्व पर क्लिक करता है + drags। जाहिर है कि हर एक तत्व को मैन्युअल रूप से हैंडलर को जोड़े बिना। बस एक बार ऐसा कहीं?

जवाबों:


284

ज़रूर, MouseDownअपने निम्नलिखित घटना को लागू करेंWindow

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ChangedButton == MouseButton.Left)
        this.DragMove();
}

यह उपयोगकर्ताओं को किसी भी नियंत्रण पर क्लिक करने / खींचने पर विंडो को खींचने की अनुमति देगा, नियंत्रण के लिए EXCEPT जो माउसडाउन ईवेंट को खाते हैं e.Handled = true)

आप PreviewMouseDownइसके बजाय उपयोग कर सकते हैं MouseDown, लेकिन ड्रैग इवेंट ईवेंट को खा Clickजाता है, इसलिए आपकी विंडो बाईं माउस क्लिक घटनाओं पर प्रतिक्रिया करना बंद कर देती है। यदि आप वास्तव में किसी भी नियंत्रण से फ़ॉर्म को क्लिक करने और खींचने में सक्षम होना चाहते हैं, तो आप संभवतः उपयोग कर सकते हैं PreviewMouseDown, ड्रैग ऑपरेशन शुरू करने के लिए टाइमर शुरू कर सकते हैं, और यदि MouseUpएक्स मिलीसेकंड के भीतर घटना होती है तो ऑपरेशन रद्द कर सकते हैं ।


+1। बहुत बेहतर है कि विंडो मैनेजर स्थिति को याद करके और खिड़की को घुमाकर उसे फैंकने के बजाय मूव को हैंडल करे। (उत्तरार्द्ध विधि में भी कुछ धार के मामलों में गलत होने की प्रवृत्ति है, वैसे भी)
जॉय

केवल MouseLeftButtonDown.cs में जाँच करने के बजाय, ईवेंट क्यों सेट करें ?

1
@ ड्रोन आप शायद इसके बजाय उस घटना का उपयोग कर सकते हैं, लेकिन सुनिश्चित करें कि आपने इसे पहले परीक्षण किया है क्योंकि MouseLeftButtonDownएक प्रत्यक्ष रूटिंग रणनीति है जबकि MouseDownएक बबलिंग रूटिंग रणनीति है। की टिप्पणी अनुभाग देखें MouseLeftButtonDown के लिए MSDN पेज अधिक जानकारी के लिए, और कुछ अतिरिक्त बातों के लिए आप का उपयोग करने जा रहे हैं, तो के बारे में पता होना करने के लिए MouseLeftButtonDownखत्म हो गया MouseDown
राहेल

@ राचेल हाँ मैं इसका उपयोग कर रहा हूं और यह काम करता है, लेकिन स्पष्टीकरण के लिए धन्यवाद!

2
@Rahul एक UserControl को खींचना बहुत कठिन है ... आपको इसे कैनवास की तरह एक पैरेंट पैनल में रखने की आवश्यकता होगी और उपयोगकर्ता द्वारा माउस को ले जाने पर X / Y (या Canvas.Top और Canvas.Left) गुणों को मैन्युअल रूप से सेट किया जाएगा। मैंने पिछली बार जब मैंने माउस इवेंट्स का उपयोग किया था, तो OnMouseDown कैप्चर पोजिशन और रजिस्टर मूव इवेंट, OnMouseMove परिवर्तन X / Y, और OnMouseUp मूव ईवेंट हटा दें। यह इसका मूल विचार है :)
राहेल

9

अगर wpf फॉर्म को ड्रैग करने योग्य होने की कोई जरूरत नहीं है, जहाँ पर क्लिक किया गया आसान काम है, तो किसी प्रतिनिधि को ड्रैगवॉव () मेथड को ऑन करने के लिए विंडो ऑनलोड इवेंट या ग्रिड लोड इवेंट का उपयोग करना होगा।

private void Grid_Loaded(object sender, RoutedEventArgs 
{
      this.MouseDown += delegate{DragMove();};
}

2
मैंने इसे कंस्ट्रक्टर में जोड़ा। एक आकर्षण काम करता है।
जो जॉनसन

1
यदि आप फ़ॉर्म पर कहीं भी राइट-क्लिक करते हैं, तो यह अपवाद को फेंक देगा, क्योंकि DragMoveप्राथमिक माउस बटन के डाउन होने पर ही कॉल किया जा सकता है।
स्टेपेपैन बकरक

4

कभी-कभी, हमारे पास पहुंच नहीं होती है Window, उदाहरण के लिए यदि हम उपयोग कर रहे हैं DevExpress, तो वह सब उपलब्ध है एक UIElement

चरण 1: संलग्न संपत्ति जोड़ें

इसका समाधान यह है:

  1. MouseMoveघटनाओं में हुक ;
  2. जब तक हम पहले माता-पिता को Windowनहीं पा लेते तब तक दृश्य वृक्ष को खोजें ;
  3. .DragMove()हमारे नए खोज पर कॉल करें Window

कोड:

using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace DXApplication1.AttachedProperty
{
    public class EnableDragHelper
    {
        public static readonly DependencyProperty EnableDragProperty = DependencyProperty.RegisterAttached(
            "EnableDrag",
            typeof (bool),
            typeof (EnableDragHelper),
            new PropertyMetadata(default(bool), OnLoaded));

        private static void OnLoaded(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var uiElement = dependencyObject as UIElement;
            if (uiElement == null || (dependencyPropertyChangedEventArgs.NewValue is bool) == false)
            {
                return;
            }
            if ((bool)dependencyPropertyChangedEventArgs.NewValue  == true)
            {
                uiElement.MouseMove += UIElementOnMouseMove;
            }
            else
            {
                uiElement.MouseMove -= UIElementOnMouseMove;
            }

        }

        private static void UIElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
        {
            var uiElement = sender as UIElement;
            if (uiElement != null)
            {
                if (mouseEventArgs.LeftButton == MouseButtonState.Pressed)
                {
                    DependencyObject parent = uiElement;
                    int avoidInfiniteLoop = 0;
                    // Search up the visual tree to find the first parent window.
                    while ((parent is Window) == false)
                    {
                        parent = VisualTreeHelper.GetParent(parent);
                        avoidInfiniteLoop++;
                        if (avoidInfiniteLoop == 1000)
                        {
                            // Something is wrong - we could not find the parent window.
                            return;
                        }
                    }
                    var window = parent as Window;
                    window.DragMove();
                }
            }
        }

        public static void SetEnableDrag(DependencyObject element, bool value)
        {
            element.SetValue(EnableDragProperty, value);
        }

        public static bool GetEnableDrag(DependencyObject element)
        {
            return (bool)element.GetValue(EnableDragProperty);
        }
    }
}

चरण 2: किसी भी तत्व से जुड़ी हुई संपत्ति को जोड़ें ताकि वह खिड़की को खींच सके

यदि हम इस संलग्न गुण को जोड़ते हैं, तो उपयोगकर्ता एक विशिष्ट तत्व पर क्लिक करके पूरी विंडो को खींच सकता है:

<Border local:EnableDragHelper.EnableDrag="True">
    <TextBlock Text="Click me to drag this entire window"/>
</Border>

परिशिष्ट A: वैकल्पिक उन्नत उदाहरण

DevExpress से इस उदाहरण में , हम अपने स्वयं के ग्रे आयत के साथ डॉकिंग विंडो के शीर्षक बार को बदलते हैं, फिर सुनिश्चित करें कि यदि उपयोगकर्ता क्लिक और ड्रैग ने ग्रे आयत कहा, तो विंडो सामान्य रूप से खींचेंगी:

<dx:DXWindow x:Class="DXApplication1.MainWindow" Title="MainWindow" Height="464" Width="765" 
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" 
    xmlns:local="clr-namespace:DXApplication1.AttachedProperty"
    xmlns:dxdove="http://schemas.devexpress.com/winfx/2008/xaml/docking/visualelements"
    xmlns:themeKeys="http://schemas.devexpress.com/winfx/2008/xaml/docking/themekeys">

    <dxdo:DockLayoutManager FloatingMode="Desktop">
        <dxdo:DockLayoutManager.FloatGroups>
            <dxdo:FloatGroup FloatLocation="0, 0" FloatSize="179,204" MaxHeight="300" MaxWidth="400" 
                             local:TopmostFloatingGroupHelper.IsTopmostFloatingGroup="True"                             
                             >
                <dxdo:LayoutPanel ShowBorder="True" ShowMaximizeButton="False" ShowCaption="False" ShowCaptionImage="True" 
                                  ShowControlBox="True" ShowExpandButton="True" ShowInDocumentSelector="True" Caption="TradePad General" 
                                  AllowDock="False" AllowHide="False" AllowDrag="True" AllowClose="False"
                                  >
                    <Grid Margin="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" MinHeight="15" Background="#FF515151" Margin="0 0 0 0"
                                                                  local:EnableDragHelper.EnableDrag="True">
                            <TextBlock Margin="4" Text="General" FontWeight="Bold"/>
                        </Border>
                        <TextBlock Margin="5" Grid.Row="1" Text="Hello, world!" />
                    </Grid>
                </dxdo:LayoutPanel>
            </dxdo:FloatGroup>
        </dxdo:DockLayoutManager.FloatGroups>
    </dxdo:DockLayoutManager>
</dx:DXWindow>

अस्वीकरण: मैं DevExpress से संबद्ध नहीं हूं । यह तकनीक किसी भी उपयोगकर्ता तत्व के साथ काम करेगी, जिसमें मानक WPF या टेलरिक (एक और अच्छा WPF लाइब्रेरी प्रदाता) शामिल है।


1
जैसा मुझे चाहिए था यह बिल्कुल वैसा ही है। IMHO पीछे के सभी WPF कोड को संलग्न व्यवहार के रूप में लिखा जाना चाहिए।
fjch1997

3
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
    this.DragMove();
}

कुछ मामलों में एक अपवाद को फेंक रहा है (अर्थात यदि विंडो पर आपकी क्लिक करने योग्य छवि भी है, जब क्लिक किया गया संदेश बॉक्स खोलता है। जब आप संदेश बॉक्स से बाहर निकलते हैं तो आपको त्रुटि मिलेगी) का उपयोग करना सुरक्षित है

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
            this.DragMove();
}

इसलिए आप सुनिश्चित हैं कि बाएं बटन को उसी क्षण दबाया गया है।


मैं विशेष रूप से इवेंट आर्ग से जुड़े बटन का उपयोग e.LeftButtonकरने के बजाय उपयोग कर रहा हूं Mouse.LeftButton, भले ही यह कभी भी मायने नहीं रखेगा।
Fls'Zen

2

केवल टाइटल बार ही नहीं, फॉर्म पर कहीं भी क्लिक करके फॉर्म को ड्रैग और ड्रॉप करना संभव है। यह आसान है यदि आपके पास एक सीमाहीन रूप है।

कोडप्रोजेक्ट पर यह लेख इसे लागू करने के लिए एक संभावित समाधान प्रदर्शित करता है:

http://www.codeproject.com/KB/cs/DraggableForm.aspx

मूल रूप से फॉर्म प्रकार का एक वंशज बनाया जाता है जिसमें माउस को नीचे, ऊपर और आगे की घटनाओं को संभाला जाता है।

  • माउस नीचे: स्थिति याद रखें
  • माउस चाल: नए स्थान को संचित करें
  • माउस अप करें: नए स्थान पर स्थिति फ़ॉर्म

और यहाँ एक वीडियो ट्यूटोरियल में बताया गया एक समान समाधान है:

http://www.youtube.com/watch?v=tJlY9aX73Vs

जब उपयोगकर्ता उक्त रूप में नियंत्रण पर क्लिक करता है, तो मैं फ़ॉर्म को खींचने की अनुमति नहीं दूंगा। जब वे अलग-अलग नियंत्रणों पर क्लिक करते हैं तो उपयोगकर्ता अलग-अलग परिणाम देते हैं। जब मेरा फॉर्म अचानक बढ़ना शुरू हो जाता है क्योंकि मैंने एक सूची बॉक्स, बटन, लेबल ... आदि पर क्लिक किया है। यह भ्रामक होगा।


यकीन है कि यह किसी भी नियंत्रण पर क्लिक करके नहीं चलेगा, लेकिन अगर आपने क्लिक किया और घसीटा, तो आप फॉर्म को स्थानांतरित करने की उम्मीद नहीं करेंगे। मेरा मतलब है कि आप बटन या लिस्टबॉक्स की उम्मीद नहीं करेंगे, उदाहरण के लिए अगर आप इसे खींचते हैं, तो आप इसे खींच सकते हैं। फॉर्म की गति एक स्वाभाविक अपेक्षा है यदि आपने फॉर्म में एक बटन को क्लिक करने और खींचने की कोशिश की, तो मुझे लगता है।
एलेक्स के

लगता है, यह सिर्फ व्यक्तिगत स्वाद है। किसी भी तरह .... नियंत्रण को एक ही माउस घटनाओं को संभालने की आवश्यकता होगी। आपको इन घटनाओं के मूल स्वरूप को सूचित करना होगा क्योंकि वे बबल नहीं करते हैं।
क्रिस्टोफ गेयर्स

इसके अलावा, जब मुझे इस पर WinForms समाधान के बारे में पता था, तो मैं WPF में मौजूद एक आसान तरीके की उम्मीद कर रहा था, मुझे लगता है कि मुझे इस प्रश्न में स्पष्ट करना चाहिए (अभी यह सिर्फ एक टैग है)।
एलेक्स के

माफ करना मेरा बुरा। WPF टैग पर ध्यान नहीं दिया। मूल प्रश्न में उल्लेख नहीं किया गया था। मैं सिर्फ डिफ़ॉल्ट रूप से WinForms मान लिया, टैग पर देखा।
क्रिस्टोफ गेयर्स

2

जैसा कि @ fjch1997 द्वारा पहले ही उल्लेख किया गया है , यह एक व्यवहार को लागू करने के लिए सुविधाजनक है। यहाँ यह है, मूल तर्क @ loi.efy के उत्तर के समान है :

public class DragMoveBehavior : Behavior<Window>
{
    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += AssociatedObject_MouseMove;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
    }

    private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && sender is Window window)
        {
            // In maximum window state case, window will return normal state and
            // continue moving follow cursor
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;

                // 3 or any where you want to set window location after
                // return from maximum state
                Application.Current.MainWindow.Top = 3;
            }

            window.DragMove();
        }
    }
}

उपयोग:

<Window ...
        xmlns:h="clr-namespace:A.Namespace.Of.DragMoveBehavior"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <i:Interaction.Behaviors>
        <h:DragMoveBehavior />
    </i:Interaction.Behaviors>
    ...
</Window>

1

यह सब जरूरत है!

private void UiElement_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            if (this.WindowState == WindowState.Maximized) // In maximum window state case, window will return normal state and continue moving follow cursor
            {
                this.WindowState = WindowState.Normal;
                Application.Current.MainWindow.Top = 3;// 3 or any where you want to set window location affter return from maximum state
            }
            this.DragMove();
        }
    }

0

सबसे उपयोगी विधि, दोनों के लिए WPF और विंडोज़ फॉर्म, WPF उदाहरण:

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

    public static void StartDrag(Window window)
    {
        WindowInteropHelper helper = new WindowInteropHelper(window);
        SendMessage(helper.Handle, 161, 2, 0);
    }

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.