Home > OS >  C# Community Toolkit Mvvm Source Generator: ObservableProperty capitalises property name
C# Community Toolkit Mvvm Source Generator: ObservableProperty capitalises property name

Time:01-20

I'm just getting started with the toolkit and I'm trying to generate a simple ObservableProperty to use with WPF. I create a usercontrol:

<UserControl x:Class="WPF_test.StatusControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WPF_test"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />
    </Grid>
</UserControl>

and a ViewModel:

using System;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.ComponentModel;

namespace WPF_test
{
    [ObservableObject]
    public partial class StatusControlViewModel
    {
        [ObservableProperty]
        private String? testData;
    }
}

I embed the control into the MainWindow and set the datacontext in codebehind:

    public partial class MainWindow : Window
    {
        StatusControlViewModel model;
        public MainWindow()
        {
            InitializeComponent();
            model = new StatusControlViewModel();
            status.DataContext = model;
            model.testData = "test";
        }
    }

but I see that model.testData is inaccessible due to its protection level. When I comment this line out in order to get the code to run I get a binding error saying that testData cannot be found.

This is the generated code:

namespace WPF_test
{
    partial class StatusControlViewModel
    {
        /// <inheritdoc cref="testData"/>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        public string? TestData
        {
            get => testData;
            set
            {
                if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
                {
                    OnTestDataChanging(value);
                    OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
                    testData = value;
                    OnTestDataChanged(value);
                    OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
                }
            }
        }

        /// <summary>Executes the logic for when <see cref="TestData"/> is changing.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
        partial void OnTestDataChanging(string? value);
        /// <summary>Executes the logic for when <see cref="TestData"/> just changed.</summary>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.0.0.0")]
        partial void OnTestDataChanged(string? value);
    }
}

It seems that the toolkit is capitalising my property name. I can make the databinding work by capitalising the property name in the control XAML:

<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />

and similarly access the model property:

model.TestData = "test";

Is there a way to use the toolkit so that the property is accessed in the original form, i.e.

<TextBox x:Name="txtTest" Text="{Binding testData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />

not

<TextBox x:Name="txtTest" Text="{Binding TestData}" Grid.Column="0" Grid.Row="0" Margin="5,5,5,5" />

? I think it's going to be confusing otherwise.

CodePudding user response:

No.

There is no way "to use the toolkit so that the property is accessed in the original form"

Because that's a field and not a property.

The code generator is literally generating some code.

The property name cannot be the same as your backing field. testData is a field. You cannot bind to a field.

    [ObservableProperty]
    private String? testData;

Generates a TestData property in the partial class.

    public string? TestData
    {
        get => testData;
        set
        {
            if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(testData, value))
            {
                OnTestDataChanging(value);
                OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.TestData);
                testData = value;
                OnTestDataChanged(value);
                OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.TestData);
            }
        }
    }

You can only bind to public properties so you need what that generates in order to bind. No property means no binding.

Binding:

https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-6.0

Property:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties

In the above _seconds is a field.

Hours is a property.

The difference is properties have getters and (optional) setters.

Having said all that.

You are not forced to use that attribute.

You can create your properties manually.

Then you can have whatever case you like for your property.

I suggest you learn to like upper case properties though.

I've worked for a lot of clients. The standard for property names has always been to start with an upper case letter.

PS

[Relaycommand] generates an upper class property with "Command" appended.

  • Related