Updating label binding with property isn't setting right the first time - wpf

I have a simple application, and i'm trying to get a grasp on binding, and properties.
What I'm trying to do:
Load a modal window, from the main window. I know this blocks all access to it, so I'm trying to update a label on the modal window through a property binding.
I set the datacontext:
DataContext = busyupdate
InitializeComponent()
Set the variable:
Property busyupdate As BusyWindowGetSet = New BusyWindowGetSet
Then I bind the label to the current context:
Content="{Binding Stage, UpdateSourceTrigger=PropertyChanged}"
Content="{Binding Description, UpdateSourceTrigger=PropertyChanged}"
Here is my BusyWindowGetSet class, where I have 2 properties... Stage and Description. It implements INotifyPropertyChanged
Imports System.ComponentModel
Public Class BusyWindowGetSet
Implements INotifyPropertyChanged
Public Property Stage() As String
Get
Return m_stage
End Get
Set(value As String)
m_stage = value
NotifyPropertyChanged("Stage")
End Set
End Property
Private Shared m_Stage As String
Public Property Description() As String
Get
Return m_description
End Get
Set(value As String)
m_description = value
NotifyPropertyChanged("Description")
End Set
End Property
Private Shared m_Description As String
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
#End Region
#Region "Private Helpers"
Public Sub NotifyPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
End Class
When I load the "busy window" the labels are not updated.. but If I load the window a second time, they show up and change like normal.
This is how I'm setting the properties...
busyupdate.Stage = String.Format("Stage: {0}", "Warming up1")
busyupdate.Description = String.Format("Description: {0}", "Warming up1")
What am I missing here?
Update for Mark:
Dim mm As New BusyWindow
mm.ShowDialog()
busyupdate.Stage = String.Format("Stage: {0}", "Warming up1")
busyupdate.Description = String.Format("Description: {0}", "Warming up1")

Related

Binding doesn't get called on code behind propertry

