Hangi öğe tıklanırsa tıklansın WPF penceresini sürüklenebilir yapın


111

Sorum 2 kat ve umarım WinForms'un (bu açıklamayı yapmadan önce Christophe Geers sağladığı) standart çözümlerden ziyade WPF tarafından sağlanan daha kolay çözümler vardır .

İlk olarak, fareyle tıklama + sürükleme olaylarını yakalamadan ve işlemeden Pencereyi sürüklenebilir hale getirmenin bir yolu var mı? Demek istediğim, pencere başlık çubuğu tarafından sürüklenebilir, ancak bir pencereye sahip olmamak için bir pencere ayarlarsam ve yine de onu sürükleyebilmek istersem, olayları bir şekilde başlık çubuğunu sürükleyen her şeye yeniden yönlendirmenin bir yolu var mı? ?

İkinci olarak, penceredeki tüm öğelere bir olay işleyicisi uygulamanın bir yolu var mı? Olduğu gibi, kullanıcı hangi öğeyi tıklayıp sürüklerse sürüklesin pencereyi sürüklenebilir yapın. Açıkçası işleyiciyi her bir öğeye manuel olarak eklemeden. Sadece bir yerde bir kez mi yaparsın?

Yanıtlar:


284

Elbette, aşağıdaki MouseDownetkinliği uygulaWindow

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

Bu, kullanıcıların MouseDown olayını ( e.Handled = true) yiyen kontroller HARİÇ, herhangi bir kontrolü tıkladıklarında / sürüklediklerinde Pencereyi sürüklemelerine izin verecektir.

Bunun PreviewMouseDownyerine kullanabilirsiniz MouseDown, ancak drag olayı Clickolayı yer, bu nedenle pencereniz sol fare tıklatma olaylarına yanıt vermeyi durdurur. Herhangi bir kontrolden formu GERÇEKTEN tıklayıp sürükleyebilmek istiyorsanız, muhtemelen kullanabilir PreviewMouseDown, sürükleme işlemine başlamak için bir zamanlayıcı başlatabilir ve MouseUpolay X milisaniye içinde tetiklenirse işlemi iptal edebilirsiniz .


+1. Pencere yöneticisinin konumu hatırlayarak ve pencereyi hareket ettirerek hareket ettirmek yerine hareketi halletmesine izin vermek çok daha iyi. (İkinci yöntemin de bazı uç durumlarda yanlış gitme eğilimi vardır, yine de)
Joey

Neden MouseLeftButtonDown.cs'yi kontrol etmek yerine etkinliği ayarlamıyorsunuz ?

1
@Drowin Muhtemelen bunun yerine bu olayı kullanabilirsiniz, ancak önce MouseLeftButtonDowndoğrudan bir yönlendirme stratejisi varken MouseDownköpüren bir yönlendirme stratejisi olduğundan önce test ettiğinizden emin olun . MouseLeftButtonDown için MSDN sayfasının açıklamalar bölümüne bakın ve daha fazla bilgi ve MouseLeftButtonDownüzerinde kullanıp kullanmayacağınız konusunda dikkat etmeniz gereken bazı ekstra şeyler için MouseDown.
Rachel

@Rachel Evet kullanıyorum ve işe yarıyor, ama açıklama için teşekkürler!

2
@Rahul Bir UserControl'ü sürüklemek çok daha zordur ... bunu Canvas gibi bir üst panele yerleştirmeniz ve kullanıcı fareyi hareket ettirirken X / Y (veya Canvas.Top ve Canvas.Left) özelliklerini manuel olarak ayarlamanız gerekir. Bunu en son yaptığımda fare olaylarını kullandım, bu yüzden OnMouseDown yakalama konumu ve kayıt taşıma olayı, OnMouseMove X / Y değişikliği ve OnMouseUp taşıma olayını kaldır. Temel fikir bu :)
Rachel

9

wpf formunun nereye tıklandığına bakılmaksızın sürüklenebilir olması gerekiyorsa, kolay çalışma, Windows onload olayında veya grid load olayında DragMove () yöntemini tetiklemek için bir temsilci kullanmaktır.

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

2
Bunu kurucuya ekledim. Büyüleyici çalışıyor.
Joe Johnston

1
Formda herhangi bir yere sağ tıklarsanız bu bir istisna oluşturur, çünkü DragMoveyalnızca birincil fare düğmesi aşağıdayken çağrılabilir.
Stjepan Bakrac

4

Bazen, Windowörneğin kullanıyorsak DevExpress, tüm mevcut olan a UIElement.

1. Adım: Ekli mülkü ekleyin

