Binding doesn't get called on code behind propertry - wpf

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.

Related

WPF Combobox Item Update

I am rather new to the WPF setup and I am running into an issue where as far as I can see I have set it up correctly to have my combobox bound to a observable collection of object.
The Combobox will update when I add or delete items. If I make a change the items in the drop down will not show any differently but if I select one that was edited it will now show the new information but only when selected.
I have set up the object class to use INotifyPropertyChanged correctly I think but it does not seem to be functioning. Going to attach the code below so that you can easily see exactly what I am trying to describe.
What I am trying to do it allow a user to push a button and have the text inside a combobox update to show the new text.
Imports System.ComponentModel
Public Class Window2
Public _names As New System.Collections.ObjectModel.ObservableCollection(Of TestClass)
Public Sub BaseLoading() Handles MyBase.Loaded
Dim AddNewItem As New TestClass
AddNewItem.groupName = "Item " + (_names.Count + 1).ToString
_names.Add(AddNewItem)
cbo_Names.SetBinding(ItemsControl.ItemsSourceProperty, New Binding With {.Source = _names})
End Sub
Private Sub button_PreviewMouseDown(sender As Object, e As MouseButtonEventArgs)
Dim AddNewItem As New TestClass
AddNewItem.groupName = "Item " + (_names.Count + 1).ToString
_names.Add(AddNewItem)
_names(0).groupName = ("Value Changed")
End Sub
End Class
Public Class TestClasss
Implements INotifyPropertyChanged
Public _groupName As String = ""
Public Property groupName As String
Get
Return _groupName.ToString
End Get
Set(value As String)
_groupName = value
onPropertyChanged(New PropertyChangedEventArgs(_groupName))
End Set
End Property
Public Event PropertyChagned(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Public Sub onPropertyChanged(ByVal e As PropertyChangedEventArgs)
RaiseEvent PropertyChagned(Me, e)
End Sub
End Class
XAML
<Window x:Class="Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="button" Content="Button" PreviewMouseDown="button_PreviewMouseDown"/>
<ComboBox x:Name="cbo_Names" Margin="30,5,30,5" IsEditable="False" ItemsSource="{Binding _names, NotifyOnSourceUpdated=True,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="groupName" SelectedItem="{Binding _names, NotifyOnSourceUpdated=True,Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
I would appreciate any help locating what I am missing.
You should pass the name of the data-bound property (instead of the value of the property) to the constructor of the PropertyChangedEventArgs:
onPropertyChanged(New PropertyChangedEventArgs("groupName"))
If you are using at least Visual Studio 2015, you could consider making the following change to your onPropertyChanged routine:
Public Sub onPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Then, in the setter for groupName you can call onPropertyChanged without specifying the property name, and it will be taken from the name of the caller (that is, it will end up being "groupName").
Effectively, this is doing the same thing as the previous answer, but in a way that is easier for you to code and maintain. (Along with the <CallerMemberName> attribute, this works well with NameOf, both making your code more robust against any changes in names of properties.)

WPF binding with .NET object not communicating the data

I am following a tutorial on WPF data binding. I am trying to bind to a .NET object's property to a XAML control but the control does not display the expected data. These are what I believe to be the relevant sections of code:
In procedural code: (Note: removed ObservableCollection in PhotoGallery after original post)
Namespace PhotoGallery
Partial Public Class MainWindow
Inherits Window
Private photos As New Photos
...
End Class
Namespace PhotoGallery
Public Class Photos
Inherits Collection(Of Photo)
...
End Class
In XAML (Solution/Project name is Ch13-PhotoGallery):
<Window x:Class="PhotoGallery.MainWindow"
...
xmlns:local="clr-namespace:Ch13_PhotoGallery.PhotoGallery"
...>
<Window.Resources>
<local:Photos x:Key="Photos"/>
</Window.Resources>
And this is the control that is not displaying the data, which is the size of the Photos collection:
<Label x:Name="numItemsLabel" Background="AliceBlue" FontSize="8" Content="{Binding Source={StaticResource Photos}, Path=Count}"/>
When I typed in the < Label >, Intellisense popped up 'Count' for the Path property, so I think that tells me I have everything defined correctly.
If I add this line of procedural code behind to the refresh() method:
numItemsLabel.Content = photos.Count
Then the count is displayed correctly.
But I'm not getting the binding in XAML to display Photos.Count.
This creates a new instance of the Photos class:
<local:Photos x:Key="Photos"/>
If you want to bind to the Photos collection that you have created in your MainWindow.xaml.vb file you should expose it as a public property - you can only bind to properties but not fields - and set the DataContext of the window to an instance of the class where this property is defined, i.e. the window class itself in your case:
Class MainWindow
Public Property Photos As Photos
Public Sub New()
' This call is required by the designer.
InitializeComponent()
DataContext = Me
...
End Sub
End Class
You can the bind directly to the property:
<Label x:Name="numItemsLabel" Background="AliceBlue" FontSize="8" Content="{Binding Path=Photos.Count}"/>
Your ViewModel needs to implement the INotifyPropertyChanged interface, this will let your window listen to changes to your ViewModel
Here's an example of how to implement this interface in VB
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7de44362-8b88-4292-b4ee-0385c3b34d7d/im-just-looking-for-a-simple-vb-net-mvvm-sample-wpf?forum=wpf
ViewModel
Public Class ViewModel
Implements INotifyPropertyChanged
Public Sub New()
Me.myTextValue = "default value..."
End Sub
Private myTextValue As String = String.Empty
Public Property MyTextProperty() As String
Get
Return Me.myTextValue
End Get
Set(ByVal value As String)
Me.myTextValue = value
NotifyPropertyChanged("MyTextProperty")
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding MyTextProperty}"/>
</Grid>
</Window>
XAML Code Behind
Class MainWindow
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = New ViewModel()
End Sub
End Class

WPF INotifyPropertyChanged not updating label

I'm currently learning some basics in WPF and I've been looking for the mistake for about 2 days. Hope you guys can help.
I'm trying to update my UI (in this case the content of a label) by using INotifyPropertyChanged and a binding in XAML. The thing is: it only takes the first value and puts it in the content. Furthermore nothing happens but the event (OnPropertyChanged) is fired.
This is what I have in XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:View x:Key="ViewModel"/>
</Window.Resources>
<Grid Margin="0,0,2,-4" DataContext="{Binding Source={StaticResource ViewModel}}">
....
<Label x:Name="lbl_money" Grid.ColumnSpan="2" Content="{Binding Path=PropMoney}" HorizontalAlignment="Left" Margin="403,42,0,0" VerticalAlignment="Top">
And this is the necessary part of my class View:
Public Class View
Inherits ViewModelBase
Private rest1 As New Restaurant
Private mainPlayer As New Player
Private mycurrentMoney As Double = 3
Private currentClickIncrease = mainPlayer.PropClickIncrease
Public Property PropMoney() As Double
Get
Return mycurrentMoney
End Get
Set(value As Double)
mycurrentMoney = value
OnPropertyChanged("mycurrentMoney")
End Set
End Property
Sub SelfClicked()
PropMoney() += 1
End Sub
Last but not least the MainWindow class, where i instantiate my view:
Class MainWindow
Private view As New View
Sub New()
InitializeComponent()
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
view.SelfClicked()
End Sub
End Class
So my mycurrentMoney is increasing each click and the event is fired but the label doesn't update.
Thank you in advance!
If you have Visual Studio 15 use NameOf operator instead of string literal like so:
NameOf(PropMoney);
If you later rename your property, it will still work opposed to string literal which will NOT. Alternatively modify your OnPropertyChange to make use of CallerMemberName
OnPropertyChange ([System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
{
}
The property name will be filled in, this works only in setter for current property however.
Also, set DataContext for whole window (Setting DataContext in XAML in WPF). DataContext={StaticResource ViewModel} and don't use Path in your Binding, just {Binding PropertyName}
Your OnPropertyChanged("mycurrentMoney") statement won't raise a property change on your property, because it's called PropMoney.
You have to set OnPropertyChanged("PropMoney") in your setter instead.
There are 2 problems with your code
First you raise PropertyChanged event for the backing field and should raise it for property name
OnPropertyChanged("PropMoney")
Second the property you change belong to different instance of View then the one set as DataContext. So in XAML remove DataContext changes, only leave property binding
<Window ...>
<Grid Margin="0,0,2,-4">
<!-- .... -->
<Label ... Content="{Binding Path=PropMoney}">
and then in code set DataContext of MainWindow to the instance that you create and modify
Class MainWindow
Private view As New View
Sub New()
InitializeComponent()
DataContext = view
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
view.SelfClicked()
End Sub
End Class

wpf lable text binding not working, where i am going wrong

I am new to WPF and trying something like this to update a label text in the WPF form from the class.
The onchange event is getting triggered, but not getting displayed on the form
Here is my class
Public Class ExtractDetails
Inherits UserControl
Implements INotifyPropertyChanged
Private _prdFrstName as string
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property PrdFrstName() As String
Get
Return _prdFrstName
End Get
Set(ByVal value As String)
If _prdFrstName <> value Then
_prdFrstName = value
Me.OnPropertyChanged("PrdFrstName")
End If
End Set
End Property
Public Sub suMainStrt()
PrdFrstName = strComurl ''contyains teh URL to nagigate to
webBrwFrst = New WebBrowser
webBrwFrst.Navigate(New Uri(strComurl))
Call extract(webBrwFrst, strComurl)
end sub
end class
the url keeps on changing as i ma getting the values from an excel file and looping for each URL.
i wanted to display the URL currently working now
this is my XAML
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Avenet Prduct Description Extractor" Height="396.627" Width="588.123" Background="AliceBlue" Icon="LGIcon.ico">
<Grid Height="341.077" Width="567.721" Background="AliceBlue">
<StackPanel Margin="170.225,226.418,3.143,0" Name="StackPanel1" Height="97.994" VerticalAlignment="Top">
<Label Height="30.906" Name="lblCrntSt1" Content="{Binding Path=PrdFrstName, UpdateSourceTrigger=PropertyChanged}" Width="161" BorderThickness="2" BorderBrush="AliceBlue" Background="Red" Foreground="White" FontSize="13"></Label>
</StackPanel>
</Grid>
and this is my windows class.
Class Window1
Dim clsIniti As New ExtractDetails
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
'clsIniti = New ExtractDetails
Me.DataContext = clsIniti
End Sub
end class
without updating the text labels the entire function is working good. but i wish to display few things. where i am going wrong
I tried data binding by removing few parts to new created project. it works there. so some thing wrong in this code??? :`(
I see two possible causes that this doesn't work for you.
A. How does your OnPropertyChanged method look like?
' Correct implementation:
Private Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
B. Make sure the ExtractDetails instance you call suMainStrt on, is the same as your DataContext instance. Test this by calling suMainStrt directly from the constructor of Window1:
Class Window1
Dim clsIniti As New ExtractDetails
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
'clsIniti = New ExtractDetails
Me.DataContext = clsIniti
' test (if this works, your problem is B.)
clsIniti.suMainStrt()
End Sub
End Class
As a side note: Unless you have good reasons to do this, I suggest you create a dedicated viewmodel (class, not usercontrol) that contains the properties you want to bind to.

WPF Two controls bound to single property not updating each other

I am developing a WPF application using the MVVM design pattern.
I have a ViewModel which implements INotifyPropertyChanged via it's base class. The ViewModel contains a property which is then bound to two text boxes. Relevant code below.
ViewModelBase
Public MustInherit Class ViewModelBase : Implements INotifyPropertyChanged
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Sub onPropertyChanged(propertyName As String)
If Not propertyName Is Nothing Then RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
ViewModel
Public Class TemplateEditVM : Inherits ViewModelBase
Public Property Name As String
Get
Return _Template.Name
End Get
Set(value As String)
If Not _Template.Name = value Then
_Template.Name = value
onPropertyChanged("TemplateEditName")
End If
End Set
End Property
End Class
View
<TextBox Text="{Binding Path=Name}" />
<TextBox Text="{Binding Path=Name}" />
The value of the property Name correctly populates both text boxes when the View is first loaded. The problem is that when I change the text in one of the text boxes the text of the other doesn't change, even though the underlying property has. If I step through it I can see that the PropertyChanged event is being fired.
Can anybody tell me why? Many thanks for any help.
Set your binding mode to Twoway
<TextBox Text="{Binding Path=Name, Mode="TwoWay"}" />
And your on propertyChanged method should math the exact name you bind to

Resources