Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

MVVM label not updating from button command

Andre_S_CAndre_S_C Member ✭✭
edited September 2020 in Xamarin.Forms

//Usual disclaimer about me being part newb, part stupid:)

I'm trying to get my head around MVVM. I have a label and a button with bindings where I want the text of the label to update when I click the button and logic happens in the viewmodel. I can see the SomeText property changing via the device log, but the label keeps showing the initial value.

in my View I have

<Label Text="{Binding SomeText}" />
<Button Text="@ Branch" Command="{Binding setSomeText}" CommandParameter="Branch"/>

& the ViewModel also attached in case it mangles here:

class CheckInViewModel : INotifyPropertyChanged
    {
        String someText;
        public String SomeText
        {
            set
            {
                if (someText != value)
                {
                    someText = value;
                    Console.WriteLine(" just set someText " + value);
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs("String"));
                    }
                }
            }
            get
            {
                return someText;
            }
        }
        public CheckInViewModel()
        {
            setSomeText = new Command<String>(
                execute: (String kind) =>
            {
                Console.WriteLine("setSomeText executes: " + kind);
                SomeText = "NEW " + kind;
                PropertyChanged(this, new PropertyChangedEventArgs("String"));
                RefreshCanExecutes();
            },
            canExecute: (String kind) =>
            {
                Console.WriteLine("canExecute return" + kind);
                return true; //todo conditional business logic
            }
                );
        }
        void RefreshCanExecutes()
        {
            (setSomeText as Command).ChangeCanExecute();
            OnPropertyChanged("set SomeText");
        }

        public ICommand setSomeText
        {
            private set; get;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            Console.WriteLine("Prop changed: " + propertyName + " " + this.SomeText); //
        }

    }

Any help much appreciated!
Thanks

Tagged:

Best Answers

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭
    Accepted Answer

    PropertyChanged(this, new PropertyChangedEventArgs("String")); this is not doing what you want it to be doing. This says "A property named String has changed".

    You probably want

    PropertyChanged(this, new PropertyChangedEventArgs(nameof(SomeText)));
    

    and to remove the second line of it at this spot

    SomeText = "NEW " + kind;
    PropertyChanged(this, new PropertyChangedEventArgs("String"));
    

    I would say you want to change your code to be more like this:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Text;
    using System.Windows.Input;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    using Xamarin.Forms.Core;
    
    namespace AgentApp
    {
        class CheckInViewModel : INotifyPropertyChanged
        {
            String someText;
            public String SomeText
            {
                set
                {
                    if (someText != value)
                    {
                        someText = value;
                        OnPropertyChanged(nameof(SomeText));
                    }
                }
                get
                {
                    return someText;
                }
            }
    
            public CheckInViewModel()
            {
                setSomeText = new Command<String>(
                    execute: (String kind) =>
                {
                    Console.WriteLine("setSomeText executes: " + kind);
                    SomeText = "NEW " + kind;
                    RefreshCanExecutes();
                },
                canExecute: (String kind) =>
                {
                    Console.WriteLine("canExecute return" + kind);
                    return true; //todo conditional business logic
                }
                    );
            }
    
            void RefreshCanExecutes()
            {
                (setSomeText as Command).ChangeCanExecute();
            }
    
            public ICommand setSomeText
            {
                get;
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName="")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                Console.WriteLine("Prop changed: " + propertyName + " " + this.SomeText); //
            }
    
        }
    }
    

    There is a using for the callermembername stuff that I can't remember off the top of my head, just import it.

Answers

  • AdamMeaneyAdamMeaney USMember ✭✭✭✭✭
    Accepted Answer

    PropertyChanged(this, new PropertyChangedEventArgs("String")); this is not doing what you want it to be doing. This says "A property named String has changed".

    You probably want

    PropertyChanged(this, new PropertyChangedEventArgs(nameof(SomeText)));
    

    and to remove the second line of it at this spot

    SomeText = "NEW " + kind;
    PropertyChanged(this, new PropertyChangedEventArgs("String"));
    

    I would say you want to change your code to be more like this:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Text;
    using System.Windows.Input;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    using Xamarin.Forms.Core;
    
    namespace AgentApp
    {
        class CheckInViewModel : INotifyPropertyChanged
        {
            String someText;
            public String SomeText
            {
                set
                {
                    if (someText != value)
                    {
                        someText = value;
                        OnPropertyChanged(nameof(SomeText));
                    }
                }
                get
                {
                    return someText;
                }
            }
    
            public CheckInViewModel()
            {
                setSomeText = new Command<String>(
                    execute: (String kind) =>
                {
                    Console.WriteLine("setSomeText executes: " + kind);
                    SomeText = "NEW " + kind;
                    RefreshCanExecutes();
                },
                canExecute: (String kind) =>
                {
                    Console.WriteLine("canExecute return" + kind);
                    return true; //todo conditional business logic
                }
                    );
            }
    
            void RefreshCanExecutes()
            {
                (setSomeText as Command).ChangeCanExecute();
            }
    
            public ICommand setSomeText
            {
                get;
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged([CallerMemberName] string propertyName="")
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                Console.WriteLine("Prop changed: " + propertyName + " " + this.SomeText); //
            }
    
        }
    }
    

    There is a using for the callermembername stuff that I can't remember off the top of my head, just import it.

  • Andre_S_CAndre_S_C Member ✭✭

    Hi @jezh ,

    Sorry it took me a while to work through your code - I am marking it as an accepted answer - I wish there was a 'above-and-beyond what was asked flag because indirectly it answers some of the questions I had but hadn't explicitly asked - thank you! - I've learned a lot from it.

    Now if I understand things correctly, with having moved the, uhm, update logic into the separate SetProperty method, I think it will make it easier to build in additional properties and/or commands in the specific viewmodel...

    And, I think I should be able to move the extension of INotifyPropertyChanged as well as the SetProperty, OnPropertyChanged and event handler into an abstract class of say GenericViewModel that can be inherited by other use-case specific ViewModels...

    Gonna give it a shot.

    Thanks again.

    @jezh said:
    I made a simple demo according to your code, and it works properly.

    ...

  • Andre_S_CAndre_S_C Member ✭✭

    making progress I think, I'm now binding to a viewmodel, but stuck on accessing the ListView's current item in the template to send as a command parameter

Sign In or Register to comment.