WPF updating parameterized bindings - wpf

I have a property in a class:
Default Public ReadOnly Property GetLiteral(Key As String) As String
Get
Try
Dim Row() As String = _LookupTable.Item(Key)
Return Row(_CurrentLanguage)
Catch ex As Exception
Return ("Error")
End Try
End Get
End Property
And I bind a label to it:
<Label Content="{Binding SLP.[LITERAL_USERNAME]}"/>
Where SLP is a public property in my view model containing an instance of the class containing the default property GetLiteral.
The above binding works fine. I can select different languages in my view. However, I wish to also be able to change languages on the fly, and I can't figure out how to raise INotifyPropertyChanged in order to make this binding update. I know I could probably achieve this easily with a value converter and parameter, but I like the simplicity of the XAML above.
Thanks.

I figured it out. I was calling NotifyPropertyChanged from inside SLP, where what I really needed to do was call NotifyPropertyChanged(Me, "SLP") from the View Model.

Related

Visual Basic form property array don't get stored

here's my issue:
I made an UserControl containing a ListView called "lstMain".
I have a property inside my controller:
Public ReadOnly Property DataRowBoundColumns() As System.Windows.Forms.ListView.ColumnHeaderCollection
Get
Return Me.lstMain.Columns
End Get
End Property
In the designer I can edit such property BUT when I compile the value gets deleted.
This doesn't happen for the other properties, which are a String, an Integer and other objects (not arrays).
Can anyone help me?
The problem is that the data you provide through the collection editor in design mode, do not get serialized.Assuming that your custom control is called "MyCompositeControl" you should write something like the following.
Imports System.ComponentModel
Public Class MyCompositeControl
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public ReadOnly Property DataRowBoundColumns() As System.Windows.Forms.ListView.ColumnHeaderCollection
Get
Return Me.lstMain.Columns
End Get
End Property
End Class

Changing Combobox item in code-behind throws invalidoperationexception

I have several comboboxes which are initialiced with a default text which will be replaced later on by textes out of a text-file to support different languages.
I tried different ways to change the text but none of them worked out:
Initialization:
myCombobox.itemSource = new ObservableCollection(Of String){"FirstItem", "SecoundItem"})
First Way:
myCombobox.Items(i) = GetString(myCombobox.Items(i))
Secound Way:
Dim comboboxStr = myCombobox.Items(i)
myCombobox.Items.RemoveAt(i)
myCombobox.Items.Add(GetString(i))
Both ways throw an InvalidOperationException with the hint to try "ItemsControl.ItemsSource" instead of "ItemsSource"
Is there maybe another way to change the items or what why is this exception occurring?
This is a common problem. Once you have data bound the ItemsControl.ItemsSource (or any class derived from ItemsControl, then you cannot use the ItemsControl.Items property to manipulate the data. Instead, (you should declare a property and) just access the data collection that you data bound directly:
SomeProperty = new ObservableCollection(Of String){"FirstItem", "SecoundItem"})
...
myCombobox.itemSource = SomeProperty
...
SomeProperty.Remove(someItem)

How to make ViewModel aware of conversion error

I am designing a WPF application following MVVM. My ViewModel is exposing one Double property called DoubleValue, which is binding to a TextBox in the View. I have set "ValidatesOnDataErrors=True" for the binding. So if the user types a string which can't be converted to a Double, it display the red background.
In my ViewModel I also have a Command object, let's call SaveCommand, whose CanExecute delegate is depending on whether there is any error in the VM (my ViewModelBase class implements IDataErrorInfo, I have an overridable ValidatePropertyByName function and the validation errors are stored in a dictionary.) But now my problem is, if I give an invalid string in the TextBox, since the conversion fails, it never calls the setter of the binding property value. In another word, the ValidatePropertyByName is not called and the error dictionary remains the previous state, which normally is clean. So if now the user click the Save button (which is enabled since the error dictionary is clean), the SaveCommand executes with the previous valid double value to save. This is obviously not good.
So how can I make my ViewModel aware of such conversion errors?
UPDATE:
Some code example:
The binding property is like this:
Public Property DoubleValue As Double
Get
Return _doubleValue
End Get
Set(value As Double)
If value <> _doubleValue Then
_doubleValue = value
RaisePropertyChanged("DoubleValue")
End If
End Set
End Property
Private _doubleValue As Double
My binding is like this:
<TextBox Grid.Row="3" Text="{Binding DoubleValue, ValidatesOnDataErrors=True}" />
And now my problem is: if I give a string "XXX" in the text box, since it can't be converted to a double value, the setter of DoubleValue is never get called. And so the property value remains the previous(valid) value. Now if my SaveCommand gets executed, it will do the save operation with this previous valid value, which will make the user confused.
the most easy way is to just use string properties in your viewmodel. then you get all input from the user and can validate it in your viewmodel. the drawback is that you have to convert the values to the right type when you go to the model.
if you dont want this you have to create your own controls or better behaviors so that the use can just input values that your viewmodel expect. eg. NumericInputBehavior.
You cannot simply put these two things together. One is the regular validation inside the ViewModel. The other are control-specific problems, like unconvertible values.
So there are two possible ways to solve this:
1) Don't use a converter. Just bind the string. Inside the ViewModel you can then use the validation to check for a valid value. (More MVVM)
2) Store your ValidationErrors on the controlside and merge them with the viewmodel errors. This is not easy but a good way to create one source for binding against ALL problems within your UI. We are doing this for complex textboxes at work. This means manual code in the controls but for complex customcontrols this is OK, I believe.
edit: just to elaborate a little on the 2nd point. We are having a DependencyProperty of Type ObservableCollection inside the Control. Then you can bind this Collection to a ViewModel Property and as soon as your control moves an Error inside the collection it is available inside the viewModel. You can then use this collection inside your validation implementation. This works pretty well for larger controls.
Edit2: For the MarkInvalid Stuff I mentioned in the comment. It would look like this:
DataErrorValidationRule validationRule = new DataErrorValidationRule();
ValidationError validationError = new ValidationError(validationRule, myTextBox.GetBindingExpression(TextBox.TextProperty)) { ErrorContent = "My custom message" };
Validation.MarkInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty), validationError);
You would call in from inside a TextChanged when you can't convert the new given value or
Validation.ClearInvalid(myTextBox.GetBindingExpression(TextBox.TextProperty))
Maybe that will help?

