1

In a User Control, I am trying to get a Command to modify a Property. I have an IncrementValueCommand and a Value property that I want to increment when a button is clicked. The button's Command is bound to the IncrementValueCommand and the Content is bound to the Value property.

I have tried two approaches to do this and in both cases the Button doesn't show the Value incrementing..

1st Approach: Dependency Property for Value

XAML:

<UserControl x:Class="UserControl1"
             x:Name="root"
             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:WpfApp1"
             mc:Ignorable="d"
             d:DesignHeight="100"
             d:DesignWidth="200"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <Button Content="{Binding Path=Value}"
            Command="{Binding Path=IncrementValueCommand}" />

</UserControl>

Code behind:

Public Class UserControl1

    Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(UserControl1), New PropertyMetadata(1))

    Public Property IncrementValueCommand As ICommand

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)

    End Sub

    Public Property Value() As Integer
        Get
            Return GetValue(ValueProperty)
        End Get
        Set(value As Integer)
            SetValue(ValueProperty, value)
        End Set
    End Property

    Private Sub IncrementValue()
        Value += 1
    End Sub

End Class

2nd approach: INotifyPropertyChanged for Value

XAML:

<UserControl x:Class="UserControl2"
             x:Name="root"
             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:WpfApp1"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             mc:Ignorable="d"
             d:DesignHeight="100"
             d:DesignWidth="200"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">

    <Button Content="{Binding Path=Value}"
            Command="{Binding Path=IncrementValueCommand}" />

</UserControl>

Code behind:

Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Public Class UserControl2
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private _value As Integer = 1
    Public Property IncrementValueCommand As ICommand

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.
        IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)

    End Sub

    Public Property Value() As Integer
        Get
            Return _value
        End Get
        Set(value As Integer)
            If _value <> value Then
                _value = value
                NotifyPropertyChanged()
            End If
        End Set
    End Property

    ' This method is called by the Set accessor of each property.  
    ' The CallerMemberName attribute that is applied to the optional propertyName  
    ' parameter causes the property name of the caller to be substituted as an argument.  
    Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Private Sub IncrementValue()
        Value += 1
    End Sub

End Class

I left out the RelayCommand class which is a standard implementation for ICommand.

Any help will be greatly appreciated.


Working Code (thanks to Peter Duniho for his answer)

Adjust the code-behind constructor by creating the IncrementValueCommand first:

Public Sub New()

    ' Add any initialization after the InitializeComponent() call? Nah
    IncrementValueCommand = New RelayCommand(AddressOf IncrementValue)

    ' This call is required by the designer.
    InitializeComponent()

End Sub
Étienne Laneville
  • 4,697
  • 5
  • 13
  • 29

1 Answers1

1

As I've explained in this comment, the problem in this particular variant of your attempts to use a command to update a value is that you are initializing the IncrementValueCommand property after the call to InitializeComponent() in the class constructor.

The InitializeComponent() call is where the binding to that property is set up, i.e. Command="{Binding Path=IncrementValueCommand}" in your XAML. When that call is made, the property still has its default value of null.

When you assign the property a value later, because the property is an auto-implemented property, there's nothing about that assignment that would cause a property-change notification to happen, so the binding is never updated to reflect the new value.

You can either implement property-change notification for that property, just as is already done for the Value property, or you can (as I had suggested earlier) move the assignment within the constructor so that it occurs before the call to InitializeComponent instead of after.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • That worked, thanks a lot for all your help with this. You've clarified a lot of things. One note is that when you type `Sub New()`, Visual Studio adds `InitializeComponent` for you and leads you to believe you should be typing your code underneath, especially with the comment it adds: `' Add any initialization after the InitializeComponent() call.` Also, in this question, nothing is read-only. – Étienne Laneville Sep 23 '19 at 21:47
  • @ÉtienneLaneville: Yes, the IDE is a bit less than helpful in this case, but in its defense, in WPF it's expected that all properties will include property-change notification, so usually it doesn't matter where the property is set. As for this: _" in this question, nothing is read-only"_ -- ah, that's right. 99.94% of my .NET work is done in C#, and I've mis-read the auto-implemented property in VB.NET. I forgot that you don't have to declare the setter in such cases for it to exist. – Peter Duniho Sep 23 '19 at 21:54