Problem with validation and multibinding - wpf

In my WPF application I use the following xaml:
...
<TextBox
services:TextBoxService.IsFocused="{Binding Path=IsSelected, Mode=OneWay}"
FocusVisualStyle="{x:Null}">
<MultiBinding
Converter="{StaticResource mconv_operableToString}"
UpdateSourceTrigger="PropertyChanged">
<Binding
Path="Value"
Mode="TwoWay"
NotifyOnValidationError="True" />
<Binding
RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"
Path="DataContext.Status"
Mode="OneWay" />
</MultiBinding>
The view model class which the first binding uses implements IDataErrorInfo for validation purposes. The problem is that although the error is caught in the property setter, the UI doesn't notice it. I have a style defined with an error template which should be applied when any error occurs in the text box. I suppose that maybe this scenario is not allowed with multi binding because where I use single binding everything works fine.
Thanks in advance.

It seems to me that nobody knows the answer to this but I suppose that this scenario just doesn't work. I'll try to answer it in case somebody will need it. I've tried to bind my View to my View Model class which implements IDataErrorInfo, in xaml I specified a converter and although everything worked fine, the Errors just didn't show up on the UI. So, I removed the converter from the binding and implemented that logic inside the View Model and, voila now everything works fine.

Related

Dynamic binding to any tab control - WPF

I am trying to set the max width of the grid of a view, however I want to be able to put this view anywhere within any tab control, as the width of the grid is determined by the tab control it's within. I don't want to have to specify the specific tab control by name because I want it to work with any tab control.
Does anyone know how I can change the below XAML code? This is in the WeUserControl view
<Grid.MaxWidth>
<MultiBinding Converter="{StaticResource WeMaxWidthConverter}">
<Binding ElementName="mainWindowTabControl" Path="ActualWidth"/>
</MultiBinding>
</Grid.MaxWidth>
In the view that it is being used in, it is literally just:
<views:WeUserControl Grid.Column="0" Grid.Row="1" />
and that is encapsulated in a tab control, in this case, called mainWindowTabControl
It's just the tabcontrol you're looking for and the grid will be within the visual tree of this.
The relativesource binding would be:
<Binding Path="ActualWidth"
RelativeSource="{RelativeSource AncestorType={x:Type TabControl}}" />
If you add a public property called "TabControl" to the parent window that returns a reference to the TabControl, you should be able to bind to it like this from any child element of the window:
<Binding Path="TabControl.ActualWidth"
RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" />

WPF: Validation.ErrorTemplate not displaying

