下一个例子会把这个写成一个style + cs类,这样方便模板调用。

ModernWindow.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Height="300" Width="300"
        x:Class="AvaloniaUI.ModernWindow"
        SystemDecorations="None"
        TransparencyLevelHint="Transparent"
        Background="Transparent"
        Title="ModernWindow">
    
    <Border x:Name="windowFrame"
            BorderBrush="#395984"
            BorderThickness="1"
            CornerRadius="0,20,30,40">

        <Border.Background>
            <LinearGradientBrush>
                <GradientStop Color="#E7EBF7" Offset="0"/>
                <GradientStop Color="#CEE3FF" Offset="0.5"/>
            </LinearGradientBrush>
        </Border.Background>

        <Grid RowDefinitions="auto,*,auto">
            <!-- 标题栏:PointerPressed 用于拖动窗口 -->
            <Grid Grid.Row="0"
                  Margin="1"
                  Background="Transparent"
                  ColumnDefinitions="*,auto"
                  PointerPressed="titleBar_PointerPressed">

                <!-- 可拖拽区域 -->
                <TextBlock Grid.Column="0"
                           VerticalAlignment="Center"
                           Padding="5"
                           Text="Modern Window"/>

                <!-- 关闭按钮 -->
                <Button Grid.Column="1"
                        Margin="4"
                        Padding="6,2"
                        VerticalAlignment="Center"
                        Click="cmdClose_Click">
                    x
                </Button>
            </Grid>

            <Grid Grid.Row="1" Background="#B5CBEF" >
                <TextBlock Grid.Row="1" VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Foreground="White"
                           FontSize="20"
                           Text="Content Goes Here"/>
            </Grid>

            <!-- Footer -->
            <Grid Grid.Row="2"
                  Margin="1,10,1,1"
                  ColumnDefinitions="*,auto"
                  Background="Transparent">

                <!-- Footer 文本 -->
                <TextBlock Grid.Column="0"
                           VerticalAlignment="Center"
                           HorizontalAlignment="Center"
                           Padding="5"
                           Text="Footer"/>

                <!-- 右下角 ResizeGrip -->
                <Border Grid.Column="1" ZIndex="100"
                        Width="16"
                        Height="16"
                        Margin="5"
                        Background="Transparent"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Bottom"
                        Tag="BottomRight"
                        PointerPressed="resizeGrip_PointerPressed"
                        PointerMoved="resizeGrip_PointerMoved"
                        PointerReleased="resizeGrip_PointerReleased">

                    <Rectangle Margin="1">
                        <Rectangle.Fill>
                            <VisualBrush TileMode="Tile"
                                         DestinationRect="0,0,4,4"
                                         SourceRect="0,0,8,8">
                                <VisualBrush.Visual>
                                    <Canvas Width="8" Height="8">
                                        <Rectangle Width="4"
                                                   Height="4"
                                                   Canvas.Left="4"
                                                   Canvas.Top="4"
                                                   Fill="#AAA"/>
                                    </Canvas>
                                </VisualBrush.Visual>
                            </VisualBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                </Border>
            </Grid>

            <!-- 四边 -->
            <Rectangle Width="5" Fill="Transparent" HorizontalAlignment="Left"  VerticalAlignment="Stretch"
                       Cursor="SizeWestEast" Tag="Left"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Width="5" Fill="Transparent" HorizontalAlignment="Right" VerticalAlignment="Stretch"
                       Cursor="SizeWestEast" Tag="Right"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Height="5" Fill="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Top"
                       Cursor="SizeNorthSouth" Tag="Top"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Height="5" Fill="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Bottom"
                       Cursor="SizeNorthSouth" Tag="Bottom"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <!-- 四角(10x10) -->
            <Rectangle Width="10" Height="10" Fill="Transparent"
                       HorizontalAlignment="Left" VerticalAlignment="Top"
                       Cursor="SizeAll" Tag="TopLeft"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Width="10" Height="10" Fill="Transparent"
                       HorizontalAlignment="Right" VerticalAlignment="Top"
                       Cursor="SizeAll" Tag="TopRight"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Width="10" Height="10" Fill="Transparent"
                       HorizontalAlignment="Left" VerticalAlignment="Bottom"
                       Cursor="SizeAll" Tag="BottomLeft"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>

            <Rectangle Width="10" Height="10" Fill="Transparent"
                       HorizontalAlignment="Right" VerticalAlignment="Bottom"
                       Cursor="SizeAll" Tag="BottomRight"
                       Grid.RowSpan="3"
                       PointerPressed="resizeGrip_PointerPressed"
                       PointerMoved="resizeGrip_PointerMoved"
                       PointerReleased="resizeGrip_PointerReleased"/>
        </Grid>
    </Border>
</Window>

ModernWindow.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using System;

namespace AvaloniaUI;

public partial class ModernWindow : Window
{
    [Flags]
    private enum ResizeEdges
    {
        None = 0,
        Left = 1,
        Top = 2,
        Right = 4,
        Bottom = 8
    }

