Singletons and ICommand - wpf

Is there some special rules I have to be careful about when binding my view to the commands defined in my singleton ViewModel, opposed to normal (non-singleton) ViewModels?
All my ViewModels except the one in question behave normally. Each of them exposes two public members called HasChanges (bool property) and SaveChanges (method) that I'm calling in the CanExecute and Execute functions of my commands.
While all other Views behave normally, enabling/disabling the buttons when the value of HasChanges changes and saving the contents when those buttons are clicked, the only ViewModel that implements Singleton pattern happens to call CanExecute only upon the first loading of the View.
After that, any number of PropertyChanged events raised from within that ViewModel (all my ViewModels implement INotifyPropertyChanged) do not affect the disabled state of the button.
Wondering what I'm missing here.
Here's the singleton ModelView:
Public NotInheritable Class MyViewModel
Private Shared ReadOnly mInstance As New CommonListsViewModel
Public Shared ReadOnly Property Instance() As CommonListsViewModel
Get
Return mInstance
End Get
End Property
Public Property SaveChangesCommand As ICommand
Private Sub New()
SaveChangesCommand = New Commands.SaveChangesCommand()
End Sub
Public ReadOnly Property HasChanges As Boolean Implements IEditorViewModel.HasChanges
Get
...
End Get
End Property
Public Function SaveChanges() As Boolean Implements IEditorViewModel.SaveChanges
...
End Function
End Class
Here's the command:
Friend Class SaveChangesCommand
Inherits CommandBase
Public Overrides Function CanExecute(parameter As Object) As Boolean
Return MyViewModel.Instance.HasChanges
End Function
Public Overrides Sub Execute(parameter As Object)
MyViewModel.Instance.SaveChanges()
End Sub
End Class
And here's my View:
<Grid DataContext="{x:Static local:CommonListsViewModel.Instance}">
<Button Command="{Binding SaveChangesCommand}">
</Grid>

There shouldn't be any difference in the behavior due to the fact that a class follows the singleton pattern. Anything with a reference to the singleton will not care how the construction of the class is restricted, only that the instance of the class exists.
The problem probably lies in a lack of a proper link between the PropertyChanged event of the ViewModel and the CanExecuteChanged on the Command.

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.

Hide a property from an observablecollection

So I have a WPF DataGrid bound to an ObservableCollection, which contains a single instance of a class - for example:
Public Class parent
Public Property title As String [...]
Public Property someCommonThing as Integer [...]
Public Class Child Inherits Parent
Public Property name As String [...]
Public Property address As String [...]
Public Class Window1
Dim oc As ObservableCollection(Of Object) = New ObservableCollection(Of Object)
oc.Add(New Child())
dataGrid.ItemsSource = oc
there are many child classes with different properties, hence why I can't easily define the datagrid columns directly.
I want to be able to hide certain parent properties from the datagrid (for example, never show the title property in the datagrid), while still being able to use it for databinding elsewhere (e.g. a label).
Is this possible? I can't think how to do it without manually specifying every column for every possible class instead of using the databinding.
When automatically generating columns you can change the per-property behavior using Data Annotations, in this case specifically the BrowsableAttribute class:
<Browsable(False)>
Annotating your property with this will prevent a column from being generated when using the following event handler on the AutoGeneratingColumn event of the DataGrid.
Private Sub OnAutoGeneratingColumn(sender As Object, e As DataGridAutoGeneratingColumnEventArgs)
If Not DirectCast(e.PropertyDescriptor, PropertyDescriptor).IsBrowsable Then
e.Cancel = True
End If
End Sub
Remember to add the DataAnnotations assembly to your project.

WPF disable button with IDataErrorInfo

I found this tutorial and I was able to implement it.
How can I disable a button when an errors occurs?
I searched a lot over the net, but I can't find a piece of code that resembles mine. (Yes, I know there about a zillion threads about this matter, but I just don't understand it.)
Here is my current code, it's a bit lengthy:
Public Class GradeVm
Implements IDataErrorInfo
Public Interface IDataErrorInfo
Default ReadOnly Property Item(columnName As String) As String
ReadOnly Property [Error]() As String
End Interface
#Region "Properties"
Property Grade As Integer
Property Adjust As Integer
#End Region
Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
Get
Return "Error"
End Get
End Property
Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
Get
Select Case columnName
Case "Grade"
If IsNumeric(Me.Grade) = False Then
Return [Error]
End If
Case "Adjust"
If IsNumeric(Me.Adjust) = False Then
Return [Error]
End If
End Select
Return ""
End Get
End Property
End Class
This combined with the code here did the trick! Finally!! :)
If you're binding button to RelayCommand, you can disable the button by setting CanExecute() function to return False :
Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
Return False
End Function
But RelayCommand doesn't meant to be used this way. I'd suggest to pick a MVVM framework (MVVMLight for example), and you'll get better RelayCommand implementation ready to use. Then you can use it like this :
Private _myCommand As RelayCommand = New RelayCommand(Me.ExecuteCommand, Me.CanExecuteCommand)
Public ReadOnly Property MyCommand As ICommand
Get
Return Me._myCommand
End Get
End Property
Private Sub ExecuteCommand()
......
End Sub
Private Function CanExecuteCommand() As Boolean
'do logic to disable (return false) or enable (return true) button
'based on specific criteria
'just for example, disable the button forever :
Return False
End Function
UPDATE :
Why you deleted your RelayCommand implementation? The 1st approach meant, use RelayCommand in your original post, but change return value of CanExecute() function to False instead of True. Then you can use it in ViewModel like so :
Private _myCommand As RelayCommand = New RelayCommand()
Public ReadOnly Property MyCommand As ICommand
Get
Return Me._myCommand
End Get
End Property

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

Layered INotifyPropertyChanged

I have a class that implements INotifyPropertyChanged for a property.
I have a control that is bound to that property.
I have another class that listens to the propertychanged event. In the event handler of that class I change the value of the property in code.
The problem I have is that I don't want to do any logic in the event handler for the next time it will fire due to the change of the property due to code.
However if the user changes the value of the property in the mean time (via async gui input) I still want the logic to fire. I also need to make sure that the control gets updated (this is twoway binding).
What is the best way to do this without this becoming a complete mess?
One way to accomplish this would be to refactor the setter on your property so that it called a method taking a parameter indicating whether or not to raise the event. Here is a simple code sample:
Imports System.ComponentModel
Public Class Class1
Implements INotifyPropertyChanged
Public Property MyData() As String
Get
Return _myData
End Get
Set(ByVal value As String)
SetMyData(value, True)
End Set
End Property
Private Sub SetMyData(ByVal value As String, ByVal triggerPropertyChanged As Boolean)
_myData = value
If triggerPropertyChanged Then
OnPropertyChanged("MyData")
End If
End Sub
Private _myData As String
Private Sub OnPropertyChanged(ByVal propertyName As String)
SetMyData("new value", False)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Your question is a bit vague, but you can check to see if the value has actually changed to determine if you should do your logic.
It would be better if you include some code to be more specific in your question.

Resources