Avalonia封装实现指定组件允许拖动的工具类Avalonia为弱件组件注入动态交互性:拖动功能实现

GPT4.0+Midjourney绘画+国内大模型 会员永久免费使用!
如果你想靠AI翻身,你先需要一个靠谱的工具!

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

1
2
3
4
5
6
7
8
<ItemGroup>
    <PackageReference Include="Avalonia" Version="11.0.0-preview5" />
    <PackageReference Include="Avalonia.Desktop" Version="11.0.0-preview5" />
    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
    <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为下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using DragDemo.ViewModels;
 
namespace DragDemo;
 
public class ViewLocator : IDataTemplate
{
    /// <summary>
    /// 将IControl修改成Control
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    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,因为预览版本的包已经独立需要单独安装

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<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",参数让其不要占用整个屏幕,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<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>

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

1
2
3
4
5
6
7
8
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
    }
}

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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<Control, DragModule> _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
{
    /// <summary>
    /// 记录上一次鼠标位置
    /// </summary>
    private Point? lastMousePosition;
 
    /// <summary>
    /// 用于平滑更新坐标的计时器
    /// </summary>
    private DispatcherTimer _timer;
 
    /// <summary>
    /// 标记是否先启动了拖动
    /// </summary>
    private bool isDragging = false;
 
    /// <summary>
    /// 需要更新的坐标点
    /// </summary>
    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;
    }
 
 
    /// <summary>
    /// 计时器事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnTimerTick(object sender, EventArgs e)
    {
        var window = UserControl.FindAncestorOfType<Window>();
        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<Window>();
        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再卸载掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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>("Border");
            DragControlHelper.StartDrag(border);
        });
    }
 
    protected override void OnUnloaded()
    {
        var border = this.Find<Border>("Border");
        DragControlHelper.StopDrag(border);
        base.OnUnloaded();
    }
}

效果展示:

到此这篇关于Avalonia封装实现指定组件允许拖动的工具类的文章就介绍到这了,更多相关Avalonia拖动指定组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空