Перетащите всплывающее окно WPF

элемент управления WPF Popup хорош, но, на мой взгляд, несколько ограничен. есть ли способ «перетащить» всплывающее окно, когда оно открыто (например, с помощью метода DragMove() окон)?

можно ли это сделать без больших проблем или мне нужно самому написать замену классу всплывающего окна? Благодарность


person Joachim Kerschbaumer    schedule 21.10.2008    source источник


Ответы (6)


Вот простое решение с использованием Thumb.

  • Всплывающее окно подкласса в XAML и отделенном коде
  • Добавьте Thumb с шириной/высотой, установленными на 0 (это также можно сделать в XAML)
  • Слушайте события MouseDown во всплывающем окне и вызывайте то же событие в Thumb.
  • Переместить всплывающее окно на DragDelta

XAML:

<Popup x:Class="PopupTest.DraggablePopup" ...>
    <Canvas x:Name="ContentCanvas">

    </Canvas>
</Popup>

C#:

public partial class DraggablePopup : Popup 
{
    public DraggablePopup()
    {
        var thumb = new Thumb
        {
            Width = 0,
            Height = 0,
        };
        ContentCanvas.Children.Add(thumb);

        MouseDown += (sender, e) =>
        {
            thumb.RaiseEvent(e);
        };

        thumb.DragDelta += (sender, e) =>
        {
            HorizontalOffset += e.HorizontalChange;
            VerticalOffset += e.VerticalChange;
        };
    }
}
person jacob    schedule 17.11.2011
comment
Возможно, я упускаю что-то простое, что знал бы опытный разработчик WPF, но как это можно использовать повторно? Когда я помещаю элемент управления в другой контекст XAML (например, пытаюсь повторно использовать его в окне), любое указанное мной содержимое переопределяет элемент Canvas, к которому привязан бегунок. - person Vassi; 18.01.2013
comment
Я пробовал это решение. Работает отлично! Спасибо Джейкоб. Я столкнулся только с одной проблемой: у меня есть вид внутри этого Thumb, и я могу нормально перетаскивать весь вид, однако, если в этом представлении есть полоса прокрутки, то перетаскивание полосы прокрутки вызывает перетаскивание большого пальца и, следовательно, весь вид. Есть ли способ избежать этого? - person Shankar Raju; 19.03.2014

Для PopUp нет DragMove. Просто небольшая работа, есть много улучшений, которые вы можете добавить к этому.

<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint"  Width="200">
   <Rectangle Stretch="Fill" Fill="Red"/>            
</Popup>

В коде позади добавьте это событие mousemove

   pop.MouseMove += new MouseEventHandler(pop_MouseMove);

   void pop_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
                e.GetPosition(this).Y),new Point(200,200));

        }
    }
person Jobi Joy    schedule 21.10.2008
comment
Ой! Нет, используйте Thumb и его DragDelta события (выберите DragStarted/DragCompleted, если вы хотите управлять состояниями). Это намного эффективнее, не фрагментирует память и не рискует потерять мышь, как в примере выше. Вот как правильно выполняется перетаскивание :) См. пример здесь. - person RedGlyph; 02.09.2010
comment
Я согласен, что решение, которое я дал, - это всего лишь хак. Как вы сказали, DragDelta более эффективен, чем MoveMove. - person Jobi Joy; 03.09.2010
comment
Но что, если вы не хотите неправильно использовать всплывающее окно в качестве большого пальца (что, я думаю, я имел в виду в комментарии RedGlyphs), но хотите переместить более сложное всплывающее окно (например, наложение видео). использование явного окна не вариант в моем сценарии. - person Joachim Kerschbaumer; 26.07.2011

Другой способ добиться этого - установить размещение вашего всплывающего окна на MousePoint. Это заставляет всплывающее окно изначально появляться в позиции курсора мыши.

