Как переместить дочернее окно/диалоговое окно без полей в WPF

У меня есть MainWindow с границей и ChildWindow как диалог без границы. Когда открыто дочернее окно, невозможно переместить основное окно или изменить его размер.

Я хочу, чтобы приложение вело себя так, как будто это только одно окно.

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

Перетаскивание


person Kingpin    schedule 20.05.2014    source источник


Ответы (3)


Существует гораздо более простой способ включить перетаскивание или перемещение Window без полей. См. метод Window.DragMove на странице MSDN для получения более подробной информации, но вкратце вам просто нужно добавить эту строку в свой код в одном из обработчиков событий, связанных с нажатием кнопки мыши:

public YourWindow()
{
    InitializeComponent();
    MouseLeftButtonDown += YourWindow_MouseLeftButtonDown;
}

...

private void YourWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    DragMove(); // <-- this is all you need to add
}

Затем пользователи смогут щелкнуть любую область Window (в зависимости от того, что вы в нее поместите) и перетащить ее по экрану.

ОБНОВЛЕНИЕ >>>

Так что кажется, что ваши требования шире, чем я заметил сначала. Чтобы достичь того, чего вы хотите, есть ряд вещей, которые вы должны сделать. Во-первых, вам нужно расположить дочерний элемент Window в определенном месте относительно MainWindow.xaml Window. Когда вы откроете его, сделайте что-то вроде этого:

Window window = new Window();
window.Top = this.Top;
window.Left = this.Left;
window.LocationChanged += Window_LocationChanged;
window.ShowDialog();

Дочерняя позиция Window может быть смещена на некоторую заданную величину:

Window window = new Window();
window.Top = this.Top + someHorizontalOffsetAmount;
window.Left = this.Left + someVerticalOffsetAmount;
window.LocationChanged += Window_LocationChanged;
window.ShowDialog();

Затем вам нужен обработчик события Window.LocationChanged (которое возникает при перемещении дочернего элемента Window):

private void Window_LocationChanged(object sender, EventArgs e)
{
    Window window = (Window)sender;
    this.Top = window.Top;
    this.Left = window.Left;
}

Вот и все! Теперь два Window будут двигаться вместе. Очевидно, что если вы используете смещение в первом примере, вам нужно будет использовать те же смещения в обработчике Window_LocationChanged.

person Sheridan    schedule 20.05.2014
comment
Тогда движется только мое диалоговое окно. Это не то, чем я хотел заниматься. - person Kingpin; 20.05.2014

Похоже, ваше диалоговое окно является модальным, то есть оно было вызвано с помощью ShowDialog() и не позволяет вам использовать остальную часть приложения до тех пор, пока оно не будет закрыто, включая перемещение главного окна.

Если это не то поведение, которое вам нужно, вам нужно будет сделать свой диалог немодальным, просто вызвав Show(), или еще лучше, поскольку вы, кажется, хотите, чтобы он вел себя как одно окно, почему бы не использовать WPF как это было задумано и вообще избавиться от диалога?

person GazTheDestroyer    schedule 20.05.2014
comment
Да, было бы неплохо, если бы я использовал просто элемент управления в главном окне для отображения содержимого. К сожалению, у меня нет такой возможности :( - person Kingpin; 20.05.2014
comment
Мое окно отображается с помощью ShowDialog, но Show - это не то поведение, которое я хотел. - person Kingpin; 20.05.2014
comment
-1 За то, что не дал ответ на вопрос. По какой причине вы проголосовали против моего отлично работающего ответа? Возмездие?... это поступок озлобленного нуба. Не мог потерять 2 очка репутации, не отомстив? Да ладно, Газ... с более чем 8000 репутацией, ты наверняка лучше этого. Если нет, то это действительно жалкое поведение и очень неожиданное поведение известного члена. - person Sheridan; 20.05.2014
comment
@Шеридан, успокойся. Мой отрицательный голос был потому, что ваш ответ не ответил на вопрос даже после вашего редактирования. В OP есть модальный диалог, который вообще не позволяет перемещать окно приложения. - person GazTheDestroyer; 20.05.2014
comment
Если это действительно причина, по которой вы проголосовали против, то вы будете рады удалить его после того, как действительно протестируете мой код и убедитесь, что он действительно работает... обратите внимание на использование метода ShowDialog в моем примере... это должно было стать для вас ключом к тому, что я выполнил требования автора вопроса. Используя мой пример, я могу переместить оба Window как одно, как они просили: Я хочу, чтобы приложение вело себя так, как будто это только одно окно. - person Sheridan; 20.05.2014

Итак, я наконец нашел решение. Я написал расширение для класса Windows, и это было довольно сложно :)

namespace MultiWindowWPF
{
  using System;
  using System.Drawing;
  using System.Linq;
  using System.Threading;
  using System.Windows;
  using System.Windows.Forms;
  using System.Windows.Input;
  using System.Windows.Media;
  using System.Windows.Threading;

