Доработан winui
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
using Lattice.Core.Docking.Abstractions;
|
||||
using Lattice.Core.Docking.Engine;
|
||||
using Lattice.Core.Docking.Models;
|
||||
using Lattice.UI.Docking;
|
||||
using Lattice.UI.Docking.Abstractions;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -13,25 +13,6 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Lattice.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Представляет главный контейнер док-системы для WinUI, который служит корневым элементом
|
||||
/// пользовательского интерфейса для размещения всех компонентов системы докинга.
|
||||
/// Этот контрол управляет всем макетом приложения, включая основное дерево компоновки,
|
||||
/// плавающие окна и автоскрываемые панели.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <see cref="LatticeDockHost"/> является центральным координатором UI-слоя док-системы,
|
||||
/// интегрирующим функциональность менеджера макета, системы перетаскивания и контекстных меню.
|
||||
/// Он обеспечивает согласованное отображение всех элементов и обрабатывает пользовательские
|
||||
/// взаимодействия на верхнем уровне.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Контрол реализует интерфейс <see cref="IDockHost"/> и предоставляет полный набор методов
|
||||
/// для управления структурой док-системы, включая создание/закрытие плавающих окон и
|
||||
/// добавление/удаление автоскрываемых панелей.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
{
|
||||
private readonly PropertyChangedEventHandler _modelPropertyChangedHandler;
|
||||
@@ -50,13 +31,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
private bool _showMenu = true;
|
||||
private ContentControl? _rootContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр класса <see cref="LatticeDockHost"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Конструктор устанавливает ключ стиля по умолчанию, инициализирует обработчик изменений модели
|
||||
/// и подписывается на событие изменения контекста данных.
|
||||
/// </remarks>
|
||||
public LatticeDockHost()
|
||||
{
|
||||
this.DefaultStyleKey = typeof(LatticeDockHost);
|
||||
@@ -64,17 +38,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
this.DataContextChanged += OnDataContextChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает модель данных, связанную с этим контролом.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Экземпляр, реализующий <see cref="IDockElement"/>, представляющий корневой элемент
|
||||
/// дерева компоновки. Может быть null.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Этот элемент является корнем всего макета док-системы. При изменении этого свойства
|
||||
/// происходит перестройка всего пользовательского интерфейса.
|
||||
/// </remarks>
|
||||
public IDockElement? Model
|
||||
{
|
||||
get => _model;
|
||||
@@ -88,16 +51,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает менеджер макета, к которому принадлежит этот контрол.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Экземпляр <see cref="LayoutManager"/>, управляющий структурой док-системы.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Менеджер макета используется для выполнения операций с деревом компоновки
|
||||
/// и координации изменений между различными элементами системы.
|
||||
/// </remarks>
|
||||
public LayoutManager? LayoutManager
|
||||
{
|
||||
get => _layoutManager;
|
||||
@@ -109,16 +62,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает контекстный менеджер для этого контрола.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Экземпляр <see cref="IDockContextManager"/>, управляющий контекстными меню и действиями.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Контекстный менеджер используется для отображения меню, связанных с этим элементом,
|
||||
/// и выполнения команд, доступных в текущем контексте.
|
||||
/// </remarks>
|
||||
public IDockContextManager? ContextManager
|
||||
{
|
||||
get => _contextManager;
|
||||
@@ -130,16 +73,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает признак того, что контрол выбран.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контрол выбран; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Выделение контрола обычно визуально выделяет его границы или фон,
|
||||
/// чтобы указать пользователю на активный элемент.
|
||||
/// </remarks>
|
||||
public bool IsSelected
|
||||
{
|
||||
get => _isSelected;
|
||||
@@ -151,15 +84,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает признак того, что контрол активен.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контрол активен; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Активный контрол обычно получает фокус ввода и может обрабатывать команды клавиатуры.
|
||||
/// </remarks>
|
||||
public bool IsActive
|
||||
{
|
||||
get => _isActive;
|
||||
@@ -171,16 +95,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает признак того, что контрол можно перетаскивать.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контрол можно перетаскивать; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Этот флаг влияет на возможность инициирования операции перетаскивания
|
||||
/// при взаимодействии пользователя с этим контролом.
|
||||
/// </remarks>
|
||||
public bool CanDrag
|
||||
{
|
||||
get => _canDrag;
|
||||
@@ -192,16 +106,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает признак того, что контрол может принимать сброс.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если контрол может принимать сброс; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Этот флаг влияет на возможность завершения операции перетаскивания
|
||||
/// сбросом данных на этот контрол.
|
||||
/// </remarks>
|
||||
public bool CanDrop
|
||||
{
|
||||
get => _canDrop;
|
||||
@@ -213,42 +117,9 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает коллекцию контролов плавающих окон, связанных с этим хостом.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Коллекция объектов, реализующих <see cref="IFloatingWindowControl"/>,
|
||||
/// представляющих все активные плавающие окна в системе.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Коллекция является наблюдаемой (ObservableCollection), что позволяет автоматически
|
||||
/// обновлять пользовательский интерфейс при добавлении или удалении окон.
|
||||
/// </remarks>
|
||||
public IEnumerable<IFloatingWindowControl> FloatingWindows => _floatingWindows;
|
||||
|
||||
/// <summary>
|
||||
/// Получает коллекцию контролов автоскрываемых панелей, прикрепленных к краям окна.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Коллекция объектов, реализующих <see cref="IAutoHidePanelControl"/>,
|
||||
/// представляющих автоскрываемые панели на разных сторонах окна.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Коллекция является наблюдаемой (ObservableCollection), что позволяет автоматически
|
||||
/// обновлять пользовательский интерфейс при добавлении или удалении панелей.
|
||||
/// </remarks>
|
||||
public IEnumerable<IAutoHidePanelControl> AutoHidePanels => _autoHidePanels;
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает значение, указывающее, отображается ли панель инструментов (Toolbox).
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если панель инструментов видима; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Панель инструментов обычно содержит элементы для быстрого доступа к командам
|
||||
/// или создания новых компонентов в приложении.
|
||||
/// </remarks>
|
||||
public bool ShowToolbox
|
||||
{
|
||||
get => _showToolbox;
|
||||
@@ -260,16 +131,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает значение, указывающее, отображается ли строка состояния.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если строка состояния видима; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Строка состояния обычно отображает текущий статус приложения,
|
||||
/// информацию о выбранном элементе или прогресс выполнения операций.
|
||||
/// </remarks>
|
||||
public bool ShowStatusBar
|
||||
{
|
||||
get => _showStatusBar;
|
||||
@@ -281,15 +142,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получает или задает значение, указывающее, отображается ли главное меню приложения.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// true, если главное меню видимо; в противном случае — false.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Главное меню содержит основные команды приложения, организованные в иерархическую структуру.
|
||||
/// </remarks>
|
||||
public bool ShowMenu
|
||||
{
|
||||
get => _showMenu;
|
||||
@@ -301,41 +153,43 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Событие, возникающее при изменении структуры макета док-системы.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Может вызываться при добавлении/удалении элементов, изменении размеров,
|
||||
/// создании/закрытии плавающих окон и других операциях, влияющих на компоновку.
|
||||
/// </remarks>
|
||||
public event EventHandler? LayoutChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Событие, возникающее при создании нового плавающего окна.
|
||||
/// </summary>
|
||||
public event EventHandler<FloatingWindowCreatedEventArgs>? FloatingWindowCreated;
|
||||
|
||||
/// <summary>
|
||||
/// Событие, возникающее при закрытии плавающего окна.
|
||||
/// </summary>
|
||||
public event EventHandler<FloatingWindowClosedEventArgs>? FloatingWindowClosed;
|
||||
|
||||
/// <summary>
|
||||
/// Событие, возникающее при изменении значения свойства.
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при применении шаблона контрола.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Метод получает ссылки на именованные части шаблона и обновляет отображение
|
||||
/// корневого содержимого в соответствии с текущим состоянием модели.
|
||||
/// </remarks>
|
||||
/// <inheritdoc/>
|
||||
public FrameworkElement? DragDropElement => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetupDragDropHandlers()
|
||||
{
|
||||
this.AllowDrop = true;
|
||||
this.CanDrag = true;
|
||||
|
||||
// Настройка обработчиков для хоста
|
||||
this.Drop += OnHostDrop;
|
||||
this.DragOver += OnHostDragOver;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StartDrag() { /* Реализация */ }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void EndDrag() { /* Реализация */ }
|
||||
|
||||
private void OnHostDragOver(object sender, DragEventArgs args)
|
||||
{
|
||||
args.AcceptedOperation = CanDrop ?
|
||||
Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move :
|
||||
Windows.ApplicationModel.DataTransfer.DataPackageOperation.None;
|
||||
args.DragUIOverride.IsGlyphVisible = true;
|
||||
args.DragUIOverride.Caption = "Закрепить здесь";
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_rootContainer = GetTemplateChild("PART_RootContainer") as ContentControl;
|
||||
UpdateRootContent();
|
||||
}
|
||||
@@ -349,11 +203,8 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
{
|
||||
if (_model != null && _layoutManager != null)
|
||||
{
|
||||
// Подписываемся на события менеджера макета
|
||||
_layoutManager.LayoutUpdated += OnLayoutUpdated;
|
||||
_layoutManager.AutoHidePanelsChanged += OnAutoHidePanelsChanged;
|
||||
|
||||
// Устанавливаем DataContext
|
||||
this.DataContext = _model;
|
||||
UpdateRootContent();
|
||||
}
|
||||
@@ -363,17 +214,14 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
{
|
||||
if (_model != null && _layoutManager != null)
|
||||
{
|
||||
// Отписываемся от событий
|
||||
_layoutManager.LayoutUpdated -= OnLayoutUpdated;
|
||||
_layoutManager.AutoHidePanelsChanged -= OnAutoHidePanelsChanged;
|
||||
|
||||
this.DataContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
// Обработка изменений модели
|
||||
OnPropertyChanged(e.PropertyName);
|
||||
}
|
||||
|
||||
@@ -385,7 +233,6 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
|
||||
private void OnAutoHidePanelsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
// Обновление автоскрываемых панелей
|
||||
OnPropertyChanged(nameof(AutoHidePanels));
|
||||
}
|
||||
|
||||
@@ -393,8 +240,7 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
{
|
||||
if (_rootContainer != null && _model != null && _layoutManager != null)
|
||||
{
|
||||
// Создаем дерево контролов через фабрику
|
||||
var factory = LatticeUIFramework.ControlFactory;
|
||||
var factory = Lattice.UI.Docking.LatticeUIFramework.ControlFactory;
|
||||
if (factory != null)
|
||||
{
|
||||
var control = factory.CreateControlForElement(_model);
|
||||
@@ -403,157 +249,44 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Создает новое плавающее окно для размещения указанного элемента док-системы.
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// Элемент док-системы (группа или лист), который будет размещен в плавающем окне.
|
||||
/// </param>
|
||||
/// <param name="title">Заголовок создаваемого окна.</param>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="IFloatingWindowControl"/>, представляющий созданное плавающее окно.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="element"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// Выбрасывается, так как метод еще не реализован.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Созданное окно может быть перемещено пользователем в любое место экрана,
|
||||
/// изменено в размерах и обычно содержит стандартные элементы управления окном
|
||||
/// (заголовок, кнопки закрытия/сворачивания).
|
||||
/// </remarks>
|
||||
public IFloatingWindowControl CreateFloatingWindow(IDockElement element, string title)
|
||||
{
|
||||
if (element == null) throw new ArgumentNullException(nameof(element));
|
||||
|
||||
// TODO: Реализовать создание плавающего окна через фабрику
|
||||
throw new NotImplementedException();
|
||||
throw new NotImplementedException("Floating windows not implemented yet");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Закрывает указанное плавающее окно и возвращает его содержимое в основной макет.
|
||||
/// </summary>
|
||||
/// <param name="window">
|
||||
/// Плавающее окно, которое необходимо закрыть.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="window"/> равен null.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// При закрытии плавающего окна его содержимое обычно возвращается в то место
|
||||
/// в основном макете, откуда оно было извлечено, или в ближайшую допустимую позицию.
|
||||
/// </remarks>
|
||||
public void CloseFloatingWindow(IFloatingWindowControl window)
|
||||
{
|
||||
if (window == null) throw new ArgumentNullException(nameof(window));
|
||||
|
||||
if (_floatingWindows.Remove(window))
|
||||
{
|
||||
FloatingWindowClosed?.Invoke(this, new FloatingWindowClosedEventArgs(window));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет автоскрываемую панель с указанным содержимым к заданной стороне окна.
|
||||
/// </summary>
|
||||
/// <param name="content">
|
||||
/// Контент, который будет отображаться в автоскрываемой панели.
|
||||
/// </param>
|
||||
/// <param name="side">
|
||||
/// Сторона окна, к которой будет прикреплена панель.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Экземпляр <see cref="IAutoHidePanelControl"/>, представляющий созданную панель.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="content"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Выбрасывается, если свойство <see cref="LayoutManager"/> не установлено.
|
||||
/// </exception>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// Выбрасывается, так как метод еще не реализован.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Автоскрываемые панели полезны для инструментов, к которым нужен частый,
|
||||
/// но не постоянный доступ, так как они экономят пространство экрана.
|
||||
/// </remarks>
|
||||
public IAutoHidePanelControl AddAutoHidePanel(Core.Docking.Abstractions.IDockContent content, DockSide side)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
|
||||
if (_layoutManager != null)
|
||||
{
|
||||
var panel = _layoutManager.AddAutoHidePanel(content, side);
|
||||
|
||||
// TODO: Создать UI-контрол для автоскрываемой панели через фабрику
|
||||
throw new NotImplementedException();
|
||||
throw new NotImplementedException("Auto-hide panels not implemented yet");
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("LayoutManager is not set");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет автоскрываемую панель из интерфейса.
|
||||
/// </summary>
|
||||
/// <param name="panel">
|
||||
/// Автоскрываемая панель, которую необходимо удалить.
|
||||
/// </param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Выбрасывается, если <paramref name="panel"/> равен null.
|
||||
/// </exception>
|
||||
/// <exception cref="NotImplementedException">
|
||||
/// Выбрасывается, так как метод еще не реализован.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// После удаления панели её содержимое обычно либо закрывается полностью,
|
||||
/// либо преобразуется в обычную закрепленную панель, в зависимости от настроек.
|
||||
/// </remarks>
|
||||
public void RemoveAutoHidePanel(IAutoHidePanelControl panel)
|
||||
{
|
||||
if (panel == null) throw new ArgumentNullException(nameof(panel));
|
||||
|
||||
// TODO: Реализовать удаление автоскрываемой панели
|
||||
throw new NotImplementedException();
|
||||
throw new NotImplementedException("Auto-hide panels not implemented yet");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Обновляет внешний вид контрола в соответствии с текущим состоянием модели.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Вызывает обновление корневого содержимого и всех дочерних элементов.
|
||||
/// </remarks>
|
||||
public void Refresh()
|
||||
{
|
||||
UpdateRootContent();
|
||||
}
|
||||
public object? PrepareDragData() => Model;
|
||||
public bool HandleDrop(object data, DockPosition position) => false;
|
||||
|
||||
public void Refresh() => UpdateRootContent();
|
||||
|
||||
/// <summary>
|
||||
/// Применяет указанную тему к контролу.
|
||||
/// </summary>
|
||||
/// <param name="theme">Тема для применения.</param>
|
||||
/// <remarks>
|
||||
/// В текущей реализации метод является заглушкой и должен быть расширен
|
||||
/// для поддержки динамического изменения тем.
|
||||
/// </remarks>
|
||||
public void ApplyTheme(IDockTheme theme)
|
||||
{
|
||||
// Применение темы к контролу
|
||||
if (theme != null)
|
||||
{
|
||||
// TODO: Реализовать применение темы к стилям контрола
|
||||
}
|
||||
// TODO: Реализовать применение темы
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вызывается при изменении состояния модели для обновления UI.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Имя изменившегося свойства модели.</param>
|
||||
/// <remarks>
|
||||
/// Перенаправляет вызов в обработчик изменений модели.
|
||||
/// </remarks>
|
||||
public void OnModelPropertyChanged(string propertyName)
|
||||
{
|
||||
if (_model != null)
|
||||
@@ -567,24 +300,84 @@ public sealed class LatticeDockHost : Control, IDockHost, IDisposable
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Освобождает ресурсы, используемые этим экземпляром контрола.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Выполняет отписку от событий модели, очистку коллекций и освобождение ресурсов.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
DetachModel();
|
||||
|
||||
// Очищаем коллекции
|
||||
_floatingWindows.Clear();
|
||||
_autoHidePanels.Clear();
|
||||
|
||||
_disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
private DockPosition GetDropPosition(Windows.Foundation.Point point)
|
||||
{
|
||||
if (ActualWidth <= 0 || ActualHeight <= 0)
|
||||
return DockPosition.Center;
|
||||
|
||||
var relativeX = point.X / ActualWidth;
|
||||
var relativeY = point.Y / ActualHeight;
|
||||
|
||||
// Определяем регионы для докирования
|
||||
const double edgeThreshold = 0.2; // 20% от краев
|
||||
const double centerThreshold = 0.4; // Центральная область
|
||||
|
||||
// Проверяем края
|
||||
if (relativeX < edgeThreshold) return DockPosition.Left;
|
||||
if (relativeX > (1 - edgeThreshold)) return DockPosition.Right;
|
||||
if (relativeY < edgeThreshold) return DockPosition.Top;
|
||||
if (relativeY > (1 - edgeThreshold)) return DockPosition.Bottom;
|
||||
|
||||
// Если в центральной области
|
||||
if (relativeX > centerThreshold && relativeX < (1 - centerThreshold) &&
|
||||
relativeY > centerThreshold && relativeY < (1 - centerThreshold))
|
||||
{
|
||||
return DockPosition.Center;
|
||||
}
|
||||
|
||||
// По умолчанию - центр
|
||||
return DockPosition.Center;
|
||||
}
|
||||
|
||||
private void OnHostDrop(object sender, DragEventArgs args)
|
||||
{
|
||||
if (CanDrop && args.DataView.Properties.TryGetValue("LatticeDockElement", out var data))
|
||||
{
|
||||
// Получаем позицию сброса
|
||||
var position = GetDropPosition(args.GetPosition(this));
|
||||
|
||||
// Определяем целевой элемент
|
||||
IDockElement? target = null;
|
||||
if (args.OriginalSource is FrameworkElement element)
|
||||
{
|
||||
// Находим соответствующий контрол докинга
|
||||
var dockControl = FindDockControl(element);
|
||||
target = dockControl?.Model;
|
||||
}
|
||||
|
||||
// Если цель не найдена, используем корневой элемент
|
||||
target ??= LayoutManager?.Root;
|
||||
|
||||
if (data is IDockElement source && target != null)
|
||||
{
|
||||
LayoutManager?.Move(source, target, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IDockControl? FindDockControl(FrameworkElement element)
|
||||
{
|
||||
// Поднимаемся по дереву элементов, чтобы найти контрол докинга
|
||||
var current = element;
|
||||
while (current != null)
|
||||
{
|
||||
if (current is IDockControl dockControl)
|
||||
return dockControl;
|
||||
|
||||
current = VisualTreeHelper.GetParent(current) as FrameworkElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user