Binding from multiple sources

My scenario:
I've got a Silverlight Application with a View, where i want to bind the textboxes to an object (two-way) and all labels to a dictionary holding the label translations.
My approach was to set the datacontext of the page to a dictionary with two items, one of them is the object and the other is the translation-dictionary.
In xaml the code looks like the following:
<TextBlock Text="{Binding [dict].[name],FallbackValue='Fallback'}" />
<TextBox Text="{Binding [obj].name,Mode=TwoWay}" />
This works initially, if I however change the object on the datacontext, the xaml is not notified about any changes and doesn't update.
I've had a working solution using a Converter for the translations, however due to the limitations on one converterparameter I didn't like the solution. In addition it wasn't possible to place a fallback-value in the textblock, which resulted in "invisible" textblocks while designing the page.
Any suggestions on how to solve this issue? It doesn't have to be using my dictionary, it would be also okay if i could set the datacontext to the object (which works) and bind the labels somehow different.
I know this will get a lot of traditional answers, but I would also like to put forward something completely original we tried (and succeeded) doing ourselves for more efficient localisation of Silverlight using Attached Properties instead of binding:
Localisation of Silverlight projects after completion
What would be the most flexible is rather than setting the DataContext for the view to a dictionary, you would be better off having the DataContext be something like a ViewModel. That is, a simple class that holds multiple properties: one for your "object" and one for your translation dictionary.
Then have the class that acts as your ViewModel implement INotifyPropertyChanged.
Create a method in your class called OnPropertyChanged that takes in a string representing your property name. In that method raise the PropertyChanged event passing in the instance of the ViewModel class and a new PropertyChangedEventArgs passing in the property name.
Back in the properties you created (object and dictionary) in the Set, after setting the value, call OnPropertyChanged passing in the string name of that property. This will notify the UI that the value of this property has changed and will essentially rebind the control to that property.
Finally, bind the Text properties of your controls on your View to the new properties you just created in your ViewModel. That should ensure that the controls on the view stay up to date.
I found a solution, but wasn't able to answer my own question (8h limit..)
I think this is just the approach Hydroslide suggested.
Create a class which holds all data and implements INotifyPropertyChanged
Public Class MyDatacontext
Implements ComponentModel.INotifyPropertyChanged
'Properties
Private _obj As Object
Private _dict As Dictionary(Of String, String)
'Events
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
'Methods
Public Property Obj As Object
Get
Return _obj
End Get
Set(ByVal value As Object)
_obj = value
'Notify the xaml about the changed Object
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Obj"))
End Set
End Property
Public Property Dict As Dictionary(Of String, String)
Get
Return _dict
End Get
Set(ByVal value As Dictionary(Of String, String))
_dict = value
'Notify the xaml about the changed translation
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Dict"))
End Set
End Property
End Class
Define a private var in your page code
Private mycontext As New MyDatacontext
In the constructor of your page, fill your "mycontext" with the desired data
mycontext.Dict = LoadDictionary()
mycontext.Obj = LoadObject()
Me.DataContext = mycontext
Change your xaml to the following
<TextBlock Text="{Binding Dict.[name],FallbackValue=MyFallback}" />
<TextBox Text="{Binding Obj.name,Mode=TwoWay}" />
Update your object/dictionary as you like using
mycontext.Obj = LoadNextObject()

Is there any general procedure to implement command in MVVM if the element doesn't support command?

Is there any particular way to implement command in MVVM if the element doesn't support Command. Example how to implement the TextChanged_event for the TextBox?.
There is no need to use the TextChanged_event or the SelectionchangedEvent as you can achieve the same using binding them to your ViewModel properties and waiting for their notification message (check MVVMLight's Messenger helper class).
If you desperately need a handler for those events, you can try the EventToCommand behaviour helper class which uses RelayCommand
You can check out this illustration and example program for details on messenger class and this example for getting a clear picture on EventToCommand behaviour
What you do is watch for the change in your ViewModel's property set method.
The XAML would look something like this:
<TextBox Text="{Binding Mode=TwoWay,
Path=ViewModelProperty,
UpdateSourceTrigger=PropertyChanged}" />
And on the ViewModel class, you define a property like this:
Private _ViewModelProperty As String
Public Property ViewModelProperty As String
Get
Return _ViewModelProperty
End Get
Set(ByVal value As String)
' your test for "TextChanged" goes here
If value <> _ViewModelProperty Then
_ViewModelProperty = value
OnViewModelPropertyChanged()
End If
End Set
End Property
Private Sub OnViewModelPropertyChanged()
' logic for handling changes goes here
End Sub
This has the side effect of executing OnViewModelPropertyChanged() every time you assign a new value to the ViewModelProperty, but you can avoid that by assigning to the backing field instead of the property.
Otherwise, you're implementing ICommand interfaces, which have their use; it depends on how complex you need things to get.

Resources