Matt S.
Matt S.

Reputation: 323

Update UI on Value Change from ViewModel

I'm having a problem getting my UI to update from the ViewModel. Basically, I have a text entry that takes in a zip code. Upon change of that Property, I call the USPSService to get the city and state and fill in those Properties programmatically, which it does, but the UI never updates. I have a 'main' View Model, that has a property of another class that has the fields.

public partial class AddCustomerViewModel : ObservableObject
{
    USPSService _uspsService;
    public AddCustomerViewModel(USPSService uspsService)
    {
        DataFormObject = new AddCustomerDataFormObject();
        DataFormObject.PropertyChanged += DataFormObject_PropertyChanged;
        Title = "Customer Information";
        CurrentLocation = ApplicationState.DefaultLocation;
        _uspsService = uspsService;
    }

    private async void DataFormObject_PropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        var dataObject = sender as AddCustomerDataFormObject;
        var propertyName = e.PropertyName;
        if (propertyName == "ZipCode")
        {
            await GetZipCodeInformation();
        }
    }

    public AddCustomerViewModel() { }

    public AddCustomerDataFormObject DataFormObject { get; set; }

    public async Task GetZipCodeInformation()
    {
        string zipCode = DataFormObject.ZipCode;
        if (zipCode.Length == 5)
        {
            var info = await _uspsService.GetZipCodeInformation(zipCode);
            if (info != null)
            {
                DataFormObject.City = info.city;
                DataFormObject.State = info.state;                    
            }
        }
    }    
}

public class AddCustomerDataFormObject : ObservableObject
{

    public AddCustomerDataFormObject() {}

    string addressLine1 = string.Empty;
    string addressLine2 = string.Empty;
    string city = string.Empty;
    string state = string.Empty;
    string zipCode = string.Empty;

   
    public string AddressLine1 { get { return addressLine1; } set { addressLine1 = value; } }
    public string AddressLine2 { get { return addressLine2; } set { addressLine2 = value; } }
    public string City 
    { 
        get { return city; } 
        set { 
            if (city != value)
            {
                SetProperty(ref city, value);
            }
        } 
    }
    public string State 
    { 
        get { return state; } 
        set { 
            if (state != value)
            {
                SetProperty(ref state, value);
            }
        } 
    }

    [RegularExpression(pattern: "^(\\d{5})?$", ErrorMessage = "Invalid Zip Code")]
    public string ZipCode 
    { 
        get { return zipCode; } 
        set {
            if (zipCode != value)
            {
                SetProperty(ref zipCode, value);
            }
        } 
    }
}

I have the AddCustomerViewModel as the BindingContext for the view, and then the AddCustomerDataFormObject as the Binding object to the DataForm (Syncfusion). This won't work with Entries or Labels either, so not thinking its Syncfusion.

Here's the 'code behind' of my XAML page:

public partial class AddCustomerMain : ContentPage
{
    AddCustomerViewModel _viewModel;
    public AddCustomerMain(AddCustomerViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
        _viewModel = viewModel;
        _viewModel.DataFormObject.PropertyChanged += DataFormObject_PropertyChanged;
    }

    private async void DataFormObject_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        var dataItem = sender as AddCustomerDataFormObject;
        if (e.PropertyName == "State")
        {
            this.StateDataFormTextItem.LabelText = dataItem.State;
        }    
    }
}

Thoughts? I've tried running the portion of setting the Label Text on the MainThread, but that didn't make a difference. If I look at the properties during debugging, they are all being updated, but the UI isn't showing any changes.

