Changing WPF databindings based on value of another property - wpf

I'm reasonably new to WPF, have developed a couple of apps with code-beinding files and have read up on MVVM (via Schifflett's 'in the box' introduction) prior to starting my current application.
The items I'm working with have a bunch of generic string properties, plus a Dictionary property called Hours which maps dates to hours worked.
My user interface has a DataGrid view of these items (bound to a collection in the ViewModel), and a combobox which allows the user to select a date (which binds the selected value to SelectedDate in the ViewModel). The DataGrid's Hours column needs to show the number of hours worked in the week (i.e., have the same effect as calling item.Hours[SelectedDate] or similar).
What is the best way to do this? Is it possible to put a variable within the binding expression like {Binding Hours[SelectedDate]}?

The two solutions that immediately come to mind are these:
1) Create an Hours property that is based on your SelectedDate:
public int Hours {get { return calculateHours(SelectedDate); } }
"calculateHours" could either be a method or you could put the calculation in the Setter itself. Make sure that whenever SelectedDate is Set that you raise PropertyChanged for "Hours" as well.
I would use this approach if this Hours calculation is only used in this View from this ViewModel.
2) Create a Value Converter that accepts a date and returns the calculated value. Then bind the Hours to the SelectedDate property:
<TextBlock Text="{Binding SelectedDate, Converter={StaticResource DateToHoursConverter}}"
I would use this approach if the calculation is required in multiple Views or in multiple ViewModels. Value Converters are great for this kind of reuse.

if your property you bind to has an indexer, you can bind to it. you just have to raise INotifyPropertyChanged for this indexer at the right time.
edit: a variable within the binding expression will not work, but you can bind to Hours and use a converter and the SelectedDate as convertparameter to get the value you want. you should have to raise INotifyPropertyChanged for "Hours" when "SeletedDate" changed.

Related

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?

How to Validate binded WPF TextBoxes which is depend on each other's value and how to enable/disable Ok button according to Validation

