关于Xamarin.Forms中MVVM命令绑定时状态更新机制

简介: 在编写Xamarin.Forms的MVVM的时候,发现Button绑定命令时,canExecute这个Func<bool>只执行一次,在我界面绑定的值发生变化时并不像标准WPF那样,按钮状态发生变化。很是不爽。开始调查吧。

最近研究Xamarin.Forms,发现它的MVVM的命令绑定很是让我郁闷,和标准WPF也不一样。
我要做一个简单的登录界面,在用户名和密码都输入的时候,才能点击登录,当有一个输入时可以点击清除按钮。XAML文件如下:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             xmlns:local="clr-namespace:MVVMDemo"
             x:Class="MVVMDemo.MainPage">
    <ContentPage.Resources>
        <local:MainPageViewModel x:Key="vm"></local:MainPageViewModel>
        <local:ValueConverter x:Key="valuecheck"></local:ValueConverter>
    </ContentPage.Resources>
    <Grid HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" BindingContext="{StaticResource vm}">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Label Text="Welcome to Xamarin.Forms!" Grid.ColumnSpan="2"
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
        <Label Text="UserName:" Grid.Row="1"></Label>
        <Label Text="Password:" Grid.Row="2"></Label>
        <Button Text="Login" Grid.Row="3" Command="{Binding LoginCommand}">
        </Button>
        <Entry Text="{Binding UserName,Mode=TwoWay,UpdateSourceEventName=TextChanged}" Grid.Row="1" Grid.Column="1" x:Name="EntryName"></Entry>
        <Entry Text="{Binding Password,Mode=TwoWay,UpdateSourceEventName=TextChanged}" Grid.Row="2" Grid.Column="1" x:Name="EntryPass" IsPassword="True"></Entry>
        <Button Text="Clear" Grid.Row="3" Grid.Column="1" Command="{Binding ClearCommand}">
        </Button>
    </Grid>

</ContentPage>

后台的Viewmodel如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;

namespace MVVMDemo
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        private string _name;
        private string _pass;
        public string UserName
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged(nameof(UserName)); 
                }
            }
        }

        public string Password
        {
            get { return _pass; }
            set
            {
                if (value != _pass)
                {
                    _pass = value;
                    OnPropertyChanged(nameof(Password));
                }
            }
        }

        public Command LoginCommand { get; set; }
        public Command ClearCommand { get; set; }

        public MainPageViewModel()
        {
            this.LoginCommand = new Command(DoLogin, () => { return CanDoLogin(); });
            this.ClearCommand = new Command(DoClear, () => { return CanDoClear(); }); 
        } 

        private void LoginCommand_CanExecuteChanged(object sender, EventArgs e)
        {

        }

        private void DoLogin()
        {
        }

        private bool CanDoLogin()
        {  
            return !(string.IsNullOrEmpty(UserName) || string.IsNullOrEmpty(Password));
        }

        private void DoClear()
        {
            this.UserName = string.Empty;
            this.Password = string.Empty;
        }

        private bool CanDoClear()
        {
            return !(string.IsNullOrEmpty(UserName) && string.IsNullOrEmpty(Password));
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

    }

    internal class ValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
            {
                return false;
            }

            return !string.IsNullOrEmpty(value.ToString());
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

不管我在界面怎么输入,按钮的状态就不像我想要的那样(和标准WPF不一样)。通过查看Xamarin.Forms的官方API,发现它的Command实现多了一个ChangeCanExecute方法,官方的说明是(在Command构造函数中):每当 canExecute 返回的值已更改,调用ChangeCanExecute()需要触发CanExecuteChanged。
也就是说需要我们手动告诉按钮(可能是手机的性能比PC要低得多,所以Xamarin.Forms没有实现WPF中的自动轮询吧)。
改造一个我的代码,在Viewmodel的构造函数中添加注册属性变更事件:

public MainPageViewModel()
        {
            this.LoginCommand = new Command(DoLogin, () => { return CanDoLogin(); });
            this.ClearCommand = new Command(DoClear, () => { return CanDoClear(); });
            this.PropertyChanged += MainPageViewModel_PropertyChanged;
        }

然后在发生发生变化时通知更新按钮的CanExecute状态。代码如下:

        private void MainPageViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            LoginCommand.ChangeCanExecute();
            ClearCommand.ChangeCanExecute();
        }

