Do you have to use a converter when using Multibinding in WPF? - wpf

I would like to know if there are scenarios where you can use a Multibinding without a converter - and the limitations which force us to use a converter.
In particular I am trying to bind a string to another two strings in a string.format style.

The most common area you use a MultiBinding without a converter is when you have a string format concatenating two individual values
say for example:
To format Names that have First, Last part and you want to format it based on locale
<StackPanel>
<TextBlock x:Name="firstName"
Text="John" />
<TextBlock x:Name="lastName"
Text="Wayne" />
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding ElementName="firstName"
Path="Text" />
<Binding ElementName="lastName"
Path="Text" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
You do see quite a lot of places you use a converter since using a MultiBinding your doing the same as a Binding but you have multiple source values formatted to a single result instead of single input -> single output.
You can have a Binding take a ConverterParameter to supply another input value however you have limitations like not being able to provide a runtime Bound value to it, which makes MultiBinding more appropriate for multiple inputs where you want to bind all of them.
It boils down to your use-case, If you want to provide a result based on different input types that you evaluate in a custom-way, you need a Converter(pretty much similar to Binding. Just think of the difference as 1 input bind-able value against multiple)

Related

Extending Binding class to provide a bindable converter parameter

I need to create a string in a particular format, for which I use IMultiValueConverter. Example:
{0} of {1} in {2}
SomeValue0
SomeValue1
SomeValue2
Results in:
SomeValue0 of SomeValue1 in SomeValue2
This part is not a problem. Basically converter accepts multiple strings, of which the first one is the string format, and later ones are the strings to format.
<MultiBinding Converter={...}>
<Binding>
<Binding.Source>{0} of {1} in {2}</Binding.Source>
</Binding>
<Binding Path="Value0" />
<Binding Path="Value1" />
<Binding Path="Value2" />
</MultiBinding>
It gets tricky when some of the strings (Binding) also require the use of IMultiValue converter. Imagine that property #Value1# has different value for different language. Normally we get such value also using IMultiValueConverter:
<TextBox>
<TextBox.Text>
<MultiBinding Converter={...}>
<Binding Path="Value1"?
<Binding Path="Strings" Source="{StaticResource langResources}" />
</MultiBinding>
</TextBox.Text>
</TextBox>
When the user switches to different language, a dictionary of Strings is updated, and TextBox receives new value (the same applies to changing a value to Value1).
Now the problem: It is not possible to use IMultiValueConverter inside IMultiValueConverter. You also cannot override ProvideValue for BindingBase, and IMultiValueConverter will accept only the objects of type BindingBase.
Is there any way I can somehow extend BindingBase so that it exposes a BindableConverterProperty, whose value will be used to provide value from Binding?
tricky when some of the strings (Binding) also require the use of IMultiValue converter.
Why not put the intelligence of the string's IMultivalue converter into a specialized class who's ToString() will output the correct string based on the current environmental settings? That would eliminate the need for the sub multivalue converter you describe but work within the framework of the parent MultiValue converter.
The super-classed string class I recommend simply has to subscribe to an event which informs it which dictionary to use. Then when ToString() is called, the correct string is passed on to the top level converter.

WPF TextBlock MultiBinding

I want to bind TextBlock's Text property to some elements' and some model's properties. Something like this:
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<Binding ElementName="myElement1" Mode="OneWay" Path="Text" />
<Binding ElementName="myElement2" Mode="OneWay" Path="Text" />
<Binding Mode="OneWay" Path="Property1" />
<Binding Mode="OneWay" Path="Property2" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
The TextBlock has a text value, combination of myElement1, myElement2 and Property1, Property2. There is not a problem. The text value is generated successfully.
Here is my question:
Can I bind whole (combined) text value of the TextBlock to another model's property, i.e. Property3, without code?
Not without some really bad hacking which would require writing some code to set up attached properties and other bindings anyway. The issue is that any binding has 2 ends: target and source. Since the target (where the binding is set) must be a DependencyProperty that means that your model must be on the source end of the binding you're trying to do. This isn't a problem as far as setting a value since TwoWay and OneWayToSource bindings do this just fine.
You have a bigger problem though in that the original place where the value is coming from (TextBlock.Text) already is assigned a binding and so can't be the target for your model binding. You might next want to try using another UIElement property as an intermediary to take the Text value and push it to the model. To do that you again need the model to be the source and the other UIElement property to be the target. But that same property also needs to be the target of a binding to the original Text property that you're trying to extract, so again you're stuck.
Bottom line is that you're much better off handling this in your Model and ViewModel layers rather than trying to force the stuff you have set up in XAML to be driving everything.

Stringformat concatenates databinding and resource's value

I want to concatenate in my window title a property from my viewmodel and a value that cames from a resources file.
This is what I have working without the string from resources:
Title="Binding Path=Description, StringFormat=Building: {0}}"
Now I want to remove the "Building" string and put a value from a resource like I use on other places:
xmlns:res="clr-namespace:Project.View.Resources"
{res:Strings.TitleDescription}
How can I define both? Can I define like a {1} parameter?
I've seen the MultiBinding answer in several places now, and it is almost never necessary to use it. You can define your resource as the string format instead, and as long as there is only one string format argument, no MultiBinding is required. Makes the code a lot more succinct:
<TextBlock Text="{Binding Description, StringFormat={x:Static res:Strings.TitleDesc}}" />
And the TitleDesc resource is obviously "Building: {0}".
Yes, you can. Simply use a MultiBinding.
The MSDN article on StringFormat has an example.
In your case, the code would look something like this:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Source="{x:Static res:Strings.TitleDescription}"/>
<Binding Path="Description"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>

