Is it okay to include methods in a view model or should they be placed in another part of the code?

Viewed 208

My Xamarin Forms ViewModel looks like this:

public class CFSPageViewModel : BaseViewModel
{
    #region Constructor

    public CFSPageViewModel()
    {
        PTBtnCmd = new Command<string>(PTBtn);
        OnTappedCmd = new Command<string>(OnTapped);
    }

    #endregion

    # region Commands

    public ICommand PTBtnCmd { get; set; }
    public ICommand OnTappedCmd { get; }

    #endregion

    #region Methods

    private void OnTapped(string btnText)
    {
        Utils.SetState(btnText, CFS, SET.Cfs);
        CFSMessage = Settings.cfs.TextLongDescription();
    }

    private void PTBtn(string btnText)
    {
        Utils.SetState(btnText, PT);
        SetLangVisible(btnText);
        SetLangSelected(btnText);
        CFSMessage = Settings.cfs.TextLongDescription();
    }


}

I was previously sending a message with MessageCenter to my C# back end code but now have removed MessageCenter so the methods are part of the ViewModel.

Is this a safe thing to do? I heard that MessageCenter messages passing around between ViewModels for everything was not the best of solutions.

Note that here is the way I had been doing it before:

MyPageViewModel.cs

PTBtnCmd = new Command<Templates.WideButton>((btn) =>             
              MessagingCenter.Send<CFSPageViewModel, Templates.WideButton>(
              this, "PTBtn", btn));

MyPage.xaml.cs

MessagingCenter.Subscribe<CFSPageViewModel, Templates.WideButton>(
        this, "PTBtn", (s, btn) =>
        {
            Utils.SetState(btn.Text, vm.PT);
            SetLangVisible(btn.Text);
            SetLangSelected(btn.Text);
            vm.CFSMessage = Settings.cfs.TextLongDescription();
        });

Note that methods such as SetLangVisible were also in MyPage.xaml.cs

1 Answers

To add an event handler to your Buttonsimply:

<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    x:Class="MyProject.Views.MyPage">
    <ContentPage.Content>
        <StackLayout>
            <Button Text="PTBtn" Clicked="Handle_Clicked" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

In code behind:

namespace MyProject.Views
{
    public partial class MyPage : ContentPage
    {
        public MyPage()
        {
            InitializeComponent();
        }
        void Handle_Clicked(object sender, EventArgs eventArgs)
        {
            ((Button)sender).BackgroundColor = Color.Blue; // sender is the control the event occured on

            // Here call your methods depending on what they do/if they are view related
            /*
            Utils.SetState(btn.Text, vm.PT);
            SetLangVisible(btn.Text);
            SetLangSelected(btn.Text);
            vm.CFSMessage = Settings.cfs.TextLongDescription();
            */
        }
    }
}

All the events that can have a event handler assigned to it is listed in yellow with E: enter image description here


The Command fires first and you can add CanExecute as a second parameter in the constructor - which will also stop both the command and the event handler from being executed.

I would also rename the Command to something like SelectLanguageCommand - to distinguish it from a ui action. That way you can disconnect the button from the command and connect the command to other ui - if you decide you want to change the view in the future. It would also be easier to understand when unit testing.


Is this a safe thing to do? I heard that MessageCenter messages passing around between ViewModels for everything was not the best of solutions.

You could register all your view models with DependencyService

public App()
{
    InitializeComponent();

    DependencyService.Register<AboutViewModel>();
    DependencyService.Register<CFSPageViewModel>();
    DependencyService.Register<MyPageViewModel>();
    MainPage = new AppShell();
}

Set BindingContext of the views to the instances registered:

public AboutPage()
{
    InitializeComponent();
    BindingContext = DependencyService.Get<AboutViewModel>();
}

And get the ViewModel instance anywhere you need it. That way you don't have to deal with subscriptions as you need to when using MessagingCenter.

Wether it is safe or not - I am not sure.

Related