How to multi-bind ProgressBar's value in wpf? - c#

There is a concept of Multibinding in WPF, which is really useful if we want to bind some UI Control that depends on multiple values, like in this one.
I am trying to do the same with ProgressBar, like I am using ProgressBar to display how much storage is used by the users and it depends on two properties.
UsedStorage
TotalStorage
After searching, i could not find a way to bind Value property of ProgressBar with multiple properties and custom convertor.
Something like (Just a concept)
<ProgressBar Width="172" Height="16" >
<ProgressBar.Value>
<MultiBinding Converter="{StaticResource myConverter}">
<Binding Path="UsedStorage" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
<Binding Path="TotalStorage" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
</MultiBinding>
</ProgressBar.Value>
</ProgressBar>
But problem is there is nothing like MultiBinding under ProgressBar.Value. So the question is,
Is there a way to MultiBind ProgressBar Value?

Even though the Visual Studio intellisense might not show that, it is perfectly valid.
However, Using RelativeSource TemplatedParent only works inside a ControlTemplate. It is unclear to me where are you trying to get your values from.

Related

Cannot set DynamiResource on Fallback

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>

StringFormat XAML Binding to multiple controls

I have a custom Button type and i cannot change the code. This Button has an property called MyArguments which accepts a string of semicolon separated values.
I have a bunch of TextBoxes on the screen for the user to enter some information.
<TextBox Name="TestTextBox1" />
<TextBox Name="TestTextBox2" />
<TextBox Name="TestTextBox3" />
I want my Button to take these three values and supply them to the MyArguments string property.
If there was only a single TextBox i could use the StringFormat option like this:
<MyButton MyArguments="{Binding ElementName=TestTextBox1, Path=Text, StringFormat='Arguments;{0}' }/>
However you cannot use multiple controls with StringFormat.
I tried using MultiBinding but the MyArguments property gives an error 'The attachable property 'MyArguments' was not found in type MyButton'.
<MyButton.MyArguments>
<MultiBinding StringFormat="Arguments;{0};{1}">
<Binding ElementName="TestTextBox1" Path="Text" />
<Binding ElementName="TestTextBox2" Path="Text" />
</MultiBinding>
</MyButton.MyArguments>
I need this done in pure XAML. No code behind.
Any ideas?
You forgot to add the namespace prefix (e.g. local) to the property:
<local:MyButton>
<local:MyButton.MyArguments>
<MultiBinding StringFormat="Arguments;{0};{1}">
<Binding ElementName="TestTextBox1" Path="Text" />
<Binding ElementName="TestTextBox2" Path="Text" />
</MultiBinding>
</local:MyButton.MyArguments>
</local:MyButton>
This sounds like the parser cannot associate the element syntax property with your element instance. I.e. it thinks you are defining an attached property even though it is supposed to be an instance property.
e.g.
<ListBox ItemsSource="{Binding Data}">
<ListView.ItemTemplate>
<!-- Template -->
</ListView.ItemTemplate>
<ListBox>
Are you referencing the same type? It also looks odd how you have no prefix on your button, or did someone actually abuse the system and compile the assembly to use the WPF namespace?

WPF Binding inside Text attribute differs from actual Binding tag syntax in MultiBinding

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?

WPF Value Converter

I'm making a WPF application and I need a value convertor with more than 1 binding path, is that possible?
It works with 1 path so far:
Binding="{Binding Path=Price, Converter={StaticResource vPriceConvertor}}"
I wanna give the Price and the discount to the converter so he can calculate the endprice.
Value converters are intended... for value conversion. That's why they're value converters.
To do what you want, you should make a property EndPrice in view model, and calculate its value in view model. Why are you trying to bring a non-UI logic into UI??
Look at the MultiBinding class. For example:
<TextBlock Name="textBlock" DataContext="{StaticResource myViewModel}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource vPriceConvertor}"
ConverterParameter="myParameter">
<Binding Path="Price"/>
<Binding Path="Discount"/>
<!-- Insert other paths here -->
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Implement IMultiValueConverter instead of IValueConverter to actually do the conversion, since it supports multiple sources.
Parys -
You could always create a collection (in View Model) which contains Price and Discount and then pass it through your XAML (through IValueConverter).
But to re-iterate #Dennis and everybody else point - then your converter will have calculation logic - which is not recommended.

Access to bound data in IMultiValueConverter.ConvertBack() in C#/WPF

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.

Categories

Resources