why the converter is not running? - c#

I am using the MVVM pattern and in my view I have this dataGrid:
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource myMultiValueConverter}">
<MultiBinding.Bindings>
<Binding />
<Binding ElementName="ThisControl" Path="DataContext.MyObservableCollectionInViewModel"/>
<Binding ElementName="thisControl" Path="DataContext.ControlType"/>
<Binding ElementName="ThisControl" Path="DataContext.ExternalItems"/>
<Binding Path="Componentes.OneProperty"/>
</MultiBinding.Bindings>
</MultiBinding>
</Setter.Value>
</Setter>
And my view model has this code:
private void myMethod()
{
MyObservableCollectionInViewModel.Clear();
MyObservableCollectionViewModel.Add(new myType());
}
When I execute the method MyMethod(), if a I am not wrong, the multi value converter would be run, because the ObservableCollection implements INotifyPropertyChanged when I add or remove items, but in this case does not work.
However I have another ObservableCollection for the DataSource of my dataGrid and it works as it's expected, refresh the dataGrid when I add or remove items from the ObservableCollection.
However, if in myMethod I do this:
private myMethod()
{
myObservableCollectionInMyViewModel.Clear();
myObservableCollectionInMyViewModel.Add(new MyType());
myObservableCollectionInMyViewModel = new ObservableCollection(myObservableCollectionInMyViewModel);
}
It works, so the view is notified when I create a new ObservableCollection, not when I add or remove items from the actual ObservableCollecion.

Yes, that's the correct behavior.
The reason the ObservableCollection Add/Remove works for ItemsControl, ListBox, DataGrid etc, is that they are explictly taking care of the behavior you are describing, that is not WPF specific, eg: it doesn't have to do with the actual binding of ItemsSource.
What happens under the cover is, all these controls(ListBox, etc..) are inheriting from ItemsControl which ultimately wraps ItemsSource into CollectionView, which will take advantange of INotifyCollectionChanged interface, if possible. That's how it knows / keeps up.
The "work-around" I've used successfully:
A) Just use property changed or do the swap as you've done (this might, or might not work - I don't exactly remember, but WPF might have explicit check if the actual value has been changed, in this case: it hasn't):
myObservableCollectionInMyViewModel.Clear();
myObservableCollectionInMyViewModel.Add(new MyType());
RaisePropertyChanged(() => myObservableCollectionInMyViewModel);
B)
myObservableCollectionInMyViewModel =
new ObservableCollection(new List<MyType>{
new MyType()});
C) Bind against .Count, as ObservableCollection will notify when that changes.
<Binding ElementName="ThisControl"
Path="DataContext.MyObservableCollectionInViewModel.Count"/
D) Create a new converter which will be able to listen all the events(INotifyPropertyChanged and INotifyCollectionChanged) events, which then will trigger multi converter updates.
<Binding ElementName="ThisControl"
Converter="{StaticResource observableConverter}"
Path="DataContext.MyObservableCollectionInViewModel"/>

Related

How to use TargetUpdated in WPF?

