WPF - Workaround for ValidationRule not being a DependencyObject - c#

What is the best solution for not being able to use data binding on a ValidationRule property since ValiationRule is not a DependencyObject?
Below is an example of what I would like to do. I want to be able to validate the text in the TextBox against some other DependencyProperty.
<TextBox Name="myTextBox">
<TextBox.Text>
<Binding Path="MySource" UpdateSourceTrigger="PropertyChanged">
<base:EqualsRule Target="{Binding MyTarget}" />
</Binding>
</TextBox.Text>
</TextBox>

You could use Josh Smith's virtual branch approach.

Related

MultiBinding across multiple DataTemplates with same ItemsSource

I have two data templates that make use of a multi-binding to control the visibility of a button, e.g. in data template A
<Button.Visibility>
<MultiBinding Converter="{StaticResource HideFirstOrderedItemConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" Path="Items"/>
<Binding Path="Entity"/>
</MultiBinding>
</Button.Visibility>
and in data template B
<Button.Visibility>
<MultiBinding Converter="{StaticResource HideFirstOrderedItemConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" Path="Items"/>
<Binding Path="Entity"/>
</MultiBinding>
</Button.Visibility>
The data templates are bound to the same list box items source and a data template selector is used to determine which datatemplate to use based on a property of the item in the list (Note: the datatemplate forms part of the ItemTemplate for the ListBox).
Question: Will wpf trigger the MultiBinding converter HideFirstOrderedItemConverter if either the binding to Items or Entity changes, or do both Items and Entity have to change?
Currently my binding on data template A doesn't fire when the Items property changes (an ObservableCollection that has items added and removed) and Entity does not change. Ultimately I would like the binding to fire on both data templates whenever the Items property changes.
If I comment out the binding to Entity in both templates as follows
<Button.Visibility>
<MultiBinding Converter="{StaticResource HideFirstOrderedItemConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" Path="Items"/>
<!-- <Binding Path="Entity"/> -->
</MultiBinding>
</Button.Visibility>
My converter gets hit when I add an item to the collection, but only for the data template asscoiated with the item just added. I'd like it to get hit for all items.
UPDATE:
The comments below from #ASh and #GazTheDestroyer helped my understanding here. I need to notify my view of a collection changed event using a property changed event (collection changed events are NOT surfaced to the binding framework). So by updating my XAML to
<Button.Visibility>
<MultiBinding Converter="{StaticResource HideFirstOrderedItemConverter}" Mode="TwoWay">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}" Path="DataContext.Entities"/>
<Binding Path="Entity"/>
</MultiBinding>
</Button.Visibility>
where DataContext.Entities is the undelying ObservableCollection, and in the bound view model hook into the collection changed event of that ObservableCollection, i.e.,
Entities.CollectionChanged += CollectionChanged
where the CollectionChanged callback raises the notify property changed event on the Entities ObservableCollection. I believe this is the way to do it (might be nice to abstract this behaviour into its own type).
Will wpf trigger the MultiBinding converter HideFirstOrderedItemConverter if either the binding to Items or Entity changes, or do both Items and Entity have to change?
Either. The problem is that the Items property of the ListBox isn't set when you add or remove items from the source collection. Bind to Items.Count instead:
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBox}}"
Path="Items.Count "/>
This property will change when you add or remove items from the source collection.
Also note that your Entity property must raise the PropertyChanged event for the converter to be re-invoked when you set it.

MultiBinding with PriorityBinding workaround

I'm currently working on an app in which borders are displaying objects in a "change-state". The visibility of these borders is controlled using "BorderThickness" and a priority binding (the visibility is set in a converter). Now there's a request to make the thickness of the change-state border configurable (obtained at runtime).
My initial attempt was changing the converter into a multi-binding converter:
<Border.BorderThickness>
<MultiBinding Converter="{StaticResource StringToBorderThickness}">
<PriorityBinding>
<Binding Path="Change1" />
<Binding Path="Change2" />
</PriorityBinding>
<Binding Path="DataContext.Settings.ChangeStateThickness" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}" />
</MultiBinding>
</Border.BorderThickness>
This does NOT work because WPF does not support MultiBinding+PriorityBinding at the same time (as it turns out).
Can anyone recommend a workaround? I can't remove the Priority Binding and I can't find a different way to control the border visibility (BorderVisibility also makes the object IN the border invisible)
Thank you.

How to call CanExecute when validation cancels UpdateSourceTrigger