通过这样的处理,按钮的状态终于按照我的想法发生变化了。
看来学习Xamarin.Forms的MVVM时,并不能完全照搬WPF中的思路,还得多动手多研究。

目录
相关文章
|
前端开发 C# 数据格式
C#使用Xamarin开发可移植移动应用(4.进阶篇MVVM双向绑定和命令绑定)附源码
原文:C#使用Xamarin开发可移植移动应用(4.进阶篇MVVM双向绑定和命令绑定)附源码 前言 系列目录 C#使用Xamarin开发可移植移动应用目录 源码地址:https://github.
1219 0
|
网络协议 Shell Linux
【Xamarin挖墙脚系列:Android最重要的命令工具ADB】
原文:【Xamarin挖墙脚系列:Android最重要的命令工具ADB】 adb工具提供了很好的基于命令的对系统的控制。 以前说过,安卓的本质是运行在Linux上的虚机系统。在Linux中,对系统进行操作都是以命令的形式进行。
1414 0
|
7月前
|
开发工具 Android开发 iOS开发
使用xamarin开发Android、iOS报错failed to open directory: 系统找不到指定的文件
使用vs2019学习xamarin时,创建新程序。使用模拟器真机等测试都报错如下图错误: ![请在此添加图片描述](https://developer-private-1258344699.cos.ap-guangzhou.myqcloud.com/column/article/5877188/20231030-de8ce5fd.png?x-cos-security-token=r4KyZDEowPT0kGTL0LqE8EnwfN1Nzexadb05dcffed3939ff8d7591c528c01706nvpGSE93QwHpZM8NwhJNTZctNRQa0l3KDhEnqj8P7d8t
66 0
使用xamarin开发Android、iOS报错failed to open directory: 系统找不到指定的文件
|
Java C# Android开发
.NET(WinCE、WM)开发转Android开发 ——Xamarin和Smobiler对比
WinCE从1995年诞生至今,已有20多年的发展历史,行业成熟方案覆盖范围广,从车载、工控、手持机都有涉及,且方案成熟。 近些年,Android以后来居上的态势,逐渐渗透至各行业领域,硬件手持大厂也把产品线重心向Android手持迁移,基于Android的行业解决方案越来越成熟,WinCE的开发人才流失,在WinCE解决方案上吃老本的企业寻求转型。
|
Web App开发 测试技术 Android开发
xamarin开发android收集的一些工具
原文:xamarin开发android收集的一些工具 xamarin开发android收集的一些工具 工欲善其事,必先利其器,从16年下半年开始做xamarin相关的开发,平时使用的一些工具和google插件给大家分享一下,都有下载地址,持续更新。
1489 0
|
存储 数据库 Android开发
Xamarin android使用Sqlite做本地存储数据库
android使用Sqlite做本地存储非常常见(打个比方就像是浏览器要做本地存储使用LocalStorage,貌似不是很恰当,大概就是这个意思)。 SQLite 是一个软件库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。
1318 0
|
前端开发 Android开发
Xamarin android中使用signalr实现即时通讯
前面几天也写了一些signalr的例子,不过都是在Web端,今天我就来实践一下如何在xamarin android中使用signalr,刚好工作中也用到了这个,也算是总结一下学到的东西吧,希望能帮助你们,更快地熟悉使用xamarin android进行即时通讯。
1394 0
|
XML Android开发 数据格式
Xamarin android spinner的使用方法
xamarin  android spinner的如何使用呢,大多数web开发人员经常会听到DropDownList 和Combobox 这种下拉选择框,spinner 的意思差不多,有道词典一下意思是“下拉列表”、“台湾斯普”,“下拉列表组件”,“微调控件”。
1525 0
|
XML Java Android开发
Xamarin android如何反编译apk文件
Xamarin android 如何反编译 apk文件 这里推荐一款XamarinAndroid开发的小游戏,撸棍英雄,游戏很简单,的确的是有点大。等一下我们来翻翻译这个Xamarin Android 开发的小游戏 下载链接:http://shouji.
1340 0
|
定位技术 开发工具 Android开发
Xamarin android如何调用百度地图入门示例(一)
在Xamarin android如何调用百度地图呢? 首先我们要区分清楚,百度地图这是一个广泛的概念,很多刚刚接触这个名词”百度地图api”,的确是泛泛而谈,我们来看一下百度地图的官网: android上使用百度地图的有Android地图SDK,定位SDK,导航SDK,全景SDK.
1429 0