Search code examples
c#xamlxamarinmvvm

Xamarin.Android Keeps Stopping


I am new both to MVVM pattern and Xamarin platform. Im trying to crate a bindable interface by using a view model. I started with a very simple task: Take the Entry input from the user, and send it to the label text after clicking the button.

ViewModel ---> Commands ---> EntryCommand.cs

namespace HelloWorld.ViewModel.Commands
{
    public class EntryCommand : ICommand
    {
        public ViewModel VM { get; set; }
        public EntryCommand( ViewModel vm)
        {
            VM = vm;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }
        
        public void Execute(object parameter)
        {
            VM.ChangeLabelText();
        }
    }
}

ViewModel.cs

namespace HelloWorld.ViewModel
{
    public class ViewModel: INotifyPropertyChanged
    {  
        public EntryCommand EntryCommand { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;

        public ViewModel()
        {
            EntryCommand = new EntryCommand(this);
        }

        private void OnPropertyChanged(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        public string LabelText { get; set; }
        public string Name 
        {
            get { return Name; }
            set 
            {
                Name = value;
                OnPropertyChanged("Name");
            }
        }
        public void ChangeLabelText()
        {
            if(Name!=null)
            {
                LabelText = Name;
            }
        }

    }
}

Bindable Xaml file:

MainPage.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"
             x:Class="HelloWorld.MainPage"
             xmlns:ViewModel="clr-namespace:HelloWorld.ViewModel" x:DataType="ViewModel:ViewModel">

    <ContentPage.Resources>
        <ViewModel:ViewModel x:Key="vm"/>
    </ContentPage.Resources>

    <StackLayout BindingContext="{StaticResource vm}" BackgroundColor="AliceBlue"
                 Margin="15">

        <Label Text="{Binding Name, Mode=TwoWay}"/>
        <Entry Placeholder="Enter Your Name" Text="{Binding Name, Mode=TwoWay}"/>
        <Button Text="Enter" Command="{Binding EntryCommand}"/>

    </StackLayout>

</ContentPage> 

Now, I don't get any errors but emulator keeps stopping before building the UI:

enter image description here


Solution

  • I think you're using the ViewModel incorrectly.

    Depending on your needs,there is no need to create EntryCommand: ICommand.

    You can do like this:

    public ICommand EntryCommand => new Command(DoSomething);
    
    public void DoSomething() { 
        
            // do some thing here
        }
    

    Besides, the use of Name is also incorrect, you need to create another variable (e.g. private string _name;)

        private string _name;
        public string Name
        {
            set { SetProperty(ref _name, value); }
            get { return _name; }
        }
    

    You can refer to the full sample code here:

       public class MyViewModel: INotifyPropertyChanged
        {
            public ICommand EntryCommand => new Command(DoSomething);
    
            public MyViewModel() {
                Name = "abc123";
    
            }
          
    
            public void DoSomething() { 
            
                // do some thing here
            }
    
    
    
            public string LabelText { get; set; }
    
    
            private string _name;
            public string Name
            {
                set { SetProperty(ref _name, value); }
                get { return _name; }
            }
    
    
    
            public void ChangeLabelText()
            {
                if (Name != null)
                {
                    LabelText = Name;
                }
            }
    
    
            bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
            {
                if (Object.Equals(storage, value))
                    return false;
    
                storage = value;
                OnPropertyChanged(propertyName);
                return true;
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged(string PropertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
            }
    
        }
    

    When using it in xaml, you can set the BindingContext as follows:

    <?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:testapp0211="clr-namespace:TestApp0211"
                 x:Class="TestApp0211.MainPage">
    
    
        <ContentPage.BindingContext>
            <testapp0211:MyViewModel />
        </ContentPage.BindingContext>
    
        <StackLayout  BackgroundColor="AliceBlue"
                     Margin="15">
    
            <Label Text="{Binding Name, Mode=TwoWay}"/>
            <Entry Placeholder="Enter Your Name" Text="{Binding Name, Mode=TwoWay}"/>
            <Button Text="Enter" Command="{Binding EntryCommand}"/>
    
        </StackLayout>
    </ContentPage>
    

    Note:

    It is recommended that you rename the class ViewModel (e.g. MyViewModel)to better distinguish between different viewModels.