I am using a ValidationRule on a TextBox in my View, and that ValidationRule is working properly.
However, a problem arises when I am in the state where the canExecute method for a Command (on a Button in this case) has returned true (Button is enabled), and then the user changes the contents of the TextBox so the ValidationRule returns a false ValidationResult. This results in the ViewModel property bound to the Button.Text not being updated, which means the canExecute method still thinks results are good and returns true.
So - how can I get the ViewModel property in question to update in spite of the false ValidationResult? Or is there another way of doing all this?
Edit:Here is the XAML for my TextBox:
<TextBox HorizontalAlignment="Left" Margin="67,50,0,0" VerticalAlignment="Top" Width="27">
<TextBox.Text>
<Binding Path="MachineNo" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MachineNoValidate/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
After doing some more research, the solution appears to be implementing the IDataErrorInfo interface on my ViewModel. I then have full access to the current contents of the TextBox through the bound property.
Here is the new XAML:
<TextBox HorizontalAlignment="Left" Margin="67,50,0,0" VerticalAlignment="Top" Width="27"
Text="{Binding MachineNo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True}"/>
Only this[] needs to be implemented; WPF does not use the Error property.

Implementing IValueConverter to get Name from ID

I have a table column called LOCATION_ID. I want to show the LOCATION_NAME which is located in the LOCATION table instead of the ID.
I am trying to implement IValueConverter but can't figure out how to do it. I am using WPF with entity framework.
How would I pass the ID value to this converter?
I have a method name GetLocationNameByID(). Where in the converter would I Call this method? And how would I bind the return value to the datagrid XAML?
Implementing the IValueConverer interface is pretty straightforward. In the XAML, you'd have something like this:
<Window x:Class="CarSystem.CustomControls.AlarmDisplayer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNameSpace"
DataContext="{Binding Path=MyDataContextObject, RelativeSource={RelativeSource Self}">
<Window.Resources>
<local:MyIValueConverter x:Key="Converter" />
</Window.Resoures>
<TextBox Text="{Binding Converter={StaticResource Converter} Path=MyProperty}" />
</Window>
When WPF detects a change in the value of the MyProperty in the MyDataContextObject, it calls the MyIValueConverter object's Convert method, passing the value of the property as the Value parameter. Your implementation of the Convert method does what it has to do & returns the string to be displayed.
you can use multi-binding, have your value converter implement the IMultiValueConverter interface. The converter takes an object array, each of which can be a binding to your XAML.
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource MyConverter}">
<MultiBinding.Bindings>
<Binding Path="SomeProperty" />
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</TextBox.Text>
</TextBox>

WPF MultiBinding fails in Syncfusion TabItemExt header

I have a subclass of TabItem as follows, for which I'm trying to set the Header property. I've tried this with a MultiBinding:
<DataEditPane x:TypeArguments="MyType" x:Class="MyDataEditPane">
<DataEditPane.Header>
<MultiBinding StringFormat="Hello world {0} {1}">
<Binding Path="BoundVariable1" />
<Binding Path="BoundVariable2" />
</MultiBinding>
</DataEditPane.Header>
</DataEditPane>
But it fails as such:
System.Windows.Data Error: 28 : MultiBinding failed because it has no valid Converter. MultiBindingExpression:target element is 'MyDataEditPane' (Name=''); target property is 'Header' (type 'Object')
System.Windows.Data Error: 28 : MultiBinding failed because it has no valid Converter. MultiBindingExpression:target element is 'MyDataEditPane' (Name=''); target property is 'Header' (type 'Object')
I'd always thought the StringFormat served the role of the converter, but perhaps not?
Wrapping the fields together in some kind of container, like a Label, also doesn't seem to work:
<DataEditPane x:TypeArguments="MyType" x:Class="MyDataEditPane">
<DataEditPane.Header>
<Label>
<Label.Text>
<MultiBinding StringFormat="Hello world {0} {1}">
<Binding Path="BoundVariable1" />
<Binding Path="BoundVariable2" />
</MultiBinding>
</Label.Text>
</Label>
</DataEditPane.Header>
</DataEditPane>
In this case, the .ToString() representation of the label ("System.Windows.Controls.Label") is shown as the header.
Note that a single binding works just fine:
<DataEditPane x:TypeArguments="MyType" x:Class="MyDataEditPane">
<DataEditPane.Header>
<Binding Path="BoundVariable1" />
</DataEditPane.Header>
</DataEditPane>
If it matters, I'm using the Syncfusion TabItemExt as one of my superclasses in the inheritance hierarchy, but as that class doesn't override the Header property I don't think that makes a difference.
What am I doing wrong? I know I can make another property in the ViewModel to act as the Header (and then single-bind that) but I want to learn how to do this properly in XAML.
Try a TextBlock instead of a Label. The following code worked fine for me.
I tried this:
<Window x:Class="ListBox.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ListBox" Title="Window1" Height="300" Width="300">
<Window.DataContext>
<local:TextVM/>
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Text1}" />
<TextBox Text="{Binding Text2}" />
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="Hello World {0} - {1}">
<Binding Path="Text1" />
<Binding Path="Text2" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
I wonder if StringFormat is only valid in cases where a string is expected rather than an object.
There's an example on MSDN here: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.stringformat.aspx
Multi-Bindings requires a converter, i think that a converter that you may use is the StringFormatConverter, it is a IMultiValueConverter so works for multibindings.
Maybe you should adapt it to your case.
Hope this could be useful for you...

Categories