Не удается привязать команду к кнопке MvvmCross

#c# #android #xamarin #mvvmcross

Вопрос:

Я пытаюсь создать кнопку, которая переходит на другую страницу в MvvmCross. Приложение загружается правильно, но при нажатии кнопки ничего не происходит.

Я использую модифицированную версию проекта Bable, которая была обновлена для использования mvvmcross 8. А также урезан, чтобы содержать только то, что я считаю минимальным для перемещения между двумя моделями представления.

Вот текущий код:

Библиотека основных классов:

FirstViewModel.cs

 using MvvmCross.Commands;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;

 namespace Babel2.Core.ViewModels {
    public class FirstViewModel : MvxViewModel
    {
        private readonly IMvxNavigationService _navigationService;
        
        public FirstViewModel(IMvxNavigationService navigationService)
        {
            _navigationService = navigationService;
        }

        public IMvxCommand GoCommand => new MvxCommand(() => _navigationService.Navigate<SecondViewModel>());

    } }
 

SecondViewModel.cs

 using MvvmCross.ViewModels;

namespace Babel2.Core.ViewModels
{
    public class FirstViewModel : MvxViewModel
    {
        private readonly IMvxNavigationService _navigationService;
        
        public FirstViewModel(IMvxNavigationService navigationService)
        {
            _navigationService = navigationService;
        }

        public IMvxCommand GoCommand => new MvxCommand(() => _navigationService.Navigate<SecondViewModel>());

    }
}
 

Приложение.cs

 using MvvmCross.IoC;
using MvvmCross.ViewModels;

namespace Babel2.Core
{
    public class App : MvxApplication
    {
        public override void Initialize()
        {
            CreatableTypes()
                .EndingWith("Service")
                .AsInterfaces()
                .RegisterAsLazySingleton();

            RegisterAppStart<ViewModels.FirstViewModel>();
        }
    }
}
 

Android Progject

FirstView.axml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <Button 
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:text="Second Page Go!"
            local:MvxBind="Click GoCommand" />    
</LinearLayout>
 

SecondView.axml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="40dp"
        android:text="Second View" />

</LinearLayout>
 

SplashScreen.axml

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Loading...." />
</LinearLayout>
 

FirstView.cs

 using Android.App;
using Android.OS;
using MvvmCross.Platforms.Android.Views;

namespace Babel2.Droid.Views
{
    [Activity(Label = "View for FirstViewModel")]
    public class FirstView : MvxActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);
            SetContentView(Resource.Layout.FirstView);
        }
    }
}
 

SecondView.cs

 using Android.OS;
using Android.Views;
using MvvmCross.Platforms.Android.Views.Fragments;

namespace Babel2.Droid.Views
{
    public class SecondView : MvxFragment
    {
        public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
        {
            base.OnCreateView(inflater, container, savedInstanceState);
            View view = inflater.Inflate(Resource.Layout.SecondView, container, false);
            return view;
        }
        
    }
}
 

LinkerPleaseInclude.cs

 using System.Collections.Specialized;
using System.Windows.Input;
using Android.App;
using Android.Views;
using Android.Widget;
using MvvmCross;

namespace Babel2.Droid
{
    [Preserve(AllMembers = true)]
    public class LinkerPleaseInclude
    {
                public void Include(Button button)
        {
            button.Click  = (s,e) => button.Text = button.Text   "";
        }

        public void Include(CheckBox checkBox)
        {
            checkBox.CheckedChange  = (sender, args) => checkBox.Checked = !checkBox.Checked;
        }
        
        public void Include(Switch @switch)
        {
            @switch.CheckedChange  = (sender, args) => @switch.Checked = !@switch.Checked;
        }

        public void Include(View view)
        {
            view.Click  = (s, e) => view.ContentDescription = view.ContentDescription   "";
        }

        public void Include(TextView text)
        {
            text.TextChanged  = (sender, args) => text.Text = ""   text.Text;
            text.Hint = ""   text.Hint;
        }
        
        public void Include(CheckedTextView text)
        {
            text.TextChanged  = (sender, args) => text.Text = ""   text.Text;
            text.Hint = ""   text.Hint;
        }

        public void Include(CompoundButton cb)
        {
            cb.CheckedChange  = (sender, args) => cb.Checked = !cb.Checked;
        }

        public void Include(SeekBar sb)
        {
            sb.ProgressChanged  = (sender, args) => sb.Progress = sb.Progress   1;
        }

        public void Include(Activity act)
        {
            act.Title = act.Title   "";
        }

        public void Include(INotifyCollectionChanged changed)
        {
            changed.CollectionChanged  = (s,e) => { var test = $"{e.Action}{e.NewItems}{e.NewStartingIndex}{e.OldItems}{e.OldStartingIndex}"; };
        }

        public void Include(ICommand command)
        {
            command.CanExecuteChanged  = (s, e) => { if (command.CanExecute(null)) command.Execute(null); };
        }
        
        // public void Include(MvvmCross.Platform.IoC.MvxPropertyInjector injector)
        // {
        //     injector = new MvvmCross.Platform.IoC.MvxPropertyInjector ();
        // } 

        public void Include(System.ComponentModel.INotifyPropertyChanged changed)
        {
            changed.PropertyChanged  = (sender, e) =>  {
                var test = e.PropertyName;
            };
        }
    }
}
 

Настройка.cs

 using Babel2.Core;
using Microsoft.Extensions.Logging;
using MvvmCross.Platforms.Android.Core;
using Serilog.Extensions.Logging;

namespace Babel2.Droid
{
    public class Setup : MvxAndroidSetup<App>
    {
        protected override ILoggerProvider CreateLogProvider()
        {
            return new SerilogLoggerProvider();
        }

        protected override ILoggerFactory CreateLogFactory()
        {
            return new SerilogLoggerFactory();
        }
    }
}
 

Брызговик.cs

 using Android.App;
using Android.Content.PM;
using MvvmCross.Platforms.Android.Views;

namespace Babel2.Droid
{
    [Activity(
        Label = "Babel2.Droid"
        , MainLauncher = true
        , Icon = "@drawable/icon"
        , Theme = "@style/AppTheme"
        , NoHistory = true
        , ScreenOrientation = ScreenOrientation.Portrait)]
    public class SplashScreen : MvxSplashScreenActivity
    {
        public SplashScreen()
            : base(Resource.Layout.SplashScreen)
        {
        }
    }
}
 

Ответ №1:

Вам нужно соединить модель представления с представлением, в вашем случае это будет:

 public class FirstView : MvxActivity<FirstViewModel>
 

И для вашего второго взгляда то же самое:

 public class SecondView : MvxFragment<SecondViewModel>
 

Не забудьте проверить репозиторий Playground, чтобы узнать, как использовать фреймворк. Кроме того, вы можете проверить репо образцов Star Wars.

С другой стороны, не забудьте добавить атрибуты презентации к каждому представлению, пожалуйста, ознакомьтесь с официальными документами о представителях Android — представлений.

Комментарии:

1. Отлично работает, я обнаружил, что secondView должен наследовать MvxActivity , а не MvxFragment