MultiBinding not working but corresponding Binding does work

I have the following code:
<local:StaffAtMeetingEditor DataContext="{Binding Meeting}" Grid.Row="1">
<local:StaffAtMeetingEditor.InEditMode>
<MultiBinding Converter="{StaticResource myMeetingLogEditableMultiConverter}">
<Binding Path="ParentSI.ItemInEditMode"/>
</MultiBinding>
</local:StaffAtMeetingEditor.InEditMode>
</local:StaffAtMeetingEditor>
The setup is that the containing control's datatype is "SIP_ServiceItem". This class has a property called "Meeting" (which is set as the DataContext for the local:StaffAtMeetingEditor control), which itself has a member called "ParentSI", pointing back to the parent SIP_ServiceItem object.
The issue is, that if I pass this through as a single binding (i.e. remove the start and end MultiBinding tags from the code above, leaving just the Binding), it works just fine. But when I make it a MultiBinding (I wish to add some other Bindings to this shortly), and try to pass the bound value through to myMeetingLogEditableMultiConverter, the values(0) parameter, which should correspond to the boolean ParentSI.ItemInEditMode is actually an MS.Internal.NamedObject, implying there's a null reference somewhere. Furthermore, the ParentSI property is never being evaluated, so something is going completely wrong. I am at a loss to know the difference between the single binding and multi binding cases.
Thanks.
I know this is a bit old, and you have likely figured this out by now, but I came across this as I had a similar problem and thought I'd share the solution: I had the same problem and have added the attributes ElementName and Mode as below:
<Binding Path="CurrentProvider.IsBusy" ElementName="parent" Mode="OneWay" />
Hope this helps someone, even if the OP has fixed their issue.
May be you should try to add any temporary unused bound value. For instance:
<local:StaffAtMeetingEditor DataContext="{Binding Meeting}" Grid.Row="1">
<local:StaffAtMeetingEditor.InEditMode>
<MultiBinding Converter="{StaticResource myMeetingLogEditableMultiConverter}">
<Binding Path="ParentSI.ItemInEditMode"/>
<Binding Path="ParentSI"/>
</MultiBinding>
</local:StaffAtMeetingEditor.InEditMode>
</local:StaffAtMeetingEditor>
If it doesn't work then your implementation is wrong, another case - it's MultiBinding limitations.

Can a WPF converter access the control to which it is bound?

Long version:
I have a simple WPF converter than does date conversions for me. The converter basically checks the date and formats it in dd/M/yyyy format. The converter does some smarts with the handling of the date which means that the user could type "23061971" and the value will be converted to "23/06/1971".
All this is trivial and working. The problem is that when I update the value, it does not update the caret position. Assume "|" is the caret and the user types "23061971|" then a millisecond later it is updated to "230619|71".
What I'd like to do is detect if the caret at the end of the value - if so, shift it to the end of the edit field once the new value has been updated. In order to do this, I'll need access to the edit control to which the converter is attached.
Short version:
From A WPF converter, can I get a reference to the control that is bound to that converter?
Here is an excellent article on how to get direct access to the control from within a converter: http://social.technet.microsoft.com/wiki/contents/articles/12423.wpfhowto-pass-and-use-a-control-in-it-s-own-valueconverter-for-convertconvertback.aspx
Essentially:
<MultiBinding Converter="{StaticResource MyConverter}" >
<Binding RelativeSource="{RelativeSource Self}" Mode="OneTime"/>
<Binding Path="MyValue2" />
</MultiBinding>
in the converter values[0] will be your control, ready for casting and values[1] would be the data that you are binding.
In a ValueConverter you can't get access to the control - however you can get access if you use a multibinding with a multivalueconverter.
In the multibinding the first binding is your binding as now - without the converter. The second binding you bind to the control itself - there you go acces to the control.
I have used this approach to gain other things also - you can make "dummy" bindings to properties you want to trigger updates, i.e. if you bind to the control itself you will only get updated if it changes, not if a property does - so create dummybindings for those.
You can send the control with the MultiBinding like this.
<TextBox Height="100" x:Name="textbox1" DockPanel.Dock="Top">
<TextBox.Text>
<MultiBinding Converter="{StaticResource MultiConverter}">
<Binding ElementName="textbox1" Path="." />
</MultiBinding>
</TextBox.Text>
</TextBox>
I don't thknk a converter has any way to get at the control that is using it. It is only a simple piece of logic that converts one object to another object.
However, in you case, perhaps you can trap the change event and then manually move the caret. If you think about it, caret position is strictly a view concern; it has nothing to do with converters or the data. You should not burden view-controller logic with it. You should definitely not burden converter logic (which is classified under utility classes) with it.

Resources