I have two textboxes and two buttons on c# WPF windows application
Application have One variable that called Total Amount.
one text box is for Discount Percentage and other one is for Discount Amount.
if i changed discount amount then percentage should get reflected using DataBinding in WPF and viceversa(I Have that)
I want to validate both the textboxes
1). Discount should be in range MIN to MAX
2). Discount Amount should not be grater than Total Amount
and then Ok button will get Enable/disable according to Validation
I would recommend that you adopt the MVVM pattern (if you haven't already) and have your validation logic contained in the view model. Using this approach, you can either:
Expose an IsValid property on the view model and bind it to the button; the property getter would return the result of your validation logic
Expose an ICommand on the view model whose CanExecute method would return the result of your validation logic (recommended)
A quick example:
public class DiscountViewModel : ViewModel // Base class implements INotifyPropertyChanged
{
// Define all of your view model properties, i.e., DiscountAmount, DiscountPercent, etc.
...
// Define a command
public ICommand OKCommand { get; }
}
Then in your XAML view, you would add the following binding:
<Button Command={Binding Path=OkCommand} Content="OK" />
Again, this is just a brief example that should help point you in the right direction. There are tons of great resources on the MVVM pattern available as well as resources for the WPF command pattern. Here is a good introductory resource that covers both: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
For every Binding you define you can add a Converter. This can converter can be a single value converter, then you must implement the IValueConverter Interface.
But I think you can solve your problem with a IMultiValueConverter. This Converter can be given much values, which can also get from Bindings. The Converter is getting the values from the Bindings, processing them with your logic and then giving it back to your property where you defined it.
http://msdn.microsoft.com/en-us/library/system.windows.data.imultivalueconverter.aspx

Updating an Observable Collection Based on a combobox selection

So I have an ObservableCollection of items called "Class1" and
Class1 has a property named "ID".
I use a datagrid from the WPFToolkit and bind to this collection.
Within the datagrid is a combobox column and I bind it's ItemsSource to the ID property of the class.
At this point, all is good and everything populates as it should. What I want to do is modify the ObservableCollection to reflect the value selected in the ComboBox.
I have a ValueConverter bound to the SelectedItemBinding on the ComboBox as follows:
SelectedItemBinding="{Binding Path=ID, Converter={StaticResource IDConverter}}
What is the best (i.e: WPF approved method) of modifying the collection? When the IDConverter ConvertBack() method is called, I get the appropriate Class1 instance, but I can't access the Observable collection from within the ValueConverter and I also don't have access to the SelectedIndex value from the Datagrid.
I could create a class as a static resource with a pointer to my collection and pass that as a ConverterParameter, but that seems sort of hokey and I'm assuming there must be some slicker way of doing this with databinding.
For the record, a simple solution is to create a local resource with a reference to the collection you wish to modify as a dependency property. You can then pass this as a ConverterParameter and have access to it in the ConvertBack() interface method.
A caveat: You will most likely encounter a DeferRefresh exception when you make changes to the collection and then you lose focus.
An excellent fix is found here:
http://social.msdn.microsoft.com/Forums/en/wpf/thread/187b2b8f-d403-4bf3-97ad-7f93b4385cdf

WPF and MVVM: Changing data binding converter at runtime

I am using WPF and the MVVM pattern in my user interface. In my ViewModel I have a List containing distances in millimetres, which I display in a ListView by binding ListView.ItemsSource to the List. However, I would like the values displayed to use a more natural unit - either metres or feet depending on the state of a "metric" checkbox.
I have written a couple of simple classes, MillimetresToMetresConverter and MillimetresToFeetConverter, both of which implement IValueConverter. Although I can set the Converter property on my data binding to one or the other, I am unsure how to change between these converters when the state of the checkbox changes.
My plan was to have a field "IValueConverter lengthConverter" on my ViewModel which I could set to one converter or the other, then in my XAML do ...="{Binding Converter={Binding Path=lengthConverter}}" - unfortunately this does not work since Converter is not a dependency property.
How can change the converter used by the data binding at runtime?
Most of the time when using the MVVM methodology, you can do the formatting task in the VM classes. In your case, you could add a Format property to the VM class, and based on the value of the Format property, return a well formated string.
See this discussion for more information.
If I may suggest a simple alternative solution: Create a small FormatMillimetresConverter in your ViewModel, whose UseMetric property is bound to the "metric" checkbox.

WPF Bind to DependencyProperty of another Object?

I am working on a WPF application similar to visio. I would like to be able to logically group some of the items in my diagram, each of which is a UIElement, and control certain behaviors (i.e. visibility) at the group level.
My first attempt at this was to create a control, called a Group, which had width & height = 0. I wanted to assign to my diagram elements a specific "Group" through their group property, and then bind certain UIElement properties to the group value, as below:
<DiagramNode
Width="300" Height="300"
Visibility="{Binding RelativeSource={RelativeSource Self},Path=Group.Visibility}"
> ... </DiagramNode >
Although this does not throw a binding error, it also doesn't work. Changing the Visibility of the group has no affect on the visibility of the nodes assigned to that group. No errors appear at anytime as far as i can tell, it just doesn't work.
Any ideas? Is my approach possible? If no, any one have alternatives they'd like to suggest :). I'm not a huge UI guy, feel much more comfortable in a service layer, so I'm open to other suggestions.
If there really is no binding error in the trace of the application when run through the debugger, then the problem is probably in change notifications. You must make sure that the Visibility property of your Group object provides change notifications when changed.
This is usually done by implementing INotifyPropertyChanged on the class, and in the set accessor raising a PropertyChanged event (if the value actually changed).
Is the issue perhaps in my property declaration of the Group object of my DiagramNode class?
Public Class DiagramNode
...
Private _group As Group
Public Property Group() As Group
Get
Return Me._group
End Get
Set(ByVal value As Group)
Me._group = value
End Set
End Property
...
End Class

Resources