Consolidate duplicate WPF Bindings - c#

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?

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>

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

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.

DataGridColumn SortMemberPath on MultiBinding

I'm trying to have column sort on numeric content. Multi-binding converter works fine.
This solution will set SortMemberPath to null
I've tried a variety of ways, and scoured the internet substantially.
Code has been modified from original for security purposes.
<DataGridTemplateColumn x:Name="avgPriceColumn">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource avgPriceConverter}">
<Binding Path="NumberToDivideBy" />
<Binding Path="TotalDollars" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.SortMemberPath>
<MultiBinding Converter="{StaticResource avgPriceConverter}">
<Binding Path="NumberToDivideBy" />
<Binding Path="TotalDollars" />
</MultiBinding>
</DataGridTemplateColumn.SortMemberPath>
</DataGridTemplateColumn>
EDIT:
I found a way to get data binding to work without multibinding, but sorting still doesn't work. Since DataGrid is bound to a custom class, I take in whole value and convert from that, thus reducing the need for MultiBinding.
<DataGridTextColumn x:Name="avgPriceColumn" Binding="{Binding Converter={StaticResource avgPriceConverter}}" SortMemberPath="{Binding Converter={StaticResource avgPriceConverter}}" />
On both these options SortMemberPath is default set to Binding so I don't need to explicitly define it as I have
However this ends up setting SortMemberPath value to null which conflicts with custom constraints applicable to my code environment, and doesn't sort. So I am still interested in better solutions.
EDIT:
Changed conflicting code elsewhere to allow duplicate SortMemberPath's, don't support sorting on some columns, and for some sort off neighbouring-column value
SortMemberPath is expecting the name of a property (e.g. "TotalDollars") not an individual computed row value. Think of it like the header, you set it once for the whole column. Your converter would be returning a number like 15 where SortMemberPath wants a binding path string.
Two options that come to mind:
Provide a computed property on your backing object (e.g. "AveragePrice") and bind to that. No converter or sort member path necessary.
public double AveragePrice
{
get { return TotalDollars / NumberToDivideBy; }
}
Specify an OnSorting event handler like in this question.
Hope it helps. :)

Categories