using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; using Microsoft.Maui.Layouts; using System; namespace NotesDataAnalyst { public partial class ResizableCanvasPage : ContentPage { private const double InitialWidth = 150; private const double InitialHeight = 100; private double startX, startY; private bool isResizing; public ResizableCanvasPage() { InitializeComponent(); } private void AddResizableObject(double x, double y) { // Создаем основной контейнер (Frame) для объекта и ручки var resizableFrame = new Frame { BackgroundColor = Colors.LightBlue, BorderColor = Colors.DarkBlue, CornerRadius = 8, WidthRequest = InitialWidth, HeightRequest = InitialHeight, Padding = 0, HasShadow = true }; var content = new Label { Text = "Resizable Object", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center }; resizableFrame.Content = content; // Создаем ручку для изменения размера var resizeHandle = new BoxView { BackgroundColor = Colors.Gray, WidthRequest = 20, HeightRequest = 20, HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.End }; // Создаем слой AbsoluteLayout для ручки и фрейма var layout = new AbsoluteLayout(); AbsoluteLayout.SetLayoutBounds(resizableFrame, new Rect(0, 0, InitialWidth, InitialHeight)); layout.Children.Add(resizableFrame); // Позиционируем ручку AbsoluteLayout.SetLayoutFlags(resizeHandle, AbsoluteLayoutFlags.PositionProportional); AbsoluteLayout.SetLayoutBounds(resizeHandle, new Rect(1, 1, 20, 20)); layout.Children.Add(resizeHandle); // Добавляем обработчик перемещения var panGesture = new PanGestureRecognizer(); panGesture.PanUpdated += (s, e) => OnDragPanUpdated(s, e, layout); resizableFrame.GestureRecognizers.Add(panGesture); // Добавляем обработчик для изменения размера var resizePanGesture = new PanGestureRecognizer(); resizePanGesture.PanUpdated += (s, e) => OnResizePanUpdated(s, e, layout); resizeHandle.GestureRecognizers.Add(resizePanGesture); // Устанавливаем начальную позицию AbsoluteLayout.SetLayoutBounds(layout, new Rect(x, y, InitialWidth, InitialHeight)); CanvasLayout.Children.Add(layout); } // Логика перемещения private void OnDragPanUpdated(object sender, PanUpdatedEventArgs e, AbsoluteLayout layout) { if (isResizing) return; if (e.StatusType == GestureStatus.Started) { startX = AbsoluteLayout.GetLayoutBounds(layout).X; startY = AbsoluteLayout.GetLayoutBounds(layout).Y; } else if (e.StatusType == GestureStatus.Running) { double x = startX + e.TotalX; double y = startY + e.TotalY; // Обновление UI через Dispatcher Dispatcher.Dispatch(() => { AbsoluteLayout.SetLayoutBounds(layout, new Rect(x, y, layout.WidthRequest, layout.HeightRequest)); }); } } // Логика изменения размера private void OnResizePanUpdated(object sender, PanUpdatedEventArgs e, AbsoluteLayout layout) { isResizing = true; if (e.StatusType == GestureStatus.Started) { startX = layout.WidthRequest; startY = layout.HeightRequest; } else if (e.StatusType == GestureStatus.Running) { double newWidth = Math.Max(50, startX + e.TotalX); double newHeight = Math.Max(50, startY + e.TotalY); Dispatcher.Dispatch(() => { layout.WidthRequest = newWidth; layout.HeightRequest = newHeight; }); } else if (e.StatusType == GestureStatus.Completed || e.StatusType == GestureStatus.Canceled) { isResizing = false; } } protected override void OnAppearing() { base.OnAppearing(); AddResizableObject(100, 100); AddResizableObject(400, 200); AddResizableObject(700, 400); } } }