I am attempting to use TargetUpdated in WPF.
The following is a snippet from my XAXML file.
<TextBlock TargetUpdated="QuantityRequested_TargetUpdated">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} Quantity {1} {2}">
<Binding Path="ProductName" NotifyOnTargetUpdated="True"/>
<Binding Path="QuantityRequested" NotifyOnTargetUpdated="True"/>
<Binding Path="PreCouponQtyAdjPrice" NotifyOnTargetUpdated="True"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
I have the following in my code behind file in my view class.
public partial class CheckoutView : UserControl
{
/* Additioal code omitted. */
public void QuantityRequested_TargetUpdated(
Object sender, DataTransferEventArgs args)
{
}
}
I am getting the following error.
System.Windows.Markup.XamlParseException
HResult=0x80131501
Message=Failed to create a 'TargetUpdated' from the text 'QuantityRequested_TargetUpdated'.
Source=PresentationFramework
...
Inner Exception 1:
ArgumentException: Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.
Note: I updated the question to incorporate changes suggested by #BionicCode. The changes made no difference. I am still getting the same error.
I'm sorry to say, but everything is wrong with your example.
You XAML syntax is wrong.
There are two ways of defining a Binding in XAML: attribute syntax (using the markup extension Binding) or property element syntax (by explicitly defining a Binding object as object node).
Attribute syntax
The Binding is defined using it as markup extension (Binding also extends MarkupExtension). The properties of Binding are set using the markup extension syntax:
<Object Property="{Binding Source}" />
Property element syntax
Binding is defined as a XAML object nested in to a property. In this case the properties of Binding itself are set using the attribute syntax:
<Object>
<Object.Property>
<Binding Path="QuantityRequested"
NotifyOnTargetUpdated="True" />
<Object.Property>
<Object>
The Binding.TargetUpdated is an attached event. You must therefore attach it to a DependencyObject or use the alias defined by FrameworkElement (FrameworkElement.TargetUpdated).
Binding is not a DependencyObject, so attaching the property to Binding won't compile.
Note, Binding itself does not raise this event, meaning it is not the event source. The Binding.Target (the TextBlock in your case) is the source of this event. The event is actually triggered by the BindingExpression, created for the associated Binding, using a corresponding invocation method of the FrameworkElement (the Binding.Target), which then finally raises the event.
However, all that matters is that since it is the TextBlock that raises the event, you must register the event handler with the TextBlock (and not Bindinglike in your wrong example).
The following examples assume that you have a TextBlock_TargetUpdated event handler defined.
Example that uses the FrameworkElement.TargetUpdated alias event (instance event)
<TextBlock TargetUpdated="TextBlock_TargetUpdated">
<TextBlock.Text>
<Binding Path="QuantityRequested"
NotifyOnTargetUpdated="True" />
<TextBlock.Text>
</TextBlock>
Example that uses the original Binding.TargetUpdated attached event
<TextBlock Binding.TargetUpdated="TextBlock_TargetUpdated">
<TextBlock.Text>
<Binding Path="QuantityRequested"
NotifyOnTargetUpdated="True" />
<TextBlock.Text>
</TextBlock>
Next is the event handler: the event handler must be an instance member. It can't be static (class member):
private void QuantityRequested_TargetUpdated(Object sender, DataTransferEventArgs args)
{
}
To make it finally work, you also have to fix your Binding.StringFormat value.
If there is no text preceding the first placeholder, you must start the format string with an empty curly brace {}:
<MultiBinding StringFormat="{}{0} Quantity {1} {2}">
Putting everything together, the final fix could look like this:
<TextBlock TargetUpdated="QuantityRequested_TargetUpdated">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} Quantity {1} {2}"
NotifyOnTargetUpdated="True">
<Binding Path="ProductName" />
<Binding Path="QuantityRequested" />
<Binding Path="PreCouponQtyAdjPrice" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
private void QuantityRequested_TargetUpdated(object sender, DataTransferEventArgs e)
{
}

DataContext accessible from IMultiValueConverter

I have WPF application and I need to bind two arguments (no matter what they are) to calculate the value (checkbox checked or not). So I have to use IMultiValueConverter and that's fine.
But is there a way, to give this converter access to DataContext (ViewModel) of a window I am binding to?
Basically I have some checkboxes in treeview, i need to pass to converter content (text) of theese checkboxes and its parent's header. Then in converter I need to process that text and find out if it's present in some collection I have in my ViewModel (DataContext). I know that I cannot use ConverterParameter, because it doesn't support binding.
Just add another Binding to your MultiBinding that binds to the view model, e.g.:
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Property1" />
<Binding Path="Property2" />
<Binding Path="DataContext" RelativeSource="{RelativeSource AncestorType=Window}" />
</MultiBinding>

DependencyProperty Binding with ValidationRule not updating Source when ValidationResult is false

A ComboBox.SelectedItemProperty is bound TowWay to a DependencyProperty in
The Control.
In the ControlTemplate :
<ComboBox IsEditable="True">
<ComboBox.SelectedItem>
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="SomeDP" Mode="TwoWay" NotifyOnValidationError="True">
<Binding.ValidationRules>
<vld:DeleteAfterInitValidationRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedItem>
</ComboBox>
When the ValidationRule returns false
return ValidationResult(false,msg);
The Dependency Property bound to selected item is not updated.
Is there a way to force the binding to update the source ?
*Please if any one is going to raise a discussion here about
BindingExpression.UpdateSource() , please supply a working example, and not just blurt it out because it sounds like the solution ,
i do not intend to use Explicit mode on my Binding.
Further more i can easily code my way around this but a good .net programmer should thrive to write less code and use the built in mechanisms supplied by the .net framework ,
And that is the soul purpose of this question , is there a built in way of updating the source while
notifying of a DataError ?