UPDATE: Here's the XAML

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="ShowroomApp.View.AddCustomerMain"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:core="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
    xmlns:dataform="clr-namespace:Syncfusion.Maui.DataForm;assembly=Syncfusion.Maui.DataForm"
    xmlns:inputs="clr-namespace:Syncfusion.Maui.Inputs;assembly=Syncfusion.Maui.Inputs"
    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:vm="clr-namespace:ShowroomApp.ViewModel"
    Title="{Binding Title}">
    <ContentPage.BindingContext>
        <vm:AddCustomerViewModel />
    </ContentPage.BindingContext>
    <ScrollView>
        <Grid
            Padding="10,20"
            ColumnDefinitions="Auto,*"
            RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"
            RowSpacing="10">
            <HorizontalStackLayout
                Grid.Row="0"
                Grid.Column="0"
                Grid.ColumnSpan="2"
                Margin="0,0,0,30"
                HorizontalOptions="Center">
                <Border
                    HeightRequest="100"
                    Stroke="#b3d9ff"
                    StrokeShape="RoundRectangle 10"
                    StrokeThickness="4"
                    WidthRequest="650">
                    <Border.Shadow>
                        <Shadow
                            Brush="Black"
                            Opacity=".8"
                            Offset="5,5" />
                    </Border.Shadow>
                    <VerticalStackLayout HorizontalOptions="Center">
                        <Label
                            Margin="5,5,5,0"
                            Style="{StaticResource Headline}"
                            Text="{Binding CurrentLocation.Headline1}" />
                        <Label
                            Margin="5,5,5,40"
                            Style="{StaticResource SubHeadline}"
                            Text="{Binding CurrentLocation.Headline2}" />
                    </VerticalStackLayout>
                </Border>
            </HorizontalStackLayout>

            <dataform:SfDataForm
                x:Name="CustomerDataForm"
                Grid.Row="1"
                Grid.Column="0"
                Grid.ColumnSpan="2"
                AutoGenerateItems="False"
                DataObject="{Binding DataFormObject}"
                HeightRequest="600"
                LayoutType="TextInputLayout">
                <dataform:SfDataForm.TextInputLayoutSettings>
                    <dataform:TextInputLayoutSettings
                        ContainerType="Outlined"
                        FocusedStroke="{StaticResource TextBoxBorderFocused}"
                        Stroke="{StaticResource TextBoxBorder}" />
                </dataform:SfDataForm.TextInputLayoutSettings>
                <dataform:SfDataForm.Items>
                    <dataform:DataFormGroupItem
                        Name="Address"
                        ColumnCount="3"
                        HeaderBackground="{StaticResource TextBoxBackground}">
                        <dataform:DataFormGroupItem.Items>
                            <dataform:DataFormTextItem
                                ColumnSpan="3"
                                FieldName="AddressLine1"
                                LabelText="Address Line 1" />
                            <dataform:DataFormTextItem
                                ColumnSpan="3"
                                FieldName="AddressLine2"
                                LabelText="Address Line 2" />
                            <dataform:DataFormTextItem FieldName="ZipCode" LabelText="ZipCode" />
                            <dataform:DataFormTextItem IsReadOnly="True" LabelText="City" />
                            <dataform:DataFormTextItem
                                x:Name="StateDataFormTextItem"
                                FieldName="State"
                                IsReadOnly="True"
                                LabelText="State" />
                        </dataform:DataFormGroupItem.Items>
                    </dataform:DataFormGroupItem>
                </dataform:SfDataForm.Items>
            </dataform:SfDataForm>

            <Button
                Grid.Row="2"
                Grid.Column="0"
                Grid.ColumnSpan="2"
                Margin="0,0,0,0"
                Clicked="Button_Clicked"
                HorizontalOptions="Center"
                SemanticProperties.Hint="Register as a Customer"
                Text="Next &gt;&gt;" />
        </Grid>
    </ScrollView>
</ContentPage>

Upvotes: 0

Views: 139

Answers (1)

user23986143
user23986143

Reputation: 1

As of now, the MAUI SfDataForm control will automatically update the data object value when changes are made in the editor view. For dynamic model changes, the PropertyChanged event can be utilized for the data object to update the editor with the help of UpdateEditor method. Please refer to the following code snippet for reference:


public partial class MainPage : ContentPage

{

   public MainPage()

   {

       InitializeComponent();

       (this.dataForm.DataObject as ContactInfo).PropertyChanged += MainPage_PropertyChanged ;

   }



   private void MainPage_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)

   {

       if (!string.IsNullOrEmpty(e.PropertyName))

       {

           dataForm.UpdateEditor(e.PropertyName);

       }

   }

}

By subscribing to the PropertyChanged event of data object (ContactInfo in this example), it can be updated as the editor in response to dynamic changes in the respective model.

Please see the code snippets below for the entire implementation:

MainPage.xaml

<dataForm:SfDataForm x:Name="dataForm" DataObject="{Binding ContactInfo}" Grid.Row="1" GenerateDataFormItem="dataForm_GenerateDataFormItem"  AutoGenerateItems="False">

            <dataForm:SfDataForm.Items>

                <dataForm:DataFormTextItem FieldName="FirstName" />

                <dataForm:DataFormTextItem FieldName="LastName" />

                <dataForm:DataFormTextItem FieldName="PhoneNumber" />

            </dataForm:SfDataForm.Items>

</dataForm:SfDataForm>

MainPage.xaml.cs:


public partial class MainPage : ContentPage

{

    public MainPage()

    {

        InitializeComponent();

        (this.dataForm.DataObject as ContactInfo).PropertyChanged += MainPage_PropertyChanged ;

    }

 

    private void MainPage_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)

    {

        if (!string.IsNullOrEmpty(e.PropertyName))

        {

            dataForm.UpdateEditor(e.PropertyName);

        }

    }

}

Model class:


public class ContactInfo : INotifyPropertyChanged

{

        private string phoneNumber;

        private string firstName;

        private string lastName;

        public ContactInfo()

        {

 

        }

        public String FirstName

        {

            get { return firstName; }

            set

            {

                if (value != firstName)

                {

                    firstName = value;

                    this.RaisePropertyChanged("FirstName");

                }

            }

        }

 

        public String LastName

        {

            get { return lastName; }

            set

            {

                if (value != lastName)

                {

                    lastName = value;

                    this.RaisePropertyChanged("LastName");

                }

            }

        }
 

        public String PhoneNumber

        {

            get { return phoneNumber; }

            set

            {

                if (value != phoneNumber)

                {

                    phoneNumber = value;

                    this.RaisePropertyChanged("PhoneNumber");

                }

            }

        }

 

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(String PropertyName)

        {

            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));

        }
}


ViewModel class:

public class DataFormViewModel : INotifyPropertyChanged

{

        private ContactInfo contactInfo;

 

        public DataFormViewModel()

        {

            this.ContactInfo = new ContactInfo();

        }

 

        public ContactInfo ContactInfo

        {

            get { return this.contactInfo; }

            set

            {

                this.contactInfo = value;

                this.RaisePropertyChanged("ContactInfo");

            }

        }

 

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(String Name)

        {

            if (PropertyChanged != null)

                this.PropertyChanged(this, new PropertyChangedEventArgs(Name));

        }

}

Upvotes: 0

Related Questions