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

如何在Xamarin.Forms中实现类似WPF/UWP的Loaded触发命令?

在Xamarin.Forms中实现页面加载时绑定MVVM命令的正确方式

你提到在WPF/UWP里用MVVM Light的EventTriggerBehavior绑定Loaded事件很顺畅,但在Xamarin.Forms里尝试自定义EventToCommandBehavior绑定OnAppearing时遇到了报错。咱们一步步来解决这个问题:

首先明确:Xamarin.Forms里的页面生命周期事件和WPF/UWP不同,而且MVVM Light在Xamarin.Forms中的Behavior实现也有区别,不能直接照搬WPF的写法。

一、先分析你的报错原因

你自定义的EventToCommandBehavior大概率存在实现漏洞,导致反射调用构造函数时出错。另外一个关键错误是:你绑定的OnAppearing是页面代码后台的虚方法,不是Xamarin.Forms的可绑定路由事件,正确的事件名应该是Appearing。

二、正确实现方式(三种可选)

方式1:直接用MVVM Light为Xamarin.Forms适配的Behavior

MVVM Light已经为Xamarin.Forms提供了现成的EventToCommandBehavior,不用自己造轮子:

  1. 先确认通过NuGet安装了MvvmLight和MvvmLight.Forms包
  2. 在XAML中引入命名空间:
xmlns:mvvm="clr-namespace:GalaSoft.MvvmLight.Xamarin.Forms;assembly=GalaSoft.MvvmLight.Forms"
  1. 绑定Appearing事件到你的命令:
<ContentPage.Behaviors>
    <mvvm:EventToCommandBehavior
        EventName="Appearing"
        Command="{Binding LoadedCommand}" />
</ContentPage.Behaviors>

方式2:自定义正确的EventToCommandBehavior

如果你坚持自己实现Behavior,下面是一个标准的、避免反射错误的模板:

using System;
using System.Reflection;
using Xamarin.Forms;

namespace XamarinTest.Behaviors
{
    public class EventToCommandBehavior : Behavior<VisualElement>
    {
        public static readonly BindableProperty EventNameProperty =
            BindableProperty.Create(nameof(EventName), typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);

        public static readonly BindableProperty CommandProperty =
            BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(EventToCommandBehavior), null);

        public string EventName
        {
            get => (string)GetValue(EventNameProperty);
            set => SetValue(EventNameProperty, value);
        }

        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

        private EventInfo _targetEvent;

        protected override void OnAttachedTo(VisualElement bindable)
        {
            base.OnAttachedTo(bindable);
            RegisterEvent(EventName);
        }

        protected override void OnDetachingFrom(VisualElement bindable)
        {
            DeregisterEvent(EventName);
            base.OnDetachingFrom(bindable);
        }

        private void RegisterEvent(string eventName)
        {
            if (string.IsNullOrWhiteSpace(eventName)) return;

            _targetEvent = AssociatedObject.GetType().GetRuntimeEvent(eventName);
            if (_targetEvent == null)
                throw new ArgumentException($"找不到事件 {eventName} 在 {AssociatedObject.GetType().Name} 上");

            var handler = _targetEvent.EventHandlerType
                .GetMethod("Invoke")
                .CreateDelegate(_targetEvent.EventHandlerType, this, typeof(EventToCommandBehavior).GetMethod("OnEventTriggered"));
            
            _targetEvent.AddEventHandler(AssociatedObject, handler);
        }

        private void DeregisterEvent(string eventName)
        {
            if (_targetEvent == null || string.IsNullOrWhiteSpace(eventName)) return;

            var handler = _targetEvent.EventHandlerType
                .GetMethod("Invoke")
                .CreateDelegate(_targetEvent.EventHandlerType, this, typeof(EventToCommandBehavior).GetMethod("OnEventTriggered"));
            
            _targetEvent.RemoveEventHandler(AssociatedObject, handler);
            _targetEvent = null;
        }

        private void OnEventTriggered(object sender, EventArgs e)
        {
            if (Command?.CanExecute(e) ?? false)
                Command.Execute(e);
        }

        private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var behavior = (EventToCommandBehavior)bindable;
            if (behavior.AssociatedObject == null) return;

            behavior.DeregisterEvent((string)oldValue);
            behavior.RegisterEvent((string)newValue);
        }
    }
}

然后在XAML中绑定正确的事件名:

<ContentPage.Behaviors>
    <behaviors:EventToCommandBehavior 
        EventName="Appearing" 
        Command="{Binding LoadedCommand}" />
</ContentPage.Behaviors>

方式3:最简单的替代方案(不用Behavior)

如果不想折腾Behavior,直接在页面代码后台调用ViewModel的命令反而更直观:

public Page1()
{
    InitializeComponent();
    BindingContext = App.Locator.UserMainViewModel;
}

protected override void OnAppearing()
{
    base.OnAppearing();
    var viewModel = BindingContext as UserMainViewModel;
    viewModel?.LoadedCommand.Execute(null);
}

三、关键注意事项

  • Xamarin.Forms页面的显示触发事件是Appearing,不是WPF的Loaded,也不要用OnAppearing(这是代码后台的方法名,不是可绑定事件)
  • 确保你的RelayCommand是MVVM Light提供的GalaSoft.MvvmLight.Command.RelayCommand,ViewModel的绑定逻辑没有问题(你的绑定代码看起来是对的)
  • 自定义Behavior时一定要正确处理事件的注册和注销,避免内存泄漏和反射异常

按照上面的方法调整后,你的LoadedCommand应该就能在页面显示时正常触发了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:01:59