Затем вы можете использовать событие Thumb или MouseMove, чтобы установить HorizontalOffset и VerticalOffset всплывающего окна. Эти свойства смещают всплывающее окно из исходного положения, когда пользователь перетаскивает его.

Не забудьте сбросить HorizontalOffset и VerticalOffset обратно на ноль для следующего использования всплывающего окна!

person Ashley Davis    schedule 02.11.2011

Проблема с потерей мыши при слишком быстром движении может быть решена


Это взято из msdn:

Новое окно содержит дочерний контент Popup.

Элемент управления Popup поддерживает ссылку на свое дочернее содержимое в качестве логического дочернего элемента. При создании нового окна содержимое Popup становится визуальным дочерним элементом окна и остается логическим дочерним элементом Popup. И наоборот, Popup остается логическим родителем своего дочернего содержимого.


Другими словами, дочерний элемент всплывающего окна отображается в отдельном окне.

Итак, при попытке выполнить следующее:
Popup.CaptureMouse() захватывает окно-оболочку, а не само всплывающее окно. Вместо этого использование Popup.Child.CaptureMouse() захватывает фактическое всплывающее окно.

А все остальные события должны быть зарегистрированы с помощью Popup.Child.

Например, Popup.Child.MouseMove, Popup.Child.LostCapture и так далее.

Это было протестировано и отлично работает

person Leon    schedule 09.12.2011
comment
Да, захват ввода мыши — правильный способ справиться с перетаскиванием. Все это e.Leftbutton == Pressed немного лениво и вызывает некоторые побочные эффекты. - person Alan; 31.10.2012

Основываясь на ответе Jobi Joy, я нашел повторно используемое решение, которое позволяет добавлять в качестве элемента управления в xaml существующего элемента управления/страницы. Что было невозможно добавить как Xaml с именем, поскольку оно имеет другую область действия.

    [ContentProperty("Child")]
    [DefaultEvent("Opened")]
    [DefaultProperty("Child")]
    [Localizability(LocalizationCategory.None)]
    public class DraggablePopup : Popup
    {
        public DraggablePopup()
        {
            MouseDown += (sender, e) =>
            {
                Thumb.RaiseEvent(e);
            };

            Thumb.DragDelta += (sender, e) =>
            {
                HorizontalOffset += e.HorizontalChange;
                VerticalOffset += e.VerticalChange;
            };
        }

        /// <summary>
        /// The original child added via Xaml
        /// </summary>
        public UIElement TrueChild { get; private set; }

        public Thumb Thumb { get; private set; } = new Thumb
        {
            Width = 0,
            Height = 0,
        };

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            TrueChild = Child;

            var surrogateChild = new StackPanel();

            RemoveLogicalChild(TrueChild);

            surrogateChild.Children.Add(Thumb);
            surrogateChild.Children.Add(TrueChild);

            AddLogicalChild(surrogateChild);
            Child = surrogateChild;
        }
    }
person Gregory A. Owen    schedule 13.02.2019

Private Point startPoint;

 private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {

        startPoint = e.GetPosition(null);
    }
private void Window_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            Point relative = e.GetPosition(null);
            Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
            this.Top = AbsolutePos.Y - startPoint.Y;
            this.Left = AbsolutePos.X - startPoint.X;
        }
    }

Это работает для перетаскивания моего окна, но, как было сказано, если я передвигаю мышь слишком быстро, оно выйдет из окна и перестанет вызывать событие. Не говоря уже о том, что перетаскивание совсем не гладкое. Кто-нибудь знает, как это сделать правильно, красиво и плавно перетаскивая, не теряя при слишком быстром перетаскивании??? Разместите простой пример, если это возможно, кроме целого руководства, которое заставит новичков, таких как я, потеряться в коде. Спасибо!

person Jonatas    schedule 30.04.2011
comment
вы можете захватить мышь, чтобы избежать упомянутой вами проблемы, когда мышь выходит из окна - person Joachim Kerschbaumer; 02.05.2011