【问题标题】:xamarin: Binding commandxamarin:绑定命令
【发布时间】:2021-02-11 07:42:03
【问题描述】:

将命令绑定到按钮 - 没有效果(Xamarin、MVVM):

注释:

  • 按下按钮,没有任何反应:没有 CanExecute 检查发生。
  • 在作为选项卡模板一部分的 ContentPage 中绑定按钮
  • 功能检查和其他相关的 MVVM 绑定运行良好:定义了点击事件并从后面的代码手动触发命令。

//有人能看出原因吗?//已编辑 编辑,新: 当 CanExecute 依赖于独立更新的复合数据类型的字段时,一个好的做法是什么? (*可以带command参数,是复合数据类型,可以通过VM直接访问command)。

视图的 xaml:

    <ContentPage.Content>
    <StackLayout>
        <Entry Placeholder="Notes"/>
        <Entry x:Name="courseIDEntry" 
               Text="{Binding CourseID, Mode=TwoWay}"
               IsReadOnly="{Binding !ExistUnit}"
               Placeholder="CourseID *"/>
        <Entry x:Name="unitIDEntry" 
               Text="{Binding UnitID, Mode=TwoWay}"
               IsReadOnly="{Binding !ExistUnit}"
               Placeholder="UnitID *"/>enter code here
        <Label Text="* Fields are mandatory"/>
        <Button x:Name="AddSave"
            Text="{Binding CommandText}" 
            Command="{Binding AddSaveCMD}"    
            CommandParameter="{Binding EdittedUnit}"/>
            <!--Clicked="AddSave_Clicked"/>--> 
    </StackLayout>
</ContentPage.Content>enter code here

视图的 c# 代码(*包括 Button-Clicked 检查)

public partial class EditUnitPage : ContentPage
{
    EditUnitViewModel editUVM;

    public EditUnitPage()
    {
        InitializeComponent();

        editUVM = new EditUnitViewModel();
        BindingContext = editUVM;
    }

    public EditUnitPage(Unit6 unitSelected) : this()
    {
        if (unitSelected != null)
        {
            editUVM.EdittedUnit = unitSelected; 
            editUVM.ExistUnit = true;
        }
    }
    protected override void OnAppearing()
    {
        base.OnAppearing();
    }

    //private void AddSave_Clicked(object sender, EventArgs e)
    //{
    //    if (editUVM.AddSaveCMD.CanExecute(editUVM.EdittedUnit))
    //    {
    //        editUVM.AddSaveCMD.Execute(null);
    //    }
    //}
}       

C# MyCommand(新手。使用 ICommand 而不是命令类)

public class AddSaveUnitCommand : ICommand
{
    public EditUnitViewModel EditUVM { get; set; }


    public event EventHandler CanExecuteChanged;

    public AddSaveUnitCommand(EditUnitViewModel euvm)
    {
       EditUVM = euvm;
    }

    public bool CanExecute(object parameter)
    {
        var editted = parameter as Unit6;

        if (editted != null )
        {
            if (!string.IsNullOrEmpty(editted.CourseID) || !string.IsNullOrEmpty(editted.UnitID))
                return true;
        }
        return false;
    }

    public void Execute(object parameterf)
    {
        EditUVM.AddSaveUnitAsync();
    }
}

用于 VM 的 c#(BaseViewModel 实现 INotify)

public class EditUnitViewModel : BaseViewModel
{
    public AddSaveUnitCommand AddSaveCMD { get; set;  }

    private Unit6 edittedUnit;
    public Unit6 EdittedUnit
    {
        get { return edittedUnit; }
        set { edittedUnit = value; OnPropertyChanged(); }
    }

    private bool existUnit;
    public bool ExistUnit
    {
        get { return existUnit; }
        set 
        { 
            existUnit = value;
            //OnPropertyChanged(); 
        }
    }

    public string CommandText
    {
        get { return ExistUnit? "Save": "Add"; }
    }

    public string CourseID
    {
        get { return EdittedUnit.CourseID; }
        set { EdittedUnit.CourseID = value; OnPropertyChanged(); }
    }

    public string UnitID
    {
        get { return EdittedUnit.UnitID; }
        set { EdittedUnit.UnitID = value; OnPropertyChanged(); }
    }