I have a template for displaying a red border and an error message around controls. It works (tested on TextBoxes and ComboBoxes). But on two particular comboboxes they don't.
Well let's see what's different in the VM:
since I have common validation implementation in my base class, no change there
the same kind of asynchronously loaded data is displayed that works well with validation with just one panel over
so in short, no difference in VM.
The view is completely the same, the same style is applied, so in short no difference there either.
So I added NotifyOnValidationError=True to the ValidatesOnDataErrors=True already there, and subscribed to Validation.Error... And it fired!!! Yet the template still doesn't show. I'm out of ideas, please suggest me things to check!
EDIT: further research:
I've decompiled DataErrorValidationRule, and recompiled it as MyDataErrorValidationRule to match the original as close as possible. I removed ValidatesOnDataErrors=True, and added my ValidationRule to debug. It returned new ValidationResult(false, (object)str); with str containing the correct error message twice - once for setting the property to null, and once for forcefully validating the entire object. Template still not showing.
I've also checked Validation.GetErrorTemplate on the control (at the time of the first firing of Validation.Error) and it was NOT NULL, so it's not the DynamicResource that failed either.
EDIT: working example:
<ItemsControl ItemsSource="{Binding QuestionAnswers}">
<ItemsControl.Resources>
<!-- ... -->
<DataTemplate DataType="{x:Type Model:QuestionAnswerModel}">
<StackPanel>
<!-- here is the combo box -->
<ComboBox Margin="8,4" Padding="8" MinWidth="120" HorizontalAlignment="Left"
Validation.ErrorTemplate="{DynamicResource DefaultValidationErrorTemplate}"
ItemsSource="{Binding Options.Source}"
DisplayMemberPath="ItemName" SelectedValuePath="ItemID"
SelectedValue="{Binding Options.SelectedID, ValidatesOnDataErrors=true}" />
</StackPanel>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
non-working example:
<ComboBox Margin="8,4" Padding="8" MinWidth="120" HorizontalAlignment="Left"
Validation.ErrorTemplate="{DynamicResource DefaultValidationErrorTemplate}"
SelectedItem="{Binding Type.SelectedItem, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Validation.Error="ComboBox_Error"
ItemsSource="{Binding Type.Source}"
DisplayMemberPath="Localized"
>
They are from the same xaml file, the ItemsControl containing the working ComboBox is in the same Grid as the non-working ComboBox.
The only difference is whether SelectedItem or SelectedValue is bound, but that shouldn't have any bearings on the validation...
I had the exact problem with the error template not displaying even though the event was firiing, and could never figure out why it only happened for some controls and not others.
The workaround I eventually found was to set the ValidationStep to ConvertedProposedValue on the ValidationRule for the binding:
<TextBox>
<TextBox.Text>
<Binding Path="MyField">
<Binding.ValidationRules>
<Validation:MyValidationRule ValidationStep="ConvertedProposedValue" Message="Please enter a value." />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
That seemed to do the trick for me anyway!
EDIT: If you are using IDataErrorInfo, you could try (though I haven't personally tested it):
<Binding Path="MyField" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<DataErrorValidationRule ValidationStep="ConvertedProposedValue" />
</Binding.ValidationRules>
</Binding>
i.e. remove ValidatesOnDataErrors=True, which is just a shortcut for including a single <DataErrorValidationRule />
i would check the following:
check datacontext and binding for your combobox, if it works and IDataErrorInfo is called (i assume you do IDataErrorInfo validation) - next step
copy your validation template as a local resource to your ComboBox.Resources and check if it works
nevertheless it would be good if you post your validation template, your combobox xaml and your datacontext

MultiBinding with MultiValueConverter does not update

it seems that I have a problem with my multibinding.
Scenario:
I have a window with two datepickers and a listview.
The listliew contains some data bound elements called "entries". An entry has a property called "date".
I just want my listview to show entries whose date is in between my two datepickes dates.
My xaml code for binding the listview to the entries and dates:
<ListView.ItemsSource>
<MultiBinding Converter="{StaticResource EntriesFilterConv}"
UpdateSourceTrigger="PropertyChanged">
<Binding Path="Entries" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="EntryFromDate" Path="SelectedDate"
UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="EntryToDate" Path="SelectedDate"
UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</ListView.ItemsSource>
However, this doesnt work. My converter is called when a SelectedDate changes but its never called when Entries changes.
With normal data binding like this:
<ListView ItemsSource="{Binding Entries}">
...
</ListView>
The listview updates normally.
Any idea?
After searching for hours, I find a simple and decent answer !
Since ObservableCollection doesn't raise PropertyChanged event but CollectionChanged, we just have to bind the collection's Count to fire the event when the list changes :
<MultiBinding Converter="{Resources:ListToStringConverter}">
<Binding Path="List.Count" />
<Binding Path="List" />
</MultiBinding>
Original infos about this perfectly working multibinding here : https://stackoverflow.com/a/10884002/817504
I think the following might cause this: If you bind directly to the Entries the ListView will listen to CollectionChanged events, but if such a binding is inside a MultiBinding the only thing that would cause a reevaluation could be a PropertyChanged notification, which might not exist for the Entries property in your model.
Maybe you can subscribe to the CollectionChanged event of your collection and raise a PropertyChanged event or get the BindingExpression within your MultiBinding to call an update manually.

WPF, passing variable to converter inside data template

I assume this is possible but not sure how to do it. I need to pass the value of a class level variable to a converter, from within side a data template.
<DataTemplate x:Key="ResponseItemTemplate">
<StackPanel Orientation="Horizontal" >
<StackPanel.Visibility>
<MultiBinding Converter="{StaticResource VisibilityConverter}">
<Binding Path="Key"/>
<Binding Path="CurrentLanguage"/>
</MultiBinding>
</StackPanel.Visibility>
<TextBox Width="200" Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
The 'Key' value exists on the response item for the data template so this gets passed correctly, whereas the CurrentLanguage is a class variable and I can't get that to pass properly to the converter. Any ideas?
Thanks for the replies, this is what I needed to use in the end:
<Binding Path="DataContext.CurrentLanguage" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}"/>
You can use the binding object as follows:
<Binding Source="{x:Static local:DataObject.MyData}" />
See: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c94682e5-ad16-42f9-973f-fd7588a9c0b5.
If you define the converter as a resource, which you have, you can access it in the code behind. Once you have the converter you can then set a property on it.
var myVisConverter = (VisibilityConverter)window.Resources["VisibilityConverter"];
myVisConverter.CurrentLanguage = ...
EDIT Ok, if you're trying to get access to the parent DataContext from within the DataTemplate, there's a couple of options. Easiest is to name the control with the correct DataContext, then bind to that control like so...
<Binding Path="DataContext.CurrentLanguage" ElementName="nameGivenToElement" />
Josh Smith wrote an article with more ways of getting inherited DataContexts.

WPF Converter problem

So I have an object that implements INotifyPropertyChanged, and I have a property that when it changes, it call the PropertyChanged event all right, but when I use a converter like this:
<Image Grid.Column="0">
<Image.Source>
<Binding Path="IsInstrumentStatusOk" UpdateSourceTrigger="PropertyChanged">
<Binding.Converter>
<converters:BooleanToImageConverter
ImagePathIfFalse="/Images/InstrumentStatusBar/Instrument_Status_Alarm.png"
ImagePathIfTrue="/Images/InstrumentStatusBar/Instrument_Status_OK.png" />
</Binding.Converter>
</Binding>
</Image.Source>
</Image>
For some reason it doesn't update it, and it doesn't call the converter. If I use it like normally
Source="{Binding MyProperty, Converter={StaticResource MyConverter}}"
It works, but I don't want to use it like that because I have a bunch of converters that I want to use with different images. Any idea why it doesn't update?
Thanks.
You're setting UpdateSourceTrigger="PropertyChanged" in your XAML. This means when the target property changes the value should update back to the source. Obviously nothing is ever changing the Image::Source property.
Just remove the UpdateSourceTrigger setting altogether and you should be fine.

Resources