Здравствуйте. Меня очень интересует, как, например, в браузере google chrome (и не только) вкладка помещается в другое окно при перетаскивании? Это ведь не DragDrop. Что тогда? Все события мыши передаются только в то окно, которое я тащу, тогда как мне определить, что курсор мыши находится на панели вкладок другого окна, которое под перетаскиваемым?
Просьба не давать ссылки на готовые контролы табов, в которых это реализовано. Ну или с конкретным файлом и строкой. Потому что я пытался найти в них, как это реализовано, но не смог (на гитхабе).
Ответ
Ну вот вам пример.
Для начала, создадим окно, которое умеет определять, какому из экземпляров принадлежит точка на экране. Положим в окно контейнер для перемещаемых элементов.
(Код, работающий с Z-порядком, взят из этого ответа.)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace CrossWindowDrop
{
public partial class DropContainerWindow : Window
{
public DropContainerWindow()
{
InitializeComponent();
}
// добавить перемещаемый элемент
public void AcceptActor(Actor actor)
{
Arena.Children.Add(actor);
}
// убрать перемещаемый элемент
public void ReleaseActor(Actor actor)
{
Arena.Children.Remove(actor);
}
// найти самое верхнее окно для данной точки
public static DropContainerWindow BestTarget(Point screenPoint)
{
return AllByZOrder().FirstOrDefault(w =>
{
var arena = w.Arena;
var arenaPoint = arena.PointFromScreen(screenPoint);
return arenaPoint.X >= 0 &&
arenaPoint.Y >= 0 &&
arenaPoint.X < arena.ActualWidth &&
arenaPoint.Y < arena.ActualHeight;
});
}
// получить список всех окон, отсортированный по Z-порядку
// для обхода окон приходится использовать WinAPI, т. к.
// WPF не знает о Z-порядке
static IEnumerable
for (IntPtr hWnd = GetTopWindow(IntPtr.Zero);
hWnd != IntPtr.Zero;
hWnd = GetWindow(hWnd, GW_HWNDNEXT))
{
DropContainerWindow w;
if (byHandle.TryGetValue(hWnd, out w))
yield return w;
}
}
// вспомогательные WinAPI-функции и константы
const uint GW_HWNDNEXT = 2;
[DllImport("user32")]
static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("user32")]
static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
}
}
Теперь, сам перемещаемый элемент. Реализуем его как UserControl
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace CrossWindowDrop
{
public partial class Actor : UserControl
{
public Actor()
{
InitializeComponent();
}
// эти три свойства валидны лишь на время движения
// запоминает позицию мыши внутри контрола
Point relativeMousePos;
// контейнер
FrameworkElement parent;
// окно, в котором мы находимся в данный момент
DropContainerWindow window;
// на нажатии мыши начинаем движение
void OnMouseDown(object sender, MouseButtonEventArgs e)
{
parent = (FrameworkElement)this.Parent;
window = (DropContainerWindow)Window.GetWindow(this);
relativeMousePos = e.GetPosition(this);
// подписываемся на перемещение мыши
MouseMove += OnDragMove;
// и захватываем mouse capture
Mouse.Capture(this);
}
// при отпускании мыши прекращаем движение
void OnMouseUp(object sender, MouseButtonEventArgs e)
{
// отписываемся от перемещения мыши
MouseMove -= OnDragMove;
// обновляем наше местоположение
UpdatePosition(e);
// отпускаем mouse capture
Mouse.Capture(null);
window = null;
parent = null;
}
void OnDragMove(object sender, MouseEventArgs e)
{
UpdatePosition(e);
}
void UpdatePosition(MouseEventArgs e)
{
// абсолютное положение точки на экране
var screenPoint = PointToScreen(relativeMousePos);
// находим наиболее подходящее окно
var bestWindow = DropContainerWindow.BestTarget(screenPoint);
// если мы надо окном, и это не текущее окно...
if (bestWindow != null && bestWindow != window)
MigrateTo(bestWindow); // ... переезжаем!
// позиция мыши относительно нашего контейнера
var point = e.GetPosition(parent);
// перемещаемся в нужную позицию
Canvas.SetLeft(this, point.X - relativeMousePos.X);
Canvas.SetTop(this, point.Y - relativeMousePos.Y);
}
// самое интересное: переезд в новое окно
void MigrateTo(DropContainerWindow newWindow)
{
// отпускаем мышь - наш контрол пересоздастся в другом окне
Mouse.Capture(null);
// просим наше окно выписать отпустить нас
window.ReleaseActor(this);
// а новое - принять
newWindow.AcceptActor(this);
// подправляем наши свойства
parent = (FrameworkElement)this.Parent;
window = newWindow;
// и снова захватываем мышь
Mouse.Capture(this);
// вот и всё
}
}
}
Ну и добавьте главную программу по вкусу.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace CrossWindowDrop
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var w1 = new DropContainerWindow() { Title = "Window 1" };
var actor1 = new Actor() { Background = Brushes.Violet };
var actor2 = new Actor() { Background = Brushes.Green };
Canvas.SetLeft(actor2, 50);
w1.AcceptActor(actor1);
w1.AcceptActor(actor2);
w1.Show();
new DropContainerWindow() { Title = "Window 2" }.Show();
new DropContainerWindow() { Title = "Window 3" }.Show();
}
}
}
Всё! Теперь вы можете таскать цветные квадраты из одного окна в другое.
Комментариев нет:
Отправить комментарий