WPF Multibinding to View Model - c#

I am trying to multibind a formatted double value to a text box. I have a converter which takes in a double and a Formatter object and returns a formatted string to be displayed. The double is bound to a particular data source and the formatter is a property in the view model. The problem I'm having is that I'm unable to bind to the view model property. This is my code in xaml
<StackPanel Grid.Row="0" Grid.Column="1">
<TextBlock HorizontalAlignment="Left" Style="{StaticResource HintDataItemsStyle}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource FormatConverter}">
<Binding Path="OpenValue" />
<Binding Path="XLabelFormatterY1" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
This is the property in the view model
private ILabelFormatter _labelFormatterY1;
public ILabelFormatter XLabelFormatterY1
{
get { return _labelFormatterY1; }
set
{
_labelFormatterY1 = value;
OnPropertyChanged("XLabelFormatterY1");
}
}
So, in my converter I'm able to pick up the value for "OpenValue" ,but the runtime is unable to find XLabelFormatterY1. Most of the examples I have seen for multibinding bind to gui components. I'm trying to bind to the view model and would appreciate all help.

Old question but without answer. I beleive that you are looking for this solution. If this answer doesn't work for you, try to explicitly set NotifyOnSourceUpdated="True" in the binding. And also double check if you have set correct AncestorType as wookietomwookie says in his answer.

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>

WPF influencing the order of databinding?

I have created an AttachedProperty that has a PropertyChangedCallback function, which does some formatting to a TextBlock. But to perform the formatting, the Tag attribute is needed. The Tag itself is bound to the output of a Multiconverter.
But my problem is, that the FNamePropertChangedCallback is executed before the Tag is bound to the output of the Multibinding. Thus Tag still Null, when FNamePropertyChangedCallback is triggered.
Is there any way to influence the order in which the Attributes are bound?
I need to bind Tag before binding FInlineProperty.
public static readonly DependencyProperty FInlinePropertyProperty =
DependencyProperty.RegisterAttached("FInlineProperty", typeof(string), typeof(MainWindow),
new PropertyMetadata(null, FNamePropertyChangedCallback));
<TextBlock local:MainWindow.FInlineProperty="{Binding Name}" TextWrapping="WrapWithOverflow">
<TextBlock.Tag>
<MultiBinding Converter="{StaticResource TupleConverter}">
<Binding />
<Binding ElementName="Window"/>
</MultiBinding>
</TextBlock.Tag>
</TextBlock>
Btw: Does someone know how to write local:MainWindow.FInlineProperty="{Binding Name}" in long form? I tried <TextBlock.local:MainWindow.FInlinse>...</TextBlock.local:MainWindow.FInlinse>, but the compiler complains about it.
Update 25.Feb.2017:
I did what you suggested and bound everything to my Attached Property.
<TextBlock TextWrapping="WrapWithOverflow" >
<local:MainWindow.FInlineProperty>
<MultiBinding Converter="{StaticResource GroupConverter}">
<Binding />
<Binding ElementName="Window" Path="MySetup" />
</MultiBinding>
</local:MainWindow.FInlineProperty>
</TextBlock>
The problem with this is, that it is working the first time when the ListBoxItem is created. But updating properties from MySetup does not re-trigger FNamePropertyChangedCallback.
MySetup.ColorString = "green"; // this does not retrigger the callback
MySetup = MySetup.Copy(); // this does retrigger the callback
So currently only assigning MySetup a new copy of itself (changing the reference) triggers callback function again.
That was the reason, why I bound the name property explicitly.
MySetup.ColorString would trigger, if I bind to it explicitly, but I need to bind to MySetup to have all data, so the question is, how to force the binding to execute again, when the bound object itself (reference) has not changed, but something inside did change?
What you actually want is binding between your AttachedProperty and TextBlock.Tag property, so that AttachedProperty is set when Tag changes. But since you are also binding to Name, so I suggest to use MultiBinding for your AP using Name and Tag bindings, thus not depending upon Tag at all.
Try to set the Tag property before you set the attached property. This means that you should also set the attached property using element syntax:
<TextBlock TextWrapping="WrapWithOverflow">
<TextBlock.Tag>
<MultiBinding Converter="{StaticResource TupleConverter}">
<Binding />
<Binding ElementName="Window"/>
</MultiBinding>
</TextBlock.Tag>
<local:MainWindow.FInlineProperty>
<Binding Path="Name" />
</local:MainWindow.FInlineProperty>
</TextBlock>

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.

Setting Binding Properties in a Template

Is there a way to template Binding.Converter and Binding.ValidationRules within a style?
Eg: I have the following textbox:
<TextBox x:Name="DepartTime" Height="23" HorizontalContentAlignment="Left" HorizontalAlignment="Left"
Margin="3" Width="140"
Style="{DynamicResource TimeOfDayTextBox}">
<TextBox.Text>
<!-- Textbox notifies changes when Text is changed, and not focus. -->
<Binding Path="FlightDepartTime" StringFormat="{}{0:hh:mm tt}" >
<Binding.Converter>
<convert:TimeOfDayConverter />
</Binding.Converter>
<Binding.ValidationRules>
<!-- Validation rule set to run when binding target is updated. -->
<validate:ValidateTimeOfDay ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
.. I can't figure out how to incorporate the Converter and the Validation rule into my TimeOfDayTextBox style.
Many Thanks.
Unfortunately, no. The style could only set the Text property itself to a Binding. It cannot set attributes of the binding. Also, since Binding is not a DependencyObject there is no way to style a binding.
One option you have to make your code more concise is to use a custom MarkupExtension that creates the binding you want:
public class TimeOfDayBinding
: MarkupExtension
{
public PropertyPath Path { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding()
{
Path = Path,
Converter = new TimeOfDayConverter(),
};
binding.ValidationRules.Add(new ValidateTimeOfDay()
{
ValidatesOnTargetUpdated = true,
});
return binding.ProvideValue(serviceProvider);
}
}
Given your control names, you may also want to use a time picker control instead of a TextBox. Check out this question: What is currently the best, free time picker for WPF?
A style can contain only a common set of property which can be applied to multiple controls. In your case, the converter and the validation rule aren't applied to the textbox, but to the content of the binding, so they are specific for a single element and cannot be used in a style.

Categories

Resources