How to use TargetUpdated in WPF? - c#

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)
{
}

Related

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>

why the converter is not running?

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"/>

Get parent of Binding

I have a Binding object and I want to know parent of this Binding.
Example:
<Label Name="BindingLabel" Content="{Binding Path=LabelText}"/>
In example parent of Binding is Label with name BindingLabel and I search way to get it only from Binding object.
Thanks!
A Binding can be used on multiple objects, so it can't have a single Parent. If you know what objects you're looking through (e.g. you already have a list, or you can walk a tree) and what dependency properties to check, you can check if the element uses that binding.
Here's how you can do that if you have the exact instance of Binding that's used:
Binding binding = // whatever (must be the same instance used in the object)
IEnumerable<Label> labels = // whatever
foreach (var label in labels)
{
if (label.GetBindingExpression(Label.ContentProperty).ParentBinding
== binding)
{
// found it!
}
}
Or maybe you just want to compare the path:
var path = "LabelText";
IEnumerable<Label> labels = // whatever
foreach (var label in labels)
{
if (label.GetBindingExpression(Label.ContentProperty).ParentBinding.Path.Path
== path)
{
// found it!
}
}
Your question is kindof vague because you don't describe what you intent to do with it. If you want to set a particular property of the label you could use a valueconverter. Additionally you can pass in a reference to the object itself by using RelativeSource=Self in a (multi)binding
<Label>
<Label.Content>
<MultiBinding Converter="{StaticResource MyConverter}">
<MultiBinding.Bindings>
<Binding Path="LabelText" />
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</Label.Content>
</Label>
i didn't tested the code so i might be off with the binding declaration or something.

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.

How can I set the DataBinding to an arbitrary Property in XAML?

I have the following problem: I am designing an UserControl, a graded-color gauge. I have decieded to use the MVVM design patern, which turns out to be a good choice. However, I have the following problem. In the View XAML file, I try to convert a value to a color, using custtom converter, which needs 2 parameters. For this purpose, I use the MultiBinding:
<ItemsControl ItemsSource="{Binding Path=ViewData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Height="2">
<Rectangle.Fill>
<MultiBinding Converter="{StaticResource colorConverter}">
<Binding Path="Value"/>
<Binding Source="{StaticResource Palette_ICOS}"/>
</MultiBinding>
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The thing is, that I don't want to use
{StaticResource Palette_ISO1}
as a second parameter, but a Property, which is a direct property of the DataContext and is not a property of the ViewData collection member. I have tried several ways to accomplish this scenario, but without signifficant success.
As last, I have tried the following:
<Binding Path="CurrentPallete"/>
and the CurrentPallete looks like:
public Palette CurrentPalette
{
get { return _currentPalette; }
set
{
_currentPalette = value;
}
}
i.e. a Property in the Class, whose instance is set as a DataContext of the main control, which hosts the ItemControl. What I get is a
[0x00000001] = {DependencyProperty.UnsetValue}
value in the Debugger, when the corresponding convertor is invoked,which probably means, that the Property cannot be found. Can anyone point out what is the way to achieve the desired effect? Thanks a lot!
<Binding Path="DataContext.Palette_ICOS"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}" />
Have you tried
<Binding Path="Palette_ICOS"/>
If Palette_ICOS is a property in the current item's DataContext binding it should work.

Categories