• 封装avalonia指定组件允许拖动的工具类


    封装avalonia指定组件允许拖动的工具类

    创建Avalonia的MVVM项目,命名DragDemo ,然后将项目的Nuget包更新到预览版

        <ItemGroup>
            <PackageReference Include="Avalonia" Version="11.0.0-preview5" />
            <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview5" />
            
            <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.0-preview5" />
            <PackageReference Include="Avalonia.ReactiveUI" Version="11.0.0-preview5" />
            <PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
        ItemGroup>
    

    更新完成以后ViewLocatorApp.axaml会报错,

    修改ViewLocator.cs为下面的代码

    using System;
    using Avalonia.Controls;
    using Avalonia.Controls.Templates;
    using DragDemo.ViewModels;
    
    namespace DragDemo;
    
    public class ViewLocator : IDataTemplate
    {
        /// 
        /// 将IControl修改成Control
        /// 
        /// 
        /// 
        public Control Build(object data)
        {
            var name = data.GetType().FullName!.Replace("ViewModel", "View");
            var type = Type.GetType(name);
    
            if (type != null)
            {
                return (Control)Activator.CreateInstance(type)!;
            }
    
            return new TextBlock { Text = "Not Found: " + name };
        }
    
        public bool Match(object data)
        {
            return data is ViewModelBase;
        }
    }
    

    添加Avalonia.Themes.Fluent,因为预览版本的包已经独立需要单独安装

    <PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.0-preview5" />
    

    打开App.axaml文件,修改为以下代码

    <Application xmlns="https://github.com/avaloniaui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="using:DragDemo"
                 RequestedThemeVariant="Light" 
                 x:Class="DragDemo.App">
        <Application.DataTemplates>
            <local:ViewLocator/>
        Application.DataTemplates>
    
        <Application.Styles>
            <FluentTheme DensityStyle="Compact"/>
        Application.Styles>
        
    Application>
    

    打开Views/MainWindow.axaml

    在头部添加以下代码,让窗口无边框,设置指定窗口Height="38" Width="471",参数让其不要占用整个屏幕,

    <Window xmlns="https://github.com/avaloniaui"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:vm="using:DragDemo.ViewModels"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
            x:Class="DragDemo.Views.MainWindow"
            Icon="/Assets/avalonia-logo.ico"
            ExtendClientAreaToDecorationsHint="True"
            ExtendClientAreaChromeHints="NoChrome"
            ExtendClientAreaTitleBarHeightHint="-1"
            MaxHeight="38" MaxWidth="471"
            Title="DragDemo">
        <Window.Styles>
            <Style Selector="Window">
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
            Style>
        Window.Styles>
        <Design.DataContext>
            <vm:MainWindowViewModel/>
        Design.DataContext>
        
        <StackPanel>
            <StackPanel Opacity="0.1" Height="38" Width="471">
            StackPanel>
            <Border Name="Border" Width="471" CornerRadius="10" Opacity="1" Background="#FFFFFF">
                <Button>按钮Button>    
            Border>
        StackPanel>
    Window>
    
    

    以下代码在上面窗口用于设置窗口无边框

        <Window.Styles>
            <Style Selector="Window">
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="BorderBrush" Value="Transparent"/>
            Style>
        Window.Styles>
    

    然后打开/Views/MainWindow.axaml.cs文件,将边框设置成无边框,并且设置窗体透明为WindowTransparencyLevel.Transparent

    using Avalonia;
    using Avalonia.Controls;
    
    namespace DragDemo.Views;
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.TransparencyLevelHint = WindowTransparencyLevel.Transparent;
            ExtendClientAreaToDecorationsHint = true;
            WindowState = WindowState.Maximized;
        }
    }
    

    效果图如下,因为限制了窗体最大大小,并且在按钮上面添加了透明区块,这样看起来就像是悬浮了

    image-20230228193242400

    然后我们开始写指定组件拖动工具类,创建DragControlHelper.cs 以下就是封装的工具类 定义了一个ConcurrentDictionary静态参数,指定组件为KeyValueDragModuleDragModule模型中定义了拖动的逻辑在调用StartDrag的时候传递需要拖动的组件,他会创建一个DragModule对象,创建的时候会创建定时器,当鼠标被按下时启动定时器,当鼠标被释放时定时器被停止,定时器用于平滑更新窗体移动,如果直接移动窗体会抖动。

    using System;
    using System.Collections.Concurrent;
    using Avalonia;
    using Avalonia.Controls;
    using Avalonia.Input;
    using Avalonia.Threading;
    using Avalonia.VisualTree;
    
    namespace DragDemo;
    
    
    public class DragControlHelper
    {
        private static ConcurrentDictionary _dragModules = new();
    
        public static void StartDrag(Control userControl)
        {
            _dragModules.TryAdd(userControl, new DragModule(userControl));
        }
    
        public static void StopDrag(Control userControl)
        {
            if (_dragModules.TryRemove(userControl, out var dragModule))
            {
                dragModule.Dispose();
            }
        }
    }
    
    class DragModule : IDisposable
    {
        /// 
        /// 记录上一次鼠标位置
        /// 
        private Point? lastMousePosition;
    
        /// 
        /// 用于平滑更新坐标的计时器
        /// 
        private DispatcherTimer _timer;
    
        /// 
        /// 标记是否先启动了拖动
        /// 
        private bool isDragging = false;
    
        /// 
        /// 需要更新的坐标点
        /// 
        private PixelPoint? _targetPosition;
    
        public Control UserControl { get; set; }
    
        public DragModule(Control userControl)
        {
            UserControl = userControl;
            // 添加当前控件的事件监听
            UserControl.PointerPressed += OnPointerPressed;
            UserControl.PointerMoved += OnPointerMoved;
            UserControl.PointerReleased += OnPointerReleased;
    
            // 初始化计时器
            _timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromMilliseconds(10)
            };
            _timer.Tick += OnTimerTick;
        }
    
    
        /// 
        /// 计时器事件
        /// 
        /// 
        /// 
        private void OnTimerTick(object sender, EventArgs e)
        {
            var window = UserControl.FindAncestorOfType();
            if (window != null && window.Position != _targetPosition)
            {
                // 更新坐标
                window.Position = (PixelPoint)_targetPosition;
            }
        }
    
        private void OnPointerPressed(object sender, PointerPressedEventArgs e)
        {
            if (!e.GetCurrentPoint(UserControl).Properties.IsLeftButtonPressed) return;
            // 启动拖动
            isDragging = true;
            // 记录当前坐标
            lastMousePosition = e.GetPosition(UserControl);
            e.Handled = true;
            // 启动计时器
            _timer.Start();
        }
    
        private void OnPointerReleased(object sender, PointerReleasedEventArgs e)
        {
            if (!isDragging) return;
            // 停止拖动
            isDragging = false;
            e.Handled = true;
            // 停止计时器
            _timer.Stop();
        }
    
        private void OnPointerMoved(object sender, PointerEventArgs e)
        {
            if (!e.GetCurrentPoint(UserControl).Properties.IsLeftButtonPressed) return;
    
            // 如果没有启动拖动,则不执行
            if (!isDragging) return;
    
            var currentMousePosition = e.GetPosition(UserControl);
            var offset =currentMousePosition - lastMousePosition.Value;
            var window = UserControl.FindAncestorOfType();
            if (window != null)
            {
                // 记录当前坐标
                _targetPosition = new PixelPoint(window.Position.X + (int)offset.X,
                    window.Position.Y + (int)offset.Y);
            }
        }
    
        public void Dispose()
        {
            _timer.Stop();
            _targetPosition = null;
            lastMousePosition = null;
        }
    }
    

    打开MainWindow.axaml.cs,修改成以下代码 ,在渲染成功以后拿到Border(需要移动的组件),添加到DragControlHelper.StartDrag(border);中,然后再OnUnloaded的时候将Border再卸载掉

    using Avalonia;
    using Avalonia.Controls;
    using Avalonia.Media;
    using Avalonia.Threading;
    
    namespace DragDemo.Views;
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.TransparencyLevelHint = WindowTransparencyLevel.Transparent;
            ExtendClientAreaToDecorationsHint = true;
            WindowState = WindowState.Maximized;
        }
    
        public override void Render(DrawingContext context)
        {
            base.Render(context);
            Dispatcher.UIThread.Post(() =>
            {
                var border = this.Find("Border");
                DragControlHelper.StartDrag(border);
            });
        }
    
        protected override void OnUnloaded()
        {
            var border = this.Find("Border");
            DragControlHelper.StopDrag(border);
            base.OnUnloaded();
        }
    }
    

    效果展示:

    来着token的分享

    技术交流群:737776595

  • 相关阅读:
    Vim基础
    深度学习之基于YoloV5-Pose的人体姿态检测可视化系统
    力扣随笔之移除元素(简单27)
    Servlet---上传文件
    Redis 非关系型数据库 配置与优化
    剑指offer专项突击版第31天
    Java如何解决浮点数计算不精确问题
    2022-08-19 第六小组 瞒春 学习笔记
    软件设计不是CRUD(7):低耦合模块设计实战——组织机构模块(中)
    虹科案例 | Zuellig Pharma和ELPRO通过符合GDP标准的温度监测和高效的温度数据管理为未来发展奠定基础
  • 原文地址:https://www.cnblogs.com/hejiale010426/p/17165737.html