Hi I'm developing simple test app in MAUI for windows.
The problem is that label in view page doesn't change when PropertyChanged called.
And I found that when i change [PortErrorMsg] Property, i can see PropertyChanged func called but
'get' in [PortErrorMsg] Property is not called.
(Debug.WriteLine("Message for checking whether get is working properly"); <-- this is not called.)
Here is my code below
Xaml :
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:NxJig.ViewModels"
x:DataType="vm:JigViewModel"
x:Class="NxJig.Views.JigPage"
Title="JigPage">
<ContentPage.BindingContext>
<vm:JigViewModel/>
</ContentPage.BindingContext>
<VerticalStackLayout>
<HorizontalStackLayout Margin="30">
<Picker Title="Select Port" x:Name="picker1" SelectedIndexChanged="picker1_SelectedIndexChanged" FontSize="Large">
<Picker.Items>
<x:String>COM1</x:String>
<x:String>COM2</x:String>
<x:String>COM3</x:String>
<x:String>COM4</x:String>
<x:String>COM5</x:String>
<x:String>COM6</x:String>
<x:String>COM7</x:String>
<x:String>COM8</x:String>
<x:String>COM9</x:String>
<x:String>COM10</x:String>
<x:String>COM11</x:String>
<x:String>COM12</x:String>
<x:String>COM13</x:String>
<x:String>COM14</x:String>
<x:String>COM15</x:String>
<x:String>COM16</x:String>
<x:String>COM17</x:String>
<x:String>COM18</x:String>
<x:String>COM19</x:String>
<x:String>COM20</x:String>
</Picker.Items>
</Picker>
</HorizontalStackLayout>
<Label Text="{Binding PortErrorMsg, Mode=TwoWay}" IsEnabled="True" FontSize="Large"/>
</VerticalStackLayout>
</ContentPage>
Xaml behind code :
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void picker1_SelectedIndexChanged(object sender, EventArgs e)
{
ViewModel vm = new ViewModel();
vm.SerialPortName = picker1.SelectedItem as string;
}
}
ViewModel :
namespace NxJig.ViewModels
{
public class JigViewModel : INotifyPropertyChanged
{
private SerialPort serialPort;
private string serialPortName = "";
private string defaultStr = "init text";
private string successStr = "complete";
private string failStr = "check the port again";
public string portErrorMsg = defaultStr;
public event PropertyChangedEventHandler PropertyChanged;
public JigViewModel()
{
serialPort = new SerialPort();
serialPort.BaudRate = 9600;
serialPort.Parity = Parity.None;
serialPort.StopBits = StopBits.One;
serialPort.DataBits = 8;
}
public string SerialPortName
{
get => serialPortName;
set
{
if (serialPortName != value)
{
serialPortName = value;
Debug.WriteLine($"{serialPortName} is selected.");
OnPropertyChanged(nameof(SerialPortName));
serialPort.PortName = value;
if (!serialPort.IsOpen)
{
try
{
serialPort.Open();
if(serialPort.IsOpen)
{
this.PortErrorMsg = successStr;
}
}
catch
{
this.PortErrorMsg = failStr;
}
}
}
}
}
public string PortErrorMsg
{
set
{
if (portErrorMsg != value)
{
portErrorMsg = value;
Debug.WriteLine($"{portErrorMsg} is selected.");
OnPropertyChanged(nameof(PortErrorMsg));
}
}
get
{
Debug.WriteLine("Message for checking whether get is working properly");
return portErrorMsg;
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
Debug.WriteLine($"propertyName is {propertyName}");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
else
{
Debug.WriteLine("PropertyChanged is null !");
}
}
}
}
I expected the label in xaml is changed when PropertyChanged called.
CodePudding user response:
From my point of view, two ViewModels are created. One is created in your xaml:
<ContentPage.BindingContext>
<vm:JigViewModel/>
</ContentPage.BindingContext>
Another is created in your event:
private void picker1_SelectedIndexChanged(object sender, EventArgs e)
{
ViewModel vm = new ViewModel(); // i supposed it's JigViewModel, right?
vm.SerialPortName = picker1.SelectedItem as string;
}
Your view in your xaml is binded to the first one. When you trigger the picker1_SelectedIndexChanged, it just create a new ViewModel which has nothing related with your view. As a result, the PropertyChanged property has always been null and your label couldn't be changed.
Here i give you some suggestions. In your code-bind .CS
public partial class MainPage : ContentPage
{
JigViewModel vm;
public MainPage()
{
InitializeComponent();
vm = new JigViewModel();
this.BindingContext = vm;
}
private void picker1_SelectedIndexChanged(object sender, EventArgs e)
{
vm.SerialPortName = picker1.SelectedItem as string;
}
And in your xaml, Delete the following code:
<ContentPage.BindingContext>
<vm:JigViewModel/>
</ContentPage.BindingContext>
I hope my answer could help you.
CodePudding user response:
If I was on your place I would:
- Use CommunityToolkit.MVVM.
- Bind the Picker's SelectedItem to ObservableProperty with annotation.
- Write some code in partial void OnSerialPortChanged(...) if you need to.
You don't need those events. You don't need to manually call methods. You don't have to write a single line of boilerplate code.