Çözüm şudur:

  1. İçine Kanca MouseMoveolaylar;
  2. İlk ebeveyni bulana kadar görsel ağacı arayın Window;
  3. .DragMove()Yeni keşfimizi arayın Window.

Kod:

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

Adım 2: Pencereyi sürüklemesine izin vermek için herhangi bir öğeye Ekli Mülk ekleyin

Bu ekli özelliği eklersek, kullanıcı belirli bir öğeye tıklayarak tüm pencereyi sürükleyebilir:

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

Ek A: İsteğe Bağlı Gelişmiş Örnek

DevExpress'ten alınan bu örnekte, yerleştirme penceresinin başlık çubuğunu kendi gri dikdörtgenimizle değiştiriyoruz, ardından kullanıcı söz konusu gri dikdörtgeni tıklayıp sürüklediğinde pencerenin normal şekilde sürüklenmesini sağlıyoruz:

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

Yasal Uyarı: am değil bağlı DevExpress . Bu teknik, standart WPF veya Telerik (başka bir iyi WPF kitaplığı sağlayıcısı) dahil olmak üzere herhangi bir kullanıcı öğesi ile çalışacaktır .


1
Bu tam olarak istediğim şey. IMHO arkasındaki tüm WPF kodu ekli davranış olarak yazılmalıdır.
fjch1997

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

Bazı durumlarda bir istisna atıyor (örneğin, pencerede tıklandığında bir mesaj kutusu açan tıklanabilir bir görüntünüz varsa. Mesaj kutusundan çıktığınızda hata alırsınız) Kullanması daha güvenlidir

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

Yani o anda sol düğmeye basıldığından eminsiniz.


Muhtemelen hiçbir zaman önemli olmayacak olsa bile, özellikle olay argümanları ile ilişkili düğmeyi kullanmak e.LeftButtonyerine kullanıyorum Mouse.LeftButton.
Fls'Zen

2

Sadece başlık çubuğunu değil, formun herhangi bir yerine tıklayarak bir formu sürükleyip bırakmak mümkündür. Kenarlıksız bir formunuz varsa bu kullanışlıdır.

CodeProject hakkındaki bu makale, bunu uygulamak için olası bir çözümü göstermektedir:

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

Temel olarak, farenin aşağı, yukarı ve hareket olaylarının işlendiği Form türünün bir nesli oluşturulur.

  • Fare aşağı: konumu hatırla
  • Fare hareketi: yeni konumu kaydet
  • Fare yukarı: formu yeni konuma konumlandırma

Ve işte bir video eğitiminde açıklanan benzer bir çözüm:

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

Kullanıcı söz konusu formdaki bir denetimi tıkladığında formu sürüklemeye izin vermem. Kullanıcılar, farklı kontrollere tıkladıklarında farklı sonuçlar elde ederler. Bir liste kutusunu, düğmeyi, etiketi vb. Tıkladığım için formum aniden hareket etmeye başladığında. bu kafa karıştırıcı olurdu.


Elbette herhangi bir kontrole tıklayarak hareket etmeyecektir, ancak tıklayıp sürüklediyseniz, formun hareket etmesini beklemezdiniz. Demek istediğim, bir buton veya liste kutusunun hareket etmesini beklemiyorsunuz mesela eğer tıklayıp + sürükleseniz, formdaki bir butona tıklayıp sürüklemeye çalışsanız formun hareketi doğal bir beklentidir diye düşünüyorum.
Alex K

Tahmin et, bu sadece kişisel zevk. Her neyse .... kontrollerin aynı fare olaylarını işlemesi gerekir. Baloncuk olmadıkları için bu olayların ebeveyn formunu bilgilendirmeniz gerekir.
Christophe Geers

Ayrıca, bunun için WinForms çözümünün farkındayken, WPF'de var olmanın daha kolay bir yolunu umuyordum, sanırım soruyu daha net hale getirmeliyim (şu anda bu sadece bir etiket).
Alex K

Üzgünüm benim hatam. WPF etiketini fark etmedim. Orijinal soruda bahsedilmedi. Varsayılan olarak WinForms'u varsaydım, etikete baktım.
Christophe Geers

2

@ Fjch1997 tarafından daha önce belirtildiği gibi, bir davranışı uygulamak uygundur. İşte çekirdek mantık @ loi.efy en aynıdır olduğu cevap :

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();
        }
    }
}

Kullanımı:

<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

Bunların hepsi gerekli!

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

Hem WPF hem de Windows formu için en kullanışlı yöntem, WPF örneği:

    [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);
    }

0
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
    <![CDATA[            
        private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }
    ]]>
</x:Code>

kaynak

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.