    public EditUnitViewModel()
    {
        EdittedUnit = new Unit6();
        AddSaveCMD = new AddSaveUnitCommand(this);
    }

    public async void AddSaveUnitAsync()
    {
        var curPage = App.Current.MainPage;
        try
        {
            switch (ExistUnit)
            {
                case false: //insert new unit to the DB
                    EdittedUnit.UnitKey = ""; //Todo: look for more elegant of assigning auto value to property
                    Unit6.Insert(EdittedUnit);
                    break;
                case true: //update details on existing unit
                    EdittedUnit.UnitKey = ""; //Todo: look for more elegant of assigning auto value to property
                    Unit6.Update(EdittedUnit);
                    break;
            }
            await curPage.DisplayAlert("Success", "Unit was succesffuly updateded", "OK");
        }
        catch
        {
            await curPage.DisplayAlert("Error", "Unit was not updated", "OK");
        }
        finally
        {
            EdittedUnit = null;
            await curPage.Navigation.PushAsync(new MyTabbedPage());
        } 
    }
}

TabbedPage 的 xaml:

<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
        xmlns:views="clr-namespace:P205.Views"
        x:Class="P205.Views.MyTabbedPage">

<views:UnitsPage Title="Units" />
<views:EditUnitPage x:Name="editOrAddUnit" Title="Edit U"/>
<views:DBChangesPage Title="Edit DB"/>
<views:CoursesPage Title="Course"/> 

<ContentPage Padding="10">
    
</ContentPage>

【问题讨论】:

    标签: xamarin binding command


    【解决方案1】:

    当您的 ViewModel 定义 ICommand 类型的属性时,ViewModel 还必须包含或引用一个实现 ICommand 接口的类。此类必须包含或引用 ExecuteCanExecute 方法,并在 CanExecute 方法可能返回不同值时触发 CanExecuteChanged 事件。

    所以你可以尝试如下改变:

    public class AddSaveUnitCommand : ICommand
    {
       public EditUnitViewModel EditUVM { get; set; }
    
    
       public event EventHandler CanExecuteChanged;
    
       public AddSaveUnitCommand(EditUnitViewModel euvm)
       {
       EditUVM = euvm;
       }
    
       public bool CanExecute(object parameter)
       {
          var editted = parameter as Unit6;
    
          if (editted != null )
          {
             if (!string.IsNullOrEmpty(editted.CourseID) || !string.IsNullOrEmpty(editted.UnitID))
                return true;
          }
          return false;
       }
    
      public void Execute(object parameterf)
      {
         EditUVM.AddSaveUnitAsync();
         CanExecuteChanged?.Invoke(this, EventArgs.Empty); //add this line.
      }
    }
    

    【讨论】:

    • 谢谢。 1.问题是代码甚至没有到达CanExcute:在CanExcute上使用断点,我只在应用程序启动时在View的InitializeComponent()上看到它。在运行期间和页面本身 - 代码没有到达那里。 2. 正如我所提到的 - 我已经实现并绑定了其他命令:注册并登录到具有类似功能的应用程序并且它们运行良好。 3. 我发现的一个区别是,我在 TabbedTemplate 内的 contentPage 上工作。
    • 你是不是像上面一样添加了CanExecuteChanged?.Invoke(this, EventArgs.Empty); ?当第一次在Button的Command属性上定义绑定时,当数据绑定发生某种变化时,Button调用@987654330 @ICommand 对象中的方法。我也在标签页中对其进行了测试,它有效。
    • 嗨,我试过了。它没有到达那条线。我一直在寻找,并了解到没有直接绑定到 OnPropertyChanged 通知属性。 1. 我将从这里继续:学习在仅更新复合数据类型上的字段时引发 OnPropertyChanged() 的良好做法。 2. 与最初怀疑的选项卡式模板无关 - 所以我将编辑主题。谢谢。
    • 如果可能的话,你可以分享你的项目,我会测试一下。
    猜你喜欢
    • 2019-05-11
    • 1970-01-01
    • 2017-08-18
    • 2019-07-30
    • 2015-06-21
    • 2017-04-16
    • 2021-02-02
    • 2014-09-07
    • 1970-01-01
    相关资源
    最近更新 更多