使用MVVM Helpers在Xamarin ViewModel中处理Entry控件Completed事件的问题
解决Entry Completed事件绑定MVVM命令的错误问题
首先,咱们来拆解你遇到的cannot convert from 'void' to 'System.Action'错误:
你在实例化命令时写了这行代码:
UpdatedCost_Dif = new MvvmHelpers.Commands.Command(UpdatedCost_Completed(null,null));
这里的核心问题是你直接执行了方法UpdatedCost_Completed(null,null),这个方法执行后返回void,但Command的构造函数需要的是一个Action委托(也就是无参数的方法引用),不是方法执行后的结果,这就导致了类型不匹配的错误。另外,你的UpdatedCost_Completed方法带了sender和EventArgs参数,而MvvmHelpers的基础Command默认只接受无参数的方法,这也会导致签名不匹配。
下面给你两种可行的解决思路,优先推荐第一种(更贴合MVVM的设计思想):
方案一:通过数据绑定获取输入(推荐的MVVM写法)
在MVVM模式里,我们应该避免直接操作UI控件,而是通过绑定把Entry的文本和ViewModel的属性关联起来。这样代码更简洁,也符合前后端分离的原则。
修改后的XAML代码:
<Entry x:Name="UpdatedCost" Text="{Binding UpdatedCostText}" Completed="{Binding CalculatePriceDiffCommand}"/>
修改后的ViewModel代码:
public class ExistingProductPricingViewModel : BaseViewModel { // 绑定Entry的Text到这个属性 private string _updatedCostText; public string UpdatedCostText { get => _updatedCostText; set => SetProperty(ref _updatedCostText, value); } private int _priceDifference; public int PriceDifference { get => _priceDifference; set => SetProperty(ref _priceDifference, value); } // 处理Completed事件的命令 public ICommand CalculatePriceDiffCommand { get; } // 把json改成私有只读字段,更符合封装原则 private readonly string _baseValueJson = "2"; public ExistingProductPricingViewModel() { // 实例化命令,传递方法引用即可,不要直接调用方法 CalculatePriceDiffCommand = new MvvmHelpers.Commands.Command(CalculatePriceDifference); } private void CalculatePriceDifference() { // 用TryParse避免输入非数字时抛出异常 if (int.TryParse(UpdatedCostText, out int updatedValue)) { int baseValue = int.Parse(_baseValueJson); PriceDifference = updatedValue - baseValue; } else { // 输入无效时的处理逻辑,比如重置差值 PriceDifference = 0; } } }
这个方案的优势:
- 完全遵循MVVM的解耦思想,不需要直接操作UI控件
- 增加了输入合法性校验,避免程序崩溃
- 代码逻辑清晰,后续维护更方便
方案二:修正原有命令绑定(适合特殊场景)
如果你一定要通过sender获取Entry的文本,可以使用MvvmHelpers的带参数命令Command<object>,把控件实例作为参数传递给命令:
修改后的XAML代码:
<Entry x:Name="UpdatedCost" Completed="Entry_Completed"/>
然后在页面的后台代码(.xaml.cs)里把事件转发给ViewModel的命令:
private void Entry_Completed(object sender, EventArgs e) { if (BindingContext is ExistingProductPricingViewModel vm) { vm.UpdatedCost_Dif.Execute(sender); } }
修改后的ViewModel代码:
public class ExistingProductPricingViewModel : BaseViewModel { // 使用带参数的Command<object> public ICommand UpdatedCost_Dif { get; } int current_diff = 0; public string json = "2"; public int PriceDifference { get => current_diff; set => SetProperty(ref current_diff, value); } public ExistingProductPricingViewModel() { // 传递方法引用,不要直接调用方法 UpdatedCost_Dif = new MvvmHelpers.Commands.Command<object>(UpdatedCost_Completed); } private void UpdatedCost_Completed(object sender) { // 先校验控件类型和输入合法性 if (sender is Entry entry && int.TryParse(entry.Text, out int updated)) { current_diff = updated - int.Parse(json); PriceDifference = current_diff; } } }
这个方案解决了原有的错误:
- 用
Command<object>适配带参数的方法 - 实例化命令时传递方法引用,而非直接执行方法
- 增加了类型检查和安全解析,避免空引用或格式错误异常
总结来说,方案一更符合MVVM的设计理念,也更易于测试和维护,优先推荐使用。如果有特殊需求必须操作控件,再考虑方案二。
内容的提问来源于stack exchange,提问作者Kahalon
相关产品推荐
相关产品推荐

