Generic bindable property not working properly

55 Views Asked by At

(before starting : I'm using .NET Framework 4.8)

I've been fooling around in WPF and I was getting tired of constantly typing out properties that are used for bindings. I am now using code snippets to type out boiler plate code.

After a while, I got sick of always pasting the notify property changed code in each 'model', and I created a BindableBase, which I've learned since is kind of common practice. The class which looks like this:

public class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

    /// <summary>
    /// Sets the value of a field of type T. <br/>
    /// If new field value differs from old value, the funtion returns true. <br/> 
    /// If the value remains unchanged, the function returns false.
    /// </summary>
    /// <typeparam name="T">The type of the property / field.</typeparam>
    /// <param name="field">A reference to the field that should be updated.</param>
    /// <param name="value">The new value to be assigned to the property / field.</param>
    /// <param name="propertyName">The variable name in code. Leave empty to let the compiler do this for you.</param>
    /// <returns>True on field value change, false if field value remains the same.</returns>
    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    public delegate void PropertyValueChangedEventHandler<T>(T newValue);
}

My properties look like this

private object privateProp;
public object PublicProp
{
    get => privateProp;
    set => SetField(ref privateProp, value);
}

One thing I dislike about properties that have to call OnPropertyChanged is that they add so much code and in my opinion kind of bloat the source code. Today I tried to create a way to not have to write (generate with snippet) the whole property out everywhere.

public class BindableProperty<T> : BindableBase
{
    private T value;
    public T Value
    {
        get => value;
        set => SetField(ref value, value);
    }
}

Which I wanted to use as follows:

<StackPanel Name="PermissionsPanel">
    <CheckBox Name="ReadCheckBox" IsChecked="{Binding ReadFlag.Value}" />
    <CheckBox Name="WriteCheckBox" IsChecked="{Binding WriteFlag.Value}" />
<StackPanel />
public class Permissions
{
    BindableProperty<bool> ReadFlag = new BindableProperty<bool>();
    BindableProperty<bool> WriteFlag = new BindableProperty<bool>();
}

// ...

public class SomeWindow : Window
{
    privatye readonly Permissions permissions = new Permissions();

    public SomeWindow()
    {
        InitializeComponent();
        
        PermissionsPanel.DataContext = permissions;
    }
}

This looked like a very clean solution to not having the same boilerplate code everywhere. Sadly, it doesn't seem to work.

Can anyone tell the reason why this doesn't work? Or if, with a tweak, this could work?

2

There are 2 best solutions below

1
mm8 On

One thing I dislike about properties that have to call OnPropertyChanged is that they add so much code and in my opinion kind of bloat the source code.

You may want to consider using a source generator such as Fody. At compile time, it injects code that raises the PropertyChanged event.

Can anyone tell the reason why this doesn't work?

ReadFlag and WriteFlag must be public properties for you to be able to bind to them:

public class Permissions
{
    public BindableProperty<bool> ReadFlag { get; } = new BindableProperty<bool>();
    public BindableProperty<bool> WriteFlag { get; } = new BindableProperty<bool>();
}
1
ASh On

Your "Generic bindable property" isn't really a property, it is a field. To make a property out of it, add getters:

public class Permissions
{
    BindableProperty<bool> ReadFlag { get; } = new BindableProperty<bool>();
    BindableProperty<bool> WriteFlag { get; } = new BindableProperty<bool>();
}

you might find readymade solution in this post: Implementing INotifyPropertyChanged - does a better way exist?