    private bool isResizing;
    private ResizeEdges edges;

    private PixelPoint startWindowPos;        // Pixel
    private Size startWindowSize;             // DIP
    private PixelPoint startPointerScreenPx;  // Screen Pixel
    private double startRenderScaling;

    public ModernWindow()
    {
        InitializeComponent();
        MinWidth = 150;
        MinHeight = 120;
    }

    private void titleBar_PointerPressed(object? sender, PointerPressedEventArgs e)
    {
        if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
            BeginMoveDrag(e);
    }

    private void cmdClose_Click(object? sender, RoutedEventArgs e)
    {
        Close();
    }

    private void resizeGrip_PointerPressed(object? sender, PointerPressedEventArgs e)
    {
        if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
            return;

        if (sender is not Control c)
            return;

        edges = ParseEdges(c.Tag as string);
        if (edges == ResizeEdges.None)
            return;

        var topLevel = TopLevel.GetTopLevel(this);
        if (topLevel is null)
            return;

        isResizing = true;

        startWindowPos = Position; // Pixel
        startWindowSize = new Size(Bounds.Width, Bounds.Height); // DIP
        startRenderScaling = RenderScaling;

        startPointerScreenPx = GetPointerScreenPx(e, topLevel);

        e.Pointer.Capture(c);
        e.Handled = true;
    }

    private void resizeGrip_PointerReleased(object? sender, PointerReleasedEventArgs e)
    {
        if (!isResizing)
            return;

        isResizing = false;
        edges = ResizeEdges.None;

        e.Pointer?.Capture(null);
        e.Handled = true;
    }

    private void resizeGrip_PointerMoved(object? sender, PointerEventArgs e)
    {
        if (!isResizing || edges == ResizeEdges.None)
            return;

        var topLevel = TopLevel.GetTopLevel(this);
        if (topLevel is null)
            return;

        var pointerScreenPx = GetPointerScreenPx(e, topLevel);

        var dxPx = pointerScreenPx.X - startPointerScreenPx.X;
        var dyPx = pointerScreenPx.Y - startPointerScreenPx.Y;

        var dxDip = dxPx / startRenderScaling;
        var dyDip = dyPx / startRenderScaling;

        var newX = startWindowPos.X;
        var newY = startWindowPos.Y;

        var newW = startWindowSize.Width;
        var newH = startWindowSize.Height;

        var hasLeft = (edges & ResizeEdges.Left) != 0;
        var hasRight = (edges & ResizeEdges.Right) != 0;
        var hasTop = (edges & ResizeEdges.Top) != 0;
        var hasBottom = (edges & ResizeEdges.Bottom) != 0;

        if (hasRight)
            newW = Math.Max(MinWidth, startWindowSize.Width + dxDip);

        if (hasBottom)
            newH = Math.Max(MinHeight, startWindowSize.Height + dyDip);

        if (hasLeft)
        {
            newW = Math.Max(MinWidth, startWindowSize.Width - dxDip);
            newX = startWindowPos.X + dxPx;

            if (newW <= MinWidth)
                newX = startWindowPos.X + ClampDeltaPxForMin(startWindowSize.Width, MinWidth);
        }

        if (hasTop)
        {
            newH = Math.Max(MinHeight, startWindowSize.Height - dyDip);
            newY = startWindowPos.Y + dyPx;

            if (newH <= MinHeight)
                newY = startWindowPos.Y + ClampDeltaPxForMin(startWindowSize.Height, MinHeight);
        }

        Position = new PixelPoint(newX, newY);
        Width = newW;
        Height = newH;

        e.Handled = true;
    }

    private static ResizeEdges ParseEdges(string? tag)
    {
        return tag switch
        {
            "Left" => ResizeEdges.Left,
            "Right" => ResizeEdges.Right,
            "Top" => ResizeEdges.Top,
            "Bottom" => ResizeEdges.Bottom,
            "TopLeft" => ResizeEdges.Top | ResizeEdges.Left,
            "TopRight" => ResizeEdges.Top | ResizeEdges.Right,
            "BottomLeft" => ResizeEdges.Bottom | ResizeEdges.Left,
            "BottomRight" => ResizeEdges.Bottom | ResizeEdges.Right,
            _ => ResizeEdges.None
        };
    }

    private static PixelPoint GetPointerScreenPx(PointerEventArgs e, TopLevel topLevel)
    {
        var pInTopLevel = e.GetPosition(topLevel);      // DIP
        return topLevel.PointToScreen(pInTopLevel);     // Pixel
    }

    private int ClampDeltaPxForMin(double startSizeDip, double minSizeDip)
    {
        var clampPx = (startSizeDip - minSizeDip) * startRenderScaling;
        return (int)Math.Round(clampPx);
    }
}

运行效果

image

 

posted on 2026-03-22 15:27  dalgleish  阅读(2)  评论(0)    收藏  举报