XAML nested binding StringFormat - c#

I am trying to display an adress label. what i want is if AddresssLine2 is not an empty string (its never null) it should display it then a newline (I'm using VB, so its &#x0a), otherwise just display AddressLine2, which is an empty string, so in escense it is ignored. however, the StringFormat of the inner binding of AddressLine2 is completely ignored. it just displays the value of AddressLine2 and ignores the StringFormat. I even tried putting a constant only into StringFormat (StringFormat=" hi") but it ignored it and bound directly to AddressLine2.
here is my markup.
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}
{1}
{2} {3}, {4} {5}">
<Binding Path="FullName" />
<Binding Path="AddressLine1" />
<Binding Path="AddressLine2" StringFormat="{}{0}
" />
<Binding Path="City" />
<Binding Path="State" />
<Binding Path="ZipCode" />
</MultiBinding>
</TextBlock.Text>
my question is: why does it ignore the inner StringFormat (I tried all different combinations; "'{0}
'", "'{}{0}
'", " {0}
", none worked)?
Also, is there a better way to do this (in XAML, I dont want a converter or any code behind)?

MSDN says the StringFormat property gets or sets a string that specifies how to format the binding if it displays the bound value as a string.
Since you are using its value, and not displaying it in the multibinding, it will use the value in your MultiBinding StringFormat.
In short: StringFormat on Bindings in MultiBindings will be ignored so to speak.
When you use a MultiBinding, the
StringFormat property applies only
when it is set on the MultiBinding.
The value of StringFormat that is set
on any child Binding objects is
ignored. The number of parameters in a
composite string format cannot exceed
the number of child Binding objects in
the MultiBinding.
Workaround: expand the MultiBinding StringFormat:
StringFormat="{}{0}
{1}
{2}
{3}, {4} {5}"
But I guess you could figure that one out as well ;)

Related

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>

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?

StringFormat for DateTime is not working with MultiBinding

I have this code :
<Label>
<Label.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{} created on {0} by">
<Binding Path="CreationDate" StringFormat="{}{0:dd/MM/yyyy}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</LabeledLabel.Content>
</Label>
OUTPUT
I always get this created on 21/09/2014 00:00:00 by
I tried StringFormat="d", but it didn't work too.
What's the problem with my code ?
You've only got one Binding Path, so you'll only ever get the date and time. Basically, you need to add a Binding element for your person data type. It should be more like this:
<Label>
<Label.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{} created on {0:dd/MM/yyyy} by {1}">
<Binding Path="CreationDate" />
<Binding Path="SomeEmployeeObject.Name" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</LabeledLabel.Content>
</Label>
Note that you can also set the DateTime StringFormat using the MultiBinding.StringFormat property, instead of adding another on the first Binding object. You also needed to add the {1} to the end of the MultiBinding.StringFormat so that it would output the second (person related) value.
Please see the MultiBinding Class page on MSDN for further information.
UPDATE >>>
I don't understand why putting the StringFormat property on the MultiBinding element has a different behaviour compared to the first element
It doesn't... I could have left it there, but I moved it because you were already using a StringFormat. Using StringFormat property on a MultiBinding is virtually the same as using the string.Format method. Using the method, this is equivalent to what you had in your XAML:
string.Format("created on {0:dd/MM/yyyy} by ", someDate);
And this is equivalent to what I put in your XAML:
string.Format("created on {0:dd/MM/yyyy} by {1}", someDate, someEmployee.Name);
Hopefully, you can now see the difference.

Binding to 2 string elements?

Is it possible in WPF to bind to 2 elements?
For example I'd like to display something like myserver.com:80 in a textbox.
So to do this I'd like to bind to both a Host field then add a ":" then bind to a port field in my object all for the same label content.
In WPF 4/3.5SP1 you can use a MultiBinding in conjunction with StringFormat:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{0}:{1}">
<Binding Path="Host"/>
<Binding Path="Port"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Prior to WPF 4 you can still use a MultiBinding but would need to write your own converter instead.
An alternative to both these approaches is do MVVM and expose a property that does the concatenation for the view, then the view just binds directly to that property.

Categories

Resources