So I have a Grid with some checkboxes, etc inside it and wanted to set them all to readonly , I added the IsEnabled part below:
<Grid IsEnabled="{Binding IsFieldReadOny}">
And in the code behind added this:
Private _isFieldReadOnly As Boolean = True
Public Property IsFieldReadOny() As Boolean
Get
Return _isFieldReadOnly
End Get
Set(value As Boolean)
_isFieldReadOnly = value
End Set
End Property
But when I put breakpoint, it does not get hit or do anything.
If I manually hard code a True for the grid, then it works.
I am new to both WPF and VB syntax, so it might be something easy that I am not doing right.
Here is a very simple example of MVVM and binding with one way out of TONS to do things. Binding in and of itself has many many options of traversing a visual tree with 'RelativeSource' and scoping. As well as mode options and other settings. I chose to focus on keeping it simple though. I just want a view that has a textbox, you can change yourself, a button you can hit, a label that will update from the text you changed.
So here is a basic view:
<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:SimpleWPF"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox Text="{Binding Text}" Height="30" />
<Button Content="Example" Command="{Binding DoCommand}" />
<Label Content="{Binding Output}" Height="30" />
</StackPanel>
</Window>
I want to set up a single helper class for a 'DelegateCommand'. You can do this many ways but essentially I am saving repeat methods for later reuse for commands to help with an ICommand interface.
Public Class DelegateCommand(Of T)
Implements ICommand
Private _execute As Action(Of T)
Public Sub New(execute As Action(Of T))
_execute = execute
End Sub
Public Event CanExecuteChanged As EventHandler
Private Event ICommand_CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
Private Function ICommand_CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
Return True
End Function
Private Sub ICommand_Execute(parameter As Object) Implements ICommand.Execute
_execute.Invoke(DirectCast(parameter, T))
End Sub
End Class
Now in my Code behind of the view it should be pretty minimal except this:
Class MainWindow
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = New MainViewModel()
End Sub
End Class
And my MainViewModel is pretty simple in this case is pretty simple and I am only implementing INotifyPropertyChanged. I would usually do most of my stuff like this in a base class and inherit that on larger solutions.
Imports System.ComponentModel
Public Class MainViewModel
Implements INotifyPropertyChanged
Private _text As String
Public Property Text As String
Get
Return _text
End Get
Set(ByVal value As String)
_text = value
OnPropertyChanged(NameOf(Text))
End Set
End Property
Private _output As String
Public Property Output As String
Get
Return _output
End Get
Set(ByVal value As String)
_output = value
OnPropertyChanged(NameOf(Output))
End Set
End Property
Public Sub New()
_text = "Test"
End Sub
Public ReadOnly Property DoCommand As New DelegateCommand(Of Object)(AddressOf DoIt)
Private Sub DoIt(obj As Object)
Output = $"{Text} {DateTime.Now.ToLongDateString}"
End Sub
#Region "Implement INotifyProperty Changed"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Sub OnPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
#End Region
End Class
When you use a generic binding you are looking at the DataContext of the object and by generic I mean a {Binding PropertyName} without any other parameters. In order to bind to a property within your code behind (which I don't recommend doing) then you need to tell the binding to look at that location. You also need to use a dependency property for binding on UIElements since it's already built in.
So to make this work I've named the Window the Grid is in 'window'. Then I've given the binding expression a direct connection to the property.
<Grid IsEnabled="{Binding IsReadOnlyField, ElementName=window}" />
I've then added a binding to the Checkbox as well to the same thing.
<CheckBox Content="Is Grid Enabled" IsChecked="{Binding IsReadOnlyField, ElementName=window}" />
Then in the code behind I've changed the property to a DependencyProperty.
public bool IsReadOnlyField
{
get { return (bool)GetValue(IsReadOnlyFieldProperty); }
set { SetValue(IsReadOnlyFieldProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyFieldProperty =
DependencyProperty.Register(nameof(IsReadOnlyField), typeof(bool), typeof(MainWindow));
This will get the binding working.
If you are not using the code behind and are binding to a ViewModel or any class you should preferably make that class interface INotifyPropertyChanged (although you can also make that ViewModel inherit from DependencyObject and use the same DependencyPropery... It's just normally used for UI elements). Then write the property as normal and in the setter call the property changed event. However, you will most likely set the binding back to the way you had it and just put that ViewModel as the DataContext.
There's A LOT to explain about binding as it can be very flexible and used many different ways. Once you get it though you got it and learning more ways to bind will be simple. I suggest learning exactly how the binding takes place so that you can manipulate and choose the best binding for any situation.

vb.net programmatically added binding not working

I am trying to bind a variable value to a button's content property. i created a button named "button" inside a dockpanel of my main window in XAML.
<Button x:Name="button" Content="Button" Height="100"
VerticalAlignment="Top" Width="75"/>
Then I want to add a binding to a public variable test programmatically.
The initial value (400) is displayed correctly at runtime, but when I hit the "NextTurn" button to raise the Click event, the bound value isn't updated.
Imports System.Windows.Data
Class MainWindow
Public test As Integer
Public Sub New()
InitializeComponent()
Dim usr As New UserNS.User
mainUser = usr
test = 400
Dim btest As New Binding()
btest.Source = test
button.SetBinding(Button.ContentProperty, btest)
End Sub
Private Sub NextTurn_Click(sender As Object, e As RoutedEventArgs) Handles NextTurn.Click
test = test - 10
End Sub
End Class
Could you please help me?
Thank you very much!
First of all, fields cannot be bound, only properties.
The binding source should be an object which has the property you would like to bind.
Ideally it is not the form class itself but a separate class (aka. view model).
E.g. the main window (named MainWindow) can have a view model named MainViewModel.
This object must implement the INotifyPropertyChanged interface.
In the property setter you have to call a method which raises the PropertyChanged event that comes with INotifyPropertyChanged interface.
In my example it is:
Private Sub NotifyPropertyChanged(...)
IMPORTANT: VB.NET works in case-insensitive mode so avoid naming a Button control as button. Also if you implement a full property the backing field should have a different name. You cannot have a test field and a Test property at the same time. That's why I chose the _Test name for the field.
Here is a working example:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Class MainWindow
Implements INotifyPropertyChanged
Public Sub New()
' Actually we can initialize the Test property here as well.
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Test = 400
Dim bindingTest As New Binding() With {
.Source = Me, ' The object which has the property we want to bind.
.Mode = BindingMode.OneWay, ' We declare that the UI will accept changes from the object's property but not vica-versa.
.Path = New PropertyPath("Test") 'We have to pass the name of the property as a String value.
}
TestButton.SetBinding(Button.ContentProperty, bindingTest)
' We could also initialize the Test property here.
End Sub
' We can also initialize only the field instead of the property
' But new values must be set through the property setter.
Private _Test As Integer
Public Property Test() As Integer
Get
Return _Test
End Get
Set(ByVal value As Integer)
_Test = value
NotifyPropertyChanged()
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
' We use CallerMemberName attribute so that we do not need to pass the name of the property.
' The compiler will automatically pass the name of the caller property. In our case: "Test"
' To get it work we declare the parameter as Optional so that we really do not have to pass a parameter value.
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = "")
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Private Sub NextTurnButton_Click(sender As Object, e As RoutedEventArgs)
' You must set the property's value instead of the backing field!
Test = Test - 10
End Sub
End Class

VB.net WPF DataGrid ObservableCollection Binding property update

I am using VB.NET and WPF within Visual Studio 2010 Express.
Currently, I have:
A DataGrid by the name of downloadListDG. This has a column which is a template containing an image.
An ObservableCollection of a custom DownloadListItem class.
This DownloadListItem has a public property which is another custom class.
This class has a private dim which is a StateType (a custom enum), and a public readonly property which returns a string depending on what the StateType is (actually an image URI if you're curious).
The DownloadListItem also has a public property which just returns the StateType (this is just for binding purposes)
My problem is that whenever the StateType changes, the image column in the DataGrid does not change. I have been trying to use the IPropertyChangedNofity, but nothing changes, so either I'm using it incorrectly or I need to use another method.
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
AddHandler ControllerRef.StateChanged, AddressOf StateChangeHandler
Private Sub StateChangeHandler(NewState As State)
MsgBox(NewState)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("CurrentState"))
End Sub
Thanks in advance
Make sure the PropertyChanged event is notifying the UI of the property name you are bound to, not the property that triggers the change. Example:
Imports System.ComponentModel
Public Class DownloadListItem : Implements INotifyPropertyChanged
Friend Enum StateEnum
State1 = 0
State2 = 1
End Enum
Private _CurrentState As StateEnum
Private Sub ChangeEnumValue(NewValue As StateEnum)
_CurrentState = NewValue
OnPropertyChanged("ImageURI")
End Sub
Public ReadOnly Property ImageURI As String
Get
' TODO: Implement conditional logic to return proper value based on CurrentState Enum
End Get
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
End Class

Wpf MVVM UI do not update implementing modularity,eventaggregator

i'm currently experimenting wpf communication between two views of different modules using prism IEventAggregator. publishing module and subscribing module is working fine, for some reason i can't understand why the subscriber UI is not updating.i place a button to display a msgbox at the subscriber module just to be sure that it receives it and it does. and i think i properly implement the INotifyPropertyChanged.
if i place subscribe in subscriber view's code-behind it works as i want it to be....do i do it the wrong way? please correct me. thanks.
a separate class for module message passing. this class is from this post http://www.shujaat.net/2010/12/wpf-eventaggregator-in-prism-40-cal.html
Public Class SendServices
Public Shared Property SendMessage As EventAggregator
Shared Sub New()
SendMessage = New EventAggregator
End Sub
End Class
Publisher :
Public Class Module1ViewModel
Private _msgsend As String
Public WriteOnly Property MessageSend As String
Set(value As String)
_msgsend = value
End Set
End Property
Public Sub Send()
SendServices.SendMessage.GetEvent(Of SendStringEvent).Publish(New SendString With {.Name = _msgsend})
End Sub
End Class
Subscriber :
Public Class Module2ViewModel
Implements INotifyPropertyChanged
Private _receivedMSG As String
Public Property ReceivedMSG As String
Get
Return _receivedMSG
End Get
Set(value As String)
_receivedMSG = value
OnPropertyChanged("ReceivedMSG")
End Set
End Property
'Binded to subscriber View button using interactions
Public Sub Received()
MsgBox(ReceivedMSG)
End Sub
Private Sub ReceivedMessage(msg As SendString)
_receivedMSG = msg.Name
End Sub
Public Sub New()
SendServices.SendMessage.GetEvent(Of SendStringEvent)().Subscribe(AddressOf ReceivedMessage, ThreadOption.UIThread, False)
End Sub
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class
Subscriber View code-behind
Public Class Module2View
Sub New()
InitializeComponent()
Me.DataContext = New Module2ViewModel
End Sub
End Class
and the binding part to display the message
<TextBox Height="23" HorizontalAlignment="Left" Margin="111,39,0,0" Name="TextBox1" VerticalAlignment="Top" Width="158" Text="{Binding Path=ReceviedMSG}"/>
You don't fire the OnPropertyChanged event because you are directly setting the field _receivedMSG in your handler which is bypassing the property setter which fires the event.
So you should use the property setter instead:
Private Sub ReceivedMessage(msg As SendString)
ReceivedMSG = msg.Name
End Sub

WPF distinguish between coding-SelectionChanged and the mouse-SelectionChanged

I have a machiavellian question (for me).
In my WPF application I have a ListBox that has in the ItemTemplate a Combobox. When the user select a ComboBoxItem, I have to do some complex operations on the ObservableCollection that is the ItemsSource of the ListBox, then I have to show the ListBox with the changed data. The problem is that if I handle the event "SelectionChanged" of the ComboBox control, every time I modify the source-class of the comboboxItems I enter in the method that handle the event, and this generate wrong results. In short I have to distinguish, in some way, between the SelectionChanged generated by code, and the SelectionChanged generated manually by the user with the mouse.
I have tried many ways, but nothing that works :-(
The soution I thought was the best, is to handle the event "GotFocus" or "MouseUp" of the ContentPresenter of the ItemContainerStyle of the Combo, or else to handle the same events ("GotFocus" and "MouseUp") of the ItemsPanel of the Combo, but the method I handled didn't capture the event (in debug the cursor doesn't enter at all in the method).
I can't use a boolean to stop the method "SelectionChanged" until the "first round" is finished, because the changes of the source-class of the ComboBoxItems occurs after that the method has been all executed.
The default value of the Combos is not always the first (it would be too easy :-)), and not always the same. Everytime the user select an item of one of the Combo, the default value of the other Combos has to change.
Can you help me?
Pileggi
' XAML
<Style x:Key="modComboCriteriEventParts" TargetType="{x:Type ComboBox}">
<EventSetter Event="Selector.SelectionChanged" Handler="cb_SelectionChanged"/>
</Style>
<DataTemplate x:Key="modLBoxCriteriParts">
<ComboBox Style = "{StaticResource modComboCriteriEventParts}"
ItemsSource = "{Binding CriteriItemList}"
ItemContainerStyle = "{DynamicResource modComboContainerParts}"
SelectedIndex = "{Binding valueSelected}" ... />
</DataTemplate>
<ListBox x:Name="lbCriteri" IsSynchronizedWithCurrentItem="True"
ItemsSource = "{Binding CriteriList, Source={StaticResource P_CriteriDataSource}}"
ItemTemplate = "{DynamicResource modLBoxCriteriParts}"
... />
' Code Behind
Private Sub cb_SelectionChanged(ByVal sender As System.Object, ByVal e As SelectionChangedEventArgs)
Dim ri as New RicambiCriteriList() As ObservableCollection(Of P_CriteriItem)
' some complex operations with ri ...
be = BindingOperations.GetBindingExpression(Me.lbCriteri, ListBox.ItemsSourceProperty)
Dim allCriteri As P_Criteri = DirectCast(be.DataItem, P_Criteri)
allCriteri.AddData (ri)
e.Handled = True
End Sub
' Source-Class
Public Class P_Criteri
Private _CriteriList As New ObservableCollection(Of P_CriteriItem)
Public ReadOnly Property CriteriList() As ObservableCollection(Of P_CriteriItem)
Get
CriteriList = _CriteriList
End Get
End Property
Public Sub AddData(ByVal CriteriListPass As ObservableCollection(Of P_CriteriItem))
_CriteriList.Clear()
For Each a As P_CriteriItem In CriteriListPass
_CriteriList.Add(a)
Next
End Sub
End Class
Public Class P_CriteriItem
Implements INotifyPropertyChanged
Public Sub New(ByVal criterioPass As String, ByVal CriteriItemListPass As ObservableCollection(Of P_CriteriItemValore), _
ByVal widthCriteriValuesPass As Double)
Me._criterio = criterioPass
Me._CriteriItemList = CriteriItemListPass
Me._widthCriteriValues = widthCriteriValuesPass
End Sub
Private _criterio As String = ""
Private _CriteriItemList As New ObservableCollection(Of P_CriteriItemValore)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property criterio() As String
Get
Return Me._criterio
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._criterio, value) Then
Me._criterio = value
Me.OnPropertyChanged ("criterio")
End If
End Set
End Property
Public Property CriteriItemList() As ObservableCollection(Of P_CriteriItemValore)
Get
Return Me._CriteriItemList
End Get
Set(ByVal value As ObservableCollection(Of P_CriteriItemValore))
If Not Object.Equals(Me._CriteriItemList, value) Then
Me._CriteriItemList = value
Me.OnPropertyChanged ("CriteriItemList")
End If
End Set
End Property
Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
If handler IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Public Class P_CriteriItemValore
Implements INotifyPropertyChanged
Public Sub New(ByVal criterioValorePass As String)
Me._criterioValore = criterioValorePass
End Sub
Private _criterioValore As String = Nothing
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property criterioValore() As String
Get
Return Me._criterioValore
End Get
Set(ByVal value As String)
If Not Object.Equals(Me._criterioValore, value) Then
Me._criterioValore = value
Me.OnPropertyChanged ("criterioValore")
End If
End Set
End Property
Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
If handler IsNot Nothing Then
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End If
End Sub
End Class
Firstly I think its better to handle events on the item container itself and not on the content presenter within the item. And now that I think of it that's probably why you don't see the events. The container is probably eating the events for selection.
But either way if you can't catch the MouseDown/GotFocus events, you can use the PreviewMouseDown/PreviewGotFocus events. Just in case you are not sure what these mean you should read up on wpf event routing architecture and bubbling and tunneling events.

Resources