Storing ValueConverter to variable

I am having a ValueConverter used for binding 'To' Value in a StoryBoard animation, similar to the answer - WPF animation: binding to the “To” attribute of storyboard animation.
The problem is I am repeating the below piece of code for MultiBinding ValueConverter in couple of places.
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
I want to remove this duplicate code by storing the result of the ValueConverter to a resource variable so I can bind this local Variable directly to the story board.
<system:Double x:Key="CalculatedWidth">
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</system:Double >
I am getting the following error:
The type 'Double' does not support direct content.
Cannot add content to an object of type "Double".
I feel this is a common problem but not able to find a solution to remove this redundancy.
Update
Thanks Rohit, your answer solved the problem. But I have one more related issue, So updating the question. This variable CalculatedWidth works fine in normal case, but when it is used in RenderTransform it doesn't pick up the value. i.e. If I use the normal way to use Converter it works but it doesn't pick up the variable.
<StackPanel.RenderTransform>
<TranslateTransform x:Name="SliderTransform">
<TranslateTransform.X>
<Binding Converter="{StaticResource PanelConverter}" ElementName="SliderPanel" Path="ActualWidth" /> // Works
<Binding Path="Width" Source="{StaticResource CalculatedWidth}"/> // Doesn't Work
</TranslateTransform.X>
</TranslateTransform>
</StackPanel.RenderTransform>
I have kept the variable as part of the local resource. Does this mean the variable doesn't get created when Render transform is called?
As the error suggest you can't bind with Double. Binding can be done with only Dependency properties.
Instead use FrameworkElement in resource and bind its Width(DP) like this:
<FrameworkElement x:Key="CalculatedWidth">
<FrameworkElement.Width>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</FrameworkElement.Width>
</FrameworkElement>
and you can bind with this resource like in this sample:
<TextBlock Width="{Binding Width, Source={StaticResource CalculatedWidth}}"/>
A System.Double doesn't implements INotifyPropertyChange (and doesn't show a Value property to notify on) nor it implements dynamic properties advanced binding mechanisms. So it cannot notify of its changes.
The problem with local resources is their instanciation : they do not have visibility to hosting namescope because they are instanciated outside it. So doesn't bind to nothing and the binding returns DependancyProperty.UnsetValue.
The is relative to the FrameworkElement resource itself and returns its Tag property value: null.
If you use VS2013 with .NET 4.5 (maybe it works also with VS2012/.NET 4.0), look at Output window for data binding trace :
System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'ElementName=ExpanderContent'. BindingExpression:Path=ActualHeight; DataItem=null; target element is 'FrameworkElement' (Name=''); target property is 'Width' (type 'Double')
Distinct solutions are offered to you : you can move the FrameworkElement outside the local resources (remember that you probably have to add HorizontalAlign="Left" to allow Width changes. Another solution is to add a dependancy property to the code behind.Finally, you want to share the result of your multiplier converter between more than one control (or properties): The simplest way is maybe to bind it the first property of the first control and to bind other controls properties to this first property.

Use another element's ItemsSource property in a MultiBinding

I'd like to use the ItemsSource property from a particular element as one of the bindings in another element's MultiBinding. Here's what I have so far:
<Label>
<Label.Content>
<MultiBinding Converter="{converters:myMultiValueConverter}">
<Binding Path="PageIndex" />
<Binding ElementName="anotherElement" Path="ItemsSource"/>
</MultiBinding>
</Label.Content>
</Label>
This works once (when the ItemsSource is initially set), but the binding fails to update when the ObservableCollection bound to the original element's ItemsSource property has items added or removed. Is this kind of binding possible?
Add a dummy binding (- you don't need the value -) like this to force the MultiBinding to be reevaluated:
<Binding ElementName="anotherElement" Path="ItemsSource.Count"/>
Edit: Just noticed a flaw: If you move items that would not register if that does not change the Count property in-between, maybe this is relevant for you. In that case you could bind to your own dummy for which you can fire change notifications upon CollectionChanged (not all that clean in any case though).
You might want to consider HighCore's suggestion, a get-only property that returns the calculated value for which you manually fire PropertyChanged in all places that it depends on is usually quite convenient.

Categories