I've written a general purpose prefix operator based multibinding converter that takes a list of binding values that can be either operators or values, it then computes the value based on the pre-defined behaviour of the defined set of operators.
So like this:
<MenuItem.Background>
<MultiBinding Converter="{StaticResource ArithmeticsConverter}">
<Binding Source="?"/>
<Binding Path="IsDirty"/>
<Binding>
<Binding.Source>
<SolidColorBrush Color="#dddf05"/>
</Binding.Source>
</Binding>
<Binding>
<Binding.Source>
<SolidColorBrush Color="#F0F0F0"/>
</Binding.Source>
</Binding>
</MultiBinding>
This returns #dddf05 if Dirty, otherwise #f0f0f0.
So this is very convenient since it's pretty much the only converter I need now, the xaml is verbose but at least I don't have to write converters for every specific case.
My problem is that I want to do something like:
?
=
null
SomeVariable
Visible
Collapsed
And set it to
<MyControl.Visible>
I.e. if SomeVariable is null, return Visible, otherwise Collapsed. But I havn't been able to reference Visibility values (or more generally, system enum values) like I am referencing color values above. I know it must be possible somehow since one can easily reference these values inline.
You can use these:
<Visibility>Visible</Visibility>
<Visibility>Hidden</Visibility>
<Visibility>Collapsed</Visibility>
The XAML processor will basically take the inner string of the tag and parse/convert it to enum value.
Related
I have a multilanguage application and I'm trying to set as Fallbackvalue a Dynamic resource in this way:
<TextBlock Text="{Binding SomeProperty, FallbackValue='{DynamicResource somekEY}'" />
this will throw an exception:
You can set 'DynamicResourceExtension' for the 'StringFormat' type 'Binding' property. You can set 'DynamicResourceExtension' only for a DependencyProperty of a DependencyObject.
how can handle this situation?
The issue is that DynamicResource works like a binding. You can't bind properties of Binding itself (or anything that isn't a DependecyProperty as stated in the error message). You actually see the same sort of error message when you try to do that with a binding: (e.g. {Binding SomeProperty, FallbackValue={Binding SomeOtherProperty}})
This is where PriorityBinding comes in. It allows specifying a series of fallback values as bindings themselves. With PriorityBinding you specify a list of Bindings, the first binding with a valid value is the one used. Ideally we could write something like this:
<TextBlock>
<TextBlock.Text>
<PriorityBinding>
<Binding Path="SomeProperty" />
<DynamicResource ResourceKey="somekEY" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
Unfortunately, DynamicResourceExtension can't directly be converted to a binding to be used in a PriorityBinding (or MultiBinding) like the above, so we'll have to use a little trick instead (the example above won't work). We'll use the Tag property (which is a property that has no effect and is basically for holding values for tricks like this) to capture the value of the DynamicResource, and then use a RelativeSource binding in the PriorityBinding to get it:
<TextBlock Tag="{DynamicResource somekEY}">
<TextBlock.Text>
<PriorityBinding>
<Binding Path="SomeProperty" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</PriorityBinding>
</TextBlock.Text>
</TextBlock>
So the issue is that when I normally bind a single text item to the Text in a TextBlock, the syntax is as follows:
<TextBlock ... Text="{Binding Attributes[StatusDateTime]}" /> //WORKS GREAT!
Now, background is that the DataContext for this part of the app is the output of a 3rd party API. "Attributes" is a collection of KeyValuePair, which is a property of the parent object. StatusDateTime is the key for the value I am returning. The syntax above works just great! So the object would look something like: theDataContextObject.Attributes[StatusDateTime].
BUT, if I need to combine multiple attributes into one TextBlock that's when things get hairy. I have no idea how to access the key from an actual Binding tag:
<TextBlock Grid.Row="0" Grid.ColumnSpan="3" Style="{StaticResource popupText}">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} at {1}">
<Binding Path="{Attributes[Type]}"/>
<!--<Binding Path="StatusDateTime" />-->
<Binding Path=Attributes[Type]/>
<Binding Path="Type" ElementName="Attributes"/> //the element has no
//name it's just the datacontext
<Binding Path="[StatusDateTime]" />
<Binding Path="Attributes[StatusDateTime]"/>
</MultiBinding>
</TextBlock.Text>
I know the number of examples above does not match the stringformat, I was just pasting examples of my guesses into the multibinding of how to get these values bound.
Apologies if this is a duplicate with another question. I am not sure what to even call this kind of binding where I'm just passing Attributes[StatusDateTime] directly to binding. Keep in mind that Attributes is not the name of an object, it's a property of the object passed to datacontext of the TextBlock's parent control.
So how do I bind to the value of key in the KeyValuePair collection when i have to use a Binding tag inside a MultiBinding?
I have a TextBlock in my view that I want to always display the number 1, converted into the local currency. (e.g. $1 in US, £1 in UK, etc). I have a value converter that can do this, but I don't know how to apply a value converter to the value of 1 without getting my data from a databinding.
I can think of two solutions, but they each have their problems, and I'm looking for something more elegant:
Create a property on my ViewModel that just holds and returns the value 1 and bind to it. Then add my converter to this binding. This seems backwards, particularly as this is view-only code.
Make a binding point to an existing property and modify my converter to ignore the value given to it, and instead use the parameter to give it the number 1. This feels unintuitive to other programmers, as they'll be confused as to why I'm binding to a different property there.
Is there some way of applying a converter without first creating a binding?
If you want for this to be relatively readable from XAML alone, you can always do it like this:
<Label>
<Label.Resources>
<system:Int32 x:Key="defaultValue">1</system:Int32>
</Label.Resources>
<Label.Content>
<Binding Source="{StaticResource defaultValue}"
Converter="{StaticResource CurrencyConverter}" />
</Label.Content>
</Label>
I had similar situation, I needed to convert static enum value placed directly in xaml to be converted by my custom converter without using databinding.
<Border>
<Border.Background>
<Binding Source="{x:Static enumeration:ColorType.Main}"
Converter="{StaticResource ColorConverter}" />
</Border.Background>
</Border>
I have a MultiBinding that looks like this:
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="Object1.Object2.MyObject" />
<Binding Path="Object1.Object2.MyCollection[1]" />
<Binding Path="MyBoolean" />
</MultiBinding>
I only want to evaluate this MultiBinding (and thus call MyConverter.Convert()) when MyObject changes. I'm aware that I could set the MultiBinding's UpdateSourceTrigger to Explicit, but considering that Object1 and Object2 are regularly reassigned, I'd have to wire up a lot of PropertyChanged events in the code behind. Is it possible to achieve this in XAML?
Any help would be much appreciated - thanks!
Try setting the DataContext of your hosting control to that of Object2. Then you can bind to MyObject directly. Does this help? Of course, this still means changes to MyCollection and MyBoolean will also trigger the MultiBinding re-evaluation.
It would also help if you provided the context in which you're using your MultiBinding (i.e. is this returning text for a TextBlock?
EDIT:
In response to your comment, perhaps there's a different way. I assume your 3 multi-bound properties have individual PropertyChanged events. If your application allows, perhaps you could move those events out of the MyCollection and MyBoolean properties and into the MyObject property. For example:
public object MyObject
{
get { ... }
set
{
...
OnPropertyChanged("MyObject");
OnPropertyChanged("MyCollection");
OnPropertyChanged("MyBoolean");
}
}
Now I know this is a bit of a hack but this would trigger the MultiBinding only on change of MyObject.
EDIT #2:
Another option would be adding a EventTrigger to the hosting control, for example:
<EventTrigger RoutedEvent="MyObject.TargetUpdated">
<Setter TargetName="yourCheckBox" Property="Value">
<Setter.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="MyObject" />
<Binding Path="MyCollection" />
<Binding Path="MyBoolean" />
</MultiBinding>
</Setter.Binding>
</Setter>
</EventTrigger>
Only catch here would be to make sure Mode=TwoWay on your checkbox binding.
I referred to this link.
I have a problem with a multibinding:
<Canvas>
<local:SPoint x:Name="sp" Width="10" Height="10">
<Canvas.Left><!-- irrelevant binding here --></Canvas.Left>
<Canvas.Top>
<MultiBinding Converter="{StaticResource myConverter}" Mode="TwoWay">
<Binding ElementName="cp1" Path="(Canvas.Top)"/>
<Binding ElementName="cp1" Path="Height"/>
<Binding ElementName="cp2" Path="(Canvas.Top)"/>
<Binding ElementName="cp2" Path="Height"/>
<Binding ElementName="sp" Path="Height"/>
<Binding ElementName="sp" Path="Slope" Mode="TwoWay"/>
</MultiBinding>
</Canvas.Top>
</local:SPoint>
<local:CPoint x:Name="cp1" Width="10" Height="10" Canvas.Left="20" Canvas.Top="150"/>
<local:CPoint x:Name="cp2" Width="10" Height="10" Canvas.Left="100" Canvas.Top="20"/>
</Canvas>
In order to calculate the Canvas.Top value, myConverter needs all the bound values. This works great in Convert(). Going the other way, myConverter should ideally calculate the Slope value (Binding.DoNothing for the rest), but it needs the other values in addition to the Canvas.Top one passed to ConvertBack(). What is the right way to solve this?
I have tried making the binding OneWay and create an additional multibinding for local:SPoint.Slope, but that causes infinite recursion and stack overflow. I was thinking the ConverterParameter could be used, but it seems like it's not possible to bind to it.
The approach may not be the most elegant one but it does work around the limits of multi bindings and the fact that ConverterParameter is not a DependencyProperty.
Make your converter derive from FrameworkContentElement. Define a number of DependencyProperty proprerties. You will need as many of them as there are values that you need on both ends of conversion.
Since your converter normally sits in XAML resources, DataContext and ElementName bindings won't work without some help from DataContextSpy and/or ElementSpy, depending on your particular binding needs.
The oddity of this approach is that you will have two identical bindings, one in <MultiBinding> section of XAML and the other one in Resources where you define your converter. The former one is needed to trigger the MultiBinding while the latter one actually provides the value in ConvertBack direction.
A downside is that resources are shared and thus if you need the same functional converter somewhere else on the page, you might need to declare a second one in your resources. This is only if two MultiBindings need different parameters. If parameters are shared, you may as well use the same resource. You might also try x:Shared="False" on the converter resource but I am not sure if that would work.