#c_sharp #wpf
Здравствуйте. Меня очень интересует, как, например, в браузере google chrome (и не только) вкладка помещается в другое окно при перетаскивании? Это ведь не DragDrop. Что тогда? Все события мыши передаются только в то окно, которое я тащу, тогда как мне определить, что курсор мыши находится на панели вкладок другого окна, которое под перетаскиваемым? Просьба не давать ссылки на готовые контролы табов, в которых это реализовано. Ну или с конкретным файлом и строкой. Потому что я пытался найти в них, как это реализовано, но не смог (на гитхабе).
Ответы
Ответ 1
Ну вот вам пример. Для начала, создадим окно, которое умеет определять, какому из экземпляров принадлежит точка на экране. Положим в окно контейнер для перемещаемых элементов.(Код, работающий с 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 AllByZOrder() { var byHandle = App.Current.Windows .OfType () .ToDictionary(w => ((HwndSource)PresentationSource.FromVisual(w)) .Handle); 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(); } } } Всё! Теперь вы можете таскать цветные квадраты из одного окна в другое.
Комментариев нет:
Отправить комментарий