WPF XAML StringFormat assign amount of decimal places through binding - wpf

I have the need to give the user the ability to set their own Decimal Formating option in XAML.
I know how to set StringFormat into Binding, but I only know how to do it manually. How can I Bind the String Format value inside a binding.

How can I Bind the String Format value inside a binding.
You cannot because StringFormat is not a dependency property.
But you could use a multi binding that binds to two properties, the actual source property and the format source property, and an IMultiValueConverter class:
WPF Binding and Dynamically Assigning StringFormat Property
WPF: MultiBinding and IMultiValueConverter: https://blog.csainty.com/2009/12/wpf-multibinding-and.html

You can use ContentStringFormat property.
URL: https://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.contentstringformat.aspx
Use it like this:
<TextBox Text="{Binding MyFormat, UpdateSourceTrigger=PropertyChanged}" />
<Label Content="{Binding ValueToFormat}"
ContentStringFormat="{Binding MyFormat}" />

Related

Binding UI-element to UI-element like Textbox to Textbox

I have UI-elements like a textbox and I want to bind them to a ViewModel. I need to access many properties of the textbox like the text- or the IsEnabled-property.
Is it possible that I bind the textbox directly to another textbox in the ViewModel with all their properties instead of binding every single property to properties?
Yes, using an ElementName, but you still bind all properties though.
<!-- Bound to ViewModel -->
<TextBox Name="tbOne" IsEnabled="{Binding OneIsEnabled}" Text={Binding TextOne}/>
<TextBox Name="tbTwo" IsEnabled="{Binding ElementName=tbOne, Path=IsEnabled}" Text={Binding ElementName=tbOne, Path=Text}/>
There is no built-in way to bind all dependency properties of the TextBox to another. Personally, I would prefer binding directly to the ViewModel.
An alternative solution would be to create a UserControl that internally clones the TextBox with all bindings:
<CloneControl Target="{Binding ElementName=tbOne}"/>
Here, the ControlControl would inspect the target, and have code that create a new TextBox, and sets the bindings in code. This is only useful if you are doing this very often, and there is a slightly performance price to pay, as you are adding another level of controls to the UI tree.

Is it possible to use binding inside other binding?

Is it possible to write something like this
<TextBlock Text="{Binding Path=TextSource, StringFormat='{Binding Path=StringFormat}' }"
Or the single way is to have three properties: one for some value and other for string presentation of this value, third for format string. In this case TextBox binds with string representation of value. String presentation changes when format string changes.
Yes, it is possible in general and no for your case it is not possible because StringFormat is not Dependency Property.
Binding only works on Dependency Properties.
If you wish that to work create a resource dictionary of type Freezable and let it inherit the actual DataContext. Futhermore use StaticResource extension to set StringFormat in Binding.
StringFormat is not DependencyProperty but it doest accept {StaticResource someKey}.
It's a workaround. But it would work.
Another alternative solution would be attached property.
Attached properties are bindable. You would need to listen to property changed event of your attached property and change the StringFormat inside the handler.

WPF textblock binding question

I'm trying to get my head around the whole MVVM thing and binding. I have a ViewModel class which has a property that is another class. I want to bind to a (string) property of that class to the text of a textblock.
I set the ViewModel as my data context for my window\page. And then do this:
<TextBlock Text="{Binding ElementName=myAddressClass, Path=StreetName}" />
But this does not work. The text is empty.
I can expose the StreetName directly as below and this works:
<TextBlock Text="{Binding Path=StreetName}" />
So am I doing something wrong in the first example. It seems simple enough ... am I just confuse about what an elementname is or should be set to?
thanks
I think you probably are confused. If you want to bind to MyAddress.StreetName, just do this: Text="{Binding MyAddress.StreetName}" Make sure MyAddress is a property of your DataContext. ElementName is for binding to other controls.
ElementName is used to reference a XAML element in the Logical Tree. Since what you're trying to bind to is not an element, ElementName isn't the correct approach. Dotted path notation is the simplest approach in this case:
{Binding Path=myAddressClass.StreetName}

WPF databinding and converters

I'm trying to databind to a listbox like so:
<ListBox x:Name="MyListBox" Margin="0,0,0,65">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MyConverter}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The reason I am binding to the whole object and not a property is because my converter will need multiple properties of the object to build the string that it returns.
This works and my string is returned. But then when I change the ObservableCollection that this is based on the value doesn't change on the screen. If I bind to just a single property and change it, then the value does change.
What can I do differently? I can't bind to a single property since I need the entire object in the converter... And the ConverterParameter is already being used.
Remember, if you bind to the "main" property and the value of the main property itself isn't changed, the binding will have no reason to refresh itself. It has no clue that your converter is actually based off of a sub-property. What you can do is use a MultiBinding where you bind not only the "main" property, but also a specific sub-property. This gives your IMultiValueConverter implementation access to the main data object, but because you're also binding to the sub-property that's changing, will also be refreshed when that sub-property's value changes.
You can try using a MultiBinding which I believe updates whenever any of its Bindings are triggered. You can also use an IMultiValueConverter or just take advantage of the StringFormat of the binding.

WPF won't let me put a binding on the path of a binding -- is there another way?

I have a DataTemplate that I'm using as the CellTemplate for a GridViewColumn.
I want to write something like this for the DataTemplate:
<DataTemplate
x:Key="_myTemplate">
<TextBlock
Text="{Binding Path={Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumn}}, Path=Header}}" />
</DataTemplate>
My GridView is bound to a DataTable, and I want to bind to the column of the DataTable whose name is equal to the Header of the GridViewColumn the DataTemplate is attached to. [I hope that made sense!]
Unfortunately, this doesn't work. I get a XamlParseException: "A 'Binding' cannot be set on the 'Path' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependenceyObject."
How can I set this up?
Edit (elevating comment by DanM to the question)
I basically need a DataTemplate whose binding is determined by the DataContext and which column the DataTemplate is attached to. Is there an alternative?
You cannot assign a Binding to just any property. The property must either of the type Binding or be implemented as a Dependency Property on the object.
The Path property of the Binding class is of type PropertyPath and Binding does not implement the Path property as a dependency property. Hence you cannot dynamically bind the Path in the way you are attempting to.
Edit
You basically want to embed metadata in your bound data which drives the configuration of the DataTemplate. This can't be done in XAML alone. You would need at least some support from code.
It would seem to me that the best approach would be to use a ViewModel. That makes the binding in the XAML straight-forward and pushes this "what to bind with what" decision down into the code of the ViewModel.
Don't you just want this?
{Binding Path=Header, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GridViewColumn}}}

Resources