Thursday, November 3, 2022

Private Property instead of Private Member

In an earlier post I talked about boxing during calls to SetProperty. If the property passed to SetProperty is a member of a class or a column on a datarow it gets unboxed and boxed and the call to NotifyPropertyChanged occurs before the property gets updated, causing the property's value to lag behind the control's value.

I came across the third scenario where this can happen. One of my colleagues had accidently declared the backing private member as a property. This causes the same symptoms as described in the earlier post. 

Private Property _BadProperty As String
Public Property BadProperty As String
    Get
        Return _BadProperty
    End Get
    Set(value As String)
        SetProperty(_BadProperty, value)
    End Set
End Property

Here is a short demonstration of the problem. It's written in Visual Basic because the problem does not occur in C#.


<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PrivateProperty"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        d:DataContext="{d:DesignInstance local:MainWindow}"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="22"/>
            <RowDefinition Height="22"/>
            <RowDefinition Height="22"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
 
        <TextBlock Grid.Row="0" Grid.Column="0" Text="Good"/>
        <TextBox Grid.Row="1" Grid.Column="0" Text="{Binding GoodProperty, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Text="{Binding GoodProperty}"/>
 
        <TextBlock Grid.Row="0" Grid.Column="1" Text="Bad"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding BadProperty, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding BadProperty}"/>
    </Grid>
</Window>
 
---------------------------------------------------------------
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
 
Class MainWindow
    Inherits Window
    Implements INotifyPropertyChanged
 
    Private _GoodProperty As String
    Public Property GoodProperty As String
        Get
            Return _GoodProperty
        End Get
        Set(value As String)
            SetProperty(_GoodProperty, value)
        End Set
    End Property
 
    Private Property _BadProperty As String
    Public Property BadProperty As String
        Get
            Return _BadProperty
        End Get
        Set(value As String)
            SetProperty(_BadProperty, value)
        End Set
    End Property
 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    Public Function SetProperty(Of T)(ByRef storage As T, value As T, <CallerMemberName> Optional PropertyName As String = "") As Boolean
        If Object.Equals(storage, value) Then Return False
        storage = value
        NotifyPropertyChanged(PropertyName)
        Return True
    End Function
    Public Sub NotifyPropertyChanged(PropertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
    End Sub
End Class
 
If you run this demonstration and type "abc" in each text box you can see the problem.





No comments:

Post a Comment