You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI中如何在MVVM架构下使用手势识别器事件?

.NET MAUI中MVVM架构下处理Pointer手势的正确方式

是否应该使用MVVM?

当然应该。MVVM是.NET MAUI开发中的主流架构,通过分离UI(View)和业务逻辑(ViewModel),能大幅提升代码的可维护性、可测试性,而CommunityToolkit.MVVM工具包更是简化了MVVM的实现(比如[ObservableProperty]、RelayCommand等特性),非常适合.NET MAUI项目。

手势事件的MVVM友好处理方案

方案1:使用EventToCommandBehavior(推荐)

CommunityToolkit.Maui提供了EventToCommandBehavior,可以直接把UI事件绑定到ViewModel中的RelayCommand,完美契合MVVM的事件处理思路。

示例代码:
首先在XAML头部添加命名空间:

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"

然后修改手势识别器的写法:

<Button>
    <Button.GestureRecognizers>
        <PointerGestureRecognizer>
            <PointerGestureRecognizer.Behaviors>
                <!-- 处理PointerEntered事件 -->
                <toolkit:EventToCommandBehavior
                    EventName="PointerEntered"
                    Command="{Binding PointerEnteredCommand}" />
                <!-- 处理PointerExited事件 -->
                <toolkit:EventToCommandBehavior
                    EventName="PointerExited"
                    Command="{Binding PointerExitedCommand}" />
            </PointerGestureRecognizer.Behaviors>
        </PointerGestureRecognizer>
    </Button.GestureRecognizers>
</Button>

在ViewModel中定义对应的命令:

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class YourViewModel : ObservableObject
{
    [RelayCommand]
    private void PointerEntered()
    {
        // 处理鼠标进入逻辑(业务逻辑或需要通知UI的逻辑)
    }

    [RelayCommand]
    private void PointerExited()
    {
        // 处理鼠标离开逻辑
    }
}

如果需要传递事件参数(比如PointerEventArgs),可以修改命令为带参数的版本:

[RelayCommand]
private void PointerEntered(PointerEventArgs e)
{
    // 使用参数处理逻辑
}

对应的XAML中添加PassEventArgument="True":

<toolkit:EventToCommandBehavior
    EventName="PointerEntered"
    Command="{Binding PointerEnteredCommand}"
    PassEventArgument="True" />

方案2:自定义附加属性

如果需要更灵活的控制,可以自定义附加属性,将手势事件的触发映射到ViewModel的命令。示例代码如下:

定义附加属性类:

public static class PointerGestureCommands
{
    public static readonly BindableProperty EnterCommandProperty =
        BindableProperty.CreateAttached(
            "EnterCommand",
            typeof(ICommand),
            typeof(PointerGestureCommands),
            null,
            propertyChanged: OnEnterCommandChanged);

    public static ICommand GetEnterCommand(BindableObject view)
    {
        return (ICommand)view.GetValue(EnterCommandProperty);
    }

    public static void SetEnterCommand(BindableObject view, ICommand value)
    {
        view.SetValue(EnterCommandProperty, value);
    }

    private static void OnEnterCommandChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is PointerGestureRecognizer recognizer)
        {
            recognizer.PointerEntered -= OnPointerEntered;
            if (newValue is ICommand command)
            {
                recognizer.PointerEntered += (s, e) => command.Execute(e);
            }
        }
    }

    // 同理实现PointerExited的附加属性
    public static readonly BindableProperty ExitCommandProperty =
        BindableProperty.CreateAttached(
            "ExitCommand",
            typeof(ICommand),
            typeof(PointerGestureCommands),
            null,
            propertyChanged: OnExitCommandChanged);

    public static ICommand GetExitCommand(BindableObject view)
    {
        return (ICommand)view.GetValue(ExitCommandProperty);
    }

    public static void SetExitCommand(BindableObject view, ICommand value)
    {
        view.SetValue(ExitCommandProperty, value);
    }

    private static void OnExitCommandChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is PointerGestureRecognizer recognizer)
        {
            recognizer.PointerExited -= OnPointerExited;
            if (newValue is ICommand command)
            {
                recognizer.PointerExited += (s, e) => command.Execute(e);
            }
        }
    }

    private static void OnPointerEntered(object sender, PointerEventArgs e)
    {
        var command = GetEnterCommand(sender as BindableObject);
        command?.Execute(e);
    }

    private static void OnPointerExited(object sender, PointerEventArgs e)
    {
        var command = GetExitCommand(sender as BindableObject);
        command?.Execute(e);
    }
}

然后在XAML中使用:

<Button>
    <Button.GestureRecognizers>
        <PointerGestureRecognizer
            local:PointerGestureCommands.EnterCommand="{Binding PointerEnteredCommand}"
            local:PointerGestureCommands.ExitCommand="{Binding PointerExitedCommand}" />
    </Button.GestureRecognizers>
</Button>

ViewModel中的命令写法和方案1一致。

方案3:纯UI逻辑可直接放在代码后置

如果你的手势处理只是纯UI相关的操作(比如改变按钮的背景色、字体颜色等,没有涉及业务逻辑),完全可以直接写在代码后置文件中,不用强行套MVVM的命令模式。MVVM的核心是分离业务逻辑和UI,纯UI代码放在代码后置是合理的,不会破坏MVVM的架构原则。

比如:

private void ButtonOnPointerEntered(object sender, EventArgs e)
{
    if (sender is Button button)
    {
        button.BackgroundColor = Colors.LightBlue;
    }
}

private void ButtonOnPointerExit(object sender, EventArgs e)
{
    if (sender is Button button)
    {
        button.BackgroundColor = Colors.Blue;
    }
}

内容的提问来源于stack exchange,提问作者eop3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:01:32