Click here to Skip to main content
15,064,166 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a WPF Window (MainWindow.xaml) with TextBlock that displays the result from other UserControl (SampleUserControl.xaml). I pass a String value ("You choose color Red.") to my ViewModel (ColorVM.vb) to update my TextBlock in my WPF Window. The problem is that the TextBlock does not display the value from my UserControl. Here is the code of my sample VB WPF project:

MainWindow.xaml
XML
<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>        
        <TextBlock x:Name="StatusIndicatorTextBlock" Text="{Binding MainWindowStatusIndicator}"/>
    </Grid>
</Window>


SampleUserControl.xaml
XML
<UserControl x:Class="SampleUserControl"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
            <Button x:Name="Button1" Click="Button1_Click"/>
    </Grid>
</UserControl>


ColorVM.vb
VB
Imports System.ComponentModel

Public Class ColorVM
    Implements INotifyPropertyChanged

    Private _Results As String

    Public Sub New()
        Me._Results = _Results 
    End Sub

    Public Property Results As String
        Get
            Return Me._Results 
        End Get

        Set(ByVal ResultsValue As String)
            Me._Results = ResultsValue 
            Me.NotifyPropertyChanged("MainWindowStatusIndicator")
        End Set
    End Property
    
    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

    Private Sub NotifyPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class


SampleUserControl.vb
VB
Public Class SampleUserControl

    Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
        Dim ColorVM As New ColorVM()
        ColorVM.Results = "You choose color Red."
    End Sub
End Class


Hope that you can help me in my problem because I'm a newbie in WPF development :)
Posted

1 solution

First Problem


You're binding to a property called MainWindowStatusIndicator; you're raising a PropertyChanged event for a property called MainWindowStatusIndicator; but your property is actually called Results.

If you look in the debug output window in Visual Studio, you'll see binding errors telling you that your viewmodel doesn't contain a property called MainWindowStatusIndicator.

Either change the name of the property to MainWindowStatusIndicator, or change the binding and the string in the NotifyPropertyChanged call to the correct property name.

Second Problem


Your Button1_Click method creates a new instance of your viewmodel class, sets a property on that new instance, and then throws it away.

A change to an instance property on one instance of a class will not affect any other instance of that class.

You have two options:

  1. Retrieve the current instance of the viewmodel class from the control's DataContext property, and change the property on that instance:
    VB.NET
    Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
        Dim context As ColorVM = TryCast(DataContext, ColorVM)
        If context IsNot Nothing Then
            context.MainWindowStatusIndicator = "..."
        End If
    End Sub
  2. Do it the MVVM way - remove the click event handler, and use an ICommand instead:

    You'll need an ICommand implementation which calls a function on your viewmodel. There are various options available - this is just a random one from a Google search:
    VB.NET
    ' Code from the following blog post:
    ' http://www.paulspatterson.com/mvvm-and-wpf-for-vb-net-%E2%80%93-part-5-%E2%80%93-delegating-commands/
    ' Feel free to use your own version if you have one. :)
    Public Class DelegateCommand : Implements ICommand
        Private m_canExecute As Func(Of Object, Boolean)
        Private m_executeAction As Action(Of Object)
        Private m_canExecuteCache As Boolean
    
        Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements ICommand.CanExecuteChanged
    
        Public Sub New(ByVal executeAction As Action(Of Object), ByVal canExecute As Func(Of Object, Boolean))
            Me.m_executeAction = executeAction
            Me.m_canExecute = canExecute
        End Sub
    
        Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
            Dim temp As Boolean = m_canExecute(parameter)
            If m_canExecuteCache <> temp Then
                m_canExecuteCache = temp
                RaiseEvent CanExecuteChanged(Me, New EventArgs())
            End If
            Return m_canExecuteCache
        End Function
    
        Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
            m_executeAction(parameter)
        End Sub
    End Class


    Your viewmodel then exposes a property for the ICommand implementation, which calls private methods on the viewmodel to perform the action when the button is clicked:
    VB.NET
    Public Class ColorVM : Implements INotifyPropertyChanged
     
        Private _MainWindowStatusIndicator As String
        Private _ChooseColorCommand As ICommand
     
        Public Sub New()
            Me._ChooseColorCommand = New DelegateCommand(AddressOf ChooseColor, AddressOf CanChooseColor)
        End Sub
        
        Public ReadOnly Property ChooseColorCommand As ICommand
            Get
                Return _ChooseColorCommand
            End Get
        End Property
     
        Public Property MainWindowStatusIndicator As String
            Get
                Return _MainWindowStatusIndicator 
            End Get
            Set(ByVal Value As String)
                _MainWindowStatusIndicator = Value 
                NotifyPropertyChanged("MainWindowStatusIndicator")
            End Set
        End Property
        
        Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
     
        Private Sub NotifyPropertyChanged(ByVal propertyName As String)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub
        
        Private Sub ChooseColor(param As Object)
            MainWindowStatusIndicator = "..."
        End Sub
        
        Private Function CanChooseColor(param As Object) As Boolean
            Return True
        End Function
    End Class


    Your XAML then binds to the ICommand implementation, and doesn't use the Click event at all:
    XAML
    <UserControl x:Class="SampleUserControl"
        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" 
        mc:Ignorable="d" 
        d:DesignHeight="300" d:DesignWidth="300"
    >
        <Grid>
            <Button x:Name="Button1" Command="{Binding Path=ChooseColorCommand}"/>
        </Grid>
    </UserControl>
   
v2
Comments
TJ Valencia 20-May-15 16:11pm
   
I already apply your solution but no luck. The StatusIndicatorTextBlock still not updating everytime I click the button from my UserControl.
Richard Deeming 20-May-15 16:30pm
   
Your button's click event handler also has a problem. I've updated my answer with the possible solutions.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900