  using Application = System.Windows.Application;

  public static class WindowExtensions
  {
    /// <summary>
    /// Shows the Dialog Modal.
    /// </summary>
    /// <param name="dialogWindow">The dialog window.</param>
    public static void ShowModal(this Window dialogWindow)
    {
      Window window = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.IsKeyboardFocusWithin) ?? Application.Current.MainWindow;
      IInputElement lastFocused = FocusManager.GetFocusedElement(window);
      IInputElement lastKeyboardSelected = Keyboard.FocusedElement;
      EventHandler locationChanged = (sender, args) => ParentWndMove(dialogWindow);
      SizeChangedEventHandler sizeChanged = (sender, args) => ParentWndMove(dialogWindow);
      EventHandler stateChanged = (sender, args) => ParentWndStateChanged(dialogWindow);

      window.LocationChanged += locationChanged;
      window.SizeChanged += sizeChanged;
      window.StateChanged += stateChanged;

      EventHandler close = (sender, args) =>
      {

        if (dialogWindow.Dispatcher.CheckAccess())
        {
          dialogWindow.Close();
        }
        else
        {
          dialogWindow.Dispatcher.Invoke(dialogWindow.Close);
        }

        window.LocationChanged -= locationChanged;
        window.SizeChanged -= sizeChanged;
        window.StateChanged -= stateChanged;
      };

      EventHandler closed = (sender, args) =>
      {
        Window self = sender as Window;
        Enable();
        if (self != null)
        {
          self.Owner = null;
        }
      };

      ExitEventHandler exit = (sender, args) => close(sender, args);

      DependencyPropertyChangedEventHandler isEnabledChanged = null;
      isEnabledChanged = (o, eventArgs) =>
        {
          window.Dispatcher.BeginInvoke(
            DispatcherPriority.ApplicationIdle,
            new Action(
              () =>
                {
                  FocusManager.SetFocusedElement(window, lastFocused);
          Keyboard.Focus(lastKeyboardSelected);
        }));

        ((Window)o).IsEnabledChanged -= isEnabledChanged;
      };
      window.IsEnabledChanged += isEnabledChanged;
      dialogWindow.Closed += closed;
      Application.Current.Exit += exit;

      dialogWindow.Show();
      ParentWndMove(dialogWindow);

      while (Application.Current != null)
      {
        DoEvents();
      }
    }

    private static void DoEvents()
    {
      DispatcherFrame frame = new DispatcherFrame();
      Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
      Dispatcher.PushFrame(frame);
      Thread.Sleep(10);
    }

    private static void Enable()
    {
      foreach (Window window in Application.Current.Windows.OfType<Window>().Where(window => !window.OwnedWindows.OfType<Window>().Any()))
      {
        window.IsEnabled = true;
      }
    }

    /// <summary>
    /// Exits the frame.
    /// </summary>
    /// <param name="f">The f.</param>
    /// <returns></returns>
    public static object ExitFrame(object f)
    {
      ((DispatcherFrame)f).Continue = false;
      return null;
    }

    /// <summary>
    /// Parents the WND state changed.
    /// </summary>
    /// <param name="dialogWindow">The dialog window.</param>
    public static void ParentWndStateChanged(Window dialogWindow)
    {
      Window owner = dialogWindow.Owner;
      if (owner.WindowState != WindowState.Maximized)
      {
        dialogWindow.WindowState = owner.WindowState;
      }
      ParentWndMove(dialogWindow);
    }

    /// <summary>
    /// Parents the WND move.
    /// </summary>
    /// <param name="dialogWindow">The dialog window.</param>
    public static void ParentWndMove(Window dialogWindow)
    {
      Window owner = dialogWindow.Owner;
      PresentationSource presentationsource = PresentationSource.FromVisual(dialogWindow);
      Matrix m = presentationsource.CompositionTarget.TransformToDevice;

      double centerWidth = owner.Left + owner.ActualWidth / 2;
      double centerHeight = owner.Top + owner.ActualHeight / 2;
      if (owner.WindowState == WindowState.Normal)
      {
        dialogWindow.Top = centerHeight - dialogWindow.ActualHeight / 2;
        dialogWindow.Left = centerWidth - dialogWindow.ActualWidth / 2;
      }
      if (owner.WindowState == WindowState.Maximized)
      {
        //there is no current main window position to use, center on working screen
        Rectangle frame = Screen.FromPoint(new System.Drawing.Point((int)(dialogWindow.Left * m.M11), (int)(dialogWindow.Top * m.M22))).Bounds;

        dialogWindow.Left = frame.X / m.M11 + (frame.Width / m.M11 - dialogWindow.ActualWidth) / 2;
        dialogWindow.Top = frame.Y / m.M22 + (frame.Height / m.M22 - dialogWindow.ActualHeight) / 2;
      }
    }
  }
}
person Kingpin    schedule 20.05.2014