Storing ValueConverter to variable - c#

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.

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>

Reusable DataTemplate (in Resources) using a convertor that needs a parameter

I have to display in a grid 12 columns (one for each months) with different data.
In order to correctly display the data, I use DataTemplates for every column and multibindings. The multibindings specify a converter which is parametrized with the index of the month.
<DataTemplate x:Key="CustomerForecastQuantityDataTemplate">
<TextBlock TextAlignment="Right">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ForecastCustomerQuantityConverter}" **ConverterParameter="0"**>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type view:BaseView}}" Path="DataContext.ForecastPeriods"/>
<Binding Path="ForecastQuantities"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
I am trying to make the xaml look a bit more elegant, and have one DataTemplate defined as a static resource, and use it everywhere, like this:
<forecast:ForecastCustomerMonthQuantityGridViewDataColumn
Header="{Binding RelativeSource={RelativeSource Self}, Path=MonthIndex}"
HeaderTextAlignment="Center"
HeaderTextWrapping="Wrap"
Width="60"
IsReadOnly="True"
MonthIndex="1"
CellTemplate="{StaticResource CustomerForecastQuantityDataTemplate}"/>
My question is, how can I make this DataTemplate take a diferrent parameter depending on the MonthIndex value of each ForecastCustomerMonthQuantityGridViewDataColumn
Thanks a lot, every suggestion is highly appreciated (it might be that I don't have a good understanding of the DataTemplate concept)
I can't think of a XAML-only way to get at "MonthIndex" from within the cell template (and on a side note ConverterParamter doesn't support binding). Off the top of my head, you could try something like this (I'm away from my PC so not able to try it myself):-
Add a third binding to your multi value converter, bound to the cell being templated, something like:
<binding RelativeSource="{RelativeSource AncestorType={x:Type DataGridCell}}" />
Your multi value converter now has access to the cell, which in turn exposes a Column property, which you can cast to ForecastCustomerMonthQuantityGridViewDataColumn to get at "MonthIndex".

Consolidate duplicate WPF Bindings

Apologies if the title doesn't make much sense but I couldn't think of a good wording.
Basically I have a user control where many controls have the binding:
<TextBox.IsEnabled>
<MultiBinding Converter="{StaticResource AndLogicMultiValueConverter}">
<Binding Path="Binding1" />
<Binding Path="Binding" Converter="{StaticResource BoolToOppositeBoolConverter}" />
</MultiBinding>
</TextBox.IsEnabled>
This is repeated numerous times and makes the xaml look pretty untidy, i'm not sure whether it will also have a performance hit?
Is there any way to put this binding in the usercontrol resources and then bind to that?
Thanks
Greg
How about using a Style with a Setter for IsEnabled with that binding?

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