Built-in WPF IValueConverters - wpf

Ok, it was a nice surprise (after writing it several times) to find that there already is a BooleanToVisibilityConverter in System.Windows.Controls namespace.
Probably there are more such hidden time-savers.
Anyone got some?

I did a quick trawl using the Object Browser and this is what I have.
Derived from IValueConverter:
System.Windows.Controls.AlternationConverter
System.Windows.Controls.BooleanToVisibilityConverter
System.Windows.Documents.ZoomPercentageConverter
System.Windows.Navigation.JournalEntryListConverter
Xceed.Wpf.DataGrid.Converters.CurrencyConverter
Xceed.Wpf.DataGrid.Converters.DateTimeToStringConverter
Xceed.Wpf.DataGrid.Converters.GreaterThanZeroConverter
Xceed.Wpf.DataGrid.Converters.IndexToOddConverter
Xceed.Wpf.DataGrid.Converters.IntAdditionConverter
Xceed.Wpf.DataGrid.Converters.InverseBooleanConverter
Xceed.Wpf.DataGrid.Converters.LevelToOpacityConverter
Xceed.Wpf.DataGrid.Converters.MultimodalResultConverter
Xceed.Wpf.DataGrid.Converters.NegativeDoubleConverter
Xceed.Wpf.DataGrid.Converters.NullToBooleanConverter
Xceed.Wpf.DataGrid.Converters.SourceDataConverter
Xceed.Wpf.DataGrid.Converters.StringFormatConverter
Xceed.Wpf.DataGrid.Converters.ThicknessConverter
Xceed.Wpf.DataGrid.Converters.TypeToBooleanConverter
Xceed.Wpf.DataGrid.Converters.TypeToVisibilityConverter
Xceed.Wpf.DataGrid.Converters.ValueToMaskedTextConverter
Derived from IMultiValueConverter:
System.Windows.Controls.BorderGapMaskConverter
System.Windows.Navigation.JournalEntryUnifiedViewConverter
System.Windows.Controls.MenuScrollingVisibilityConverter
Microsoft.Windows.Themes.ProgressBarBrushConverter
Microsoft.Windows.Themes.ProgressBarHighlightConverter
Note the Xceed ones (no connection) are available free with their DataGrid. As well as those there's some clever stuff around like the debugging converter. I've also used the last IValueConverter and I'm sure there's some further lambda function goodness to be found, too.

Before 3.5 SP1, an IValueConverter was required for string formatting. Now, you can use the StringFormat property on Binding to do this.
From the MSDN page:
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} -- Now only {1:C}!">
<Binding Path="Description"/>
<Binding Path="Price"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>

Related

Make ViewModel property available for binding to IsChecked

I'm using Caliburn.Micro in my app. What I want to do is:
Create one RadioButton per available licence in the View
Check the one whose licence is currently active
So far I have two properties on my ViewModel (I'm leaving out INotify...Changed and its implementations here because that works):
BindableCollection<LicenceInfo> AvailableLicences { get; set; }
LicenceInfo ActiveLicence { get; set; }
In the ViewModel's constructor, I populate AvailableLicences and ActiveLicence. So far, so good.
Currently in the View itself, I have an ItemsControl which contains the RadioButtons and an invisible FrameworkElement to pass to MyConverter, where I extract the DataContexts of Self and the invisible FrameworkElement (whose DataContext is bound to the ViewModel) and compare them with (overridden) LicenceInfo.Equals():
<FrameworkElement Name="ActiveLicence" Visibility="Collapsed" />
<ItemsControl Name="AvailableLicences">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton cal:Message.Attach="[Event Checked] = [Action ChangeActiveLicence($dataContext)]">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource MyConverter}" Mode="OneWay">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding ElementName="ActiveLicence" />
</MultiBinding>
</RadioButton.IsChecked>
[...]
This actually works as intended, but it seems to me like an ugly workaround and I'm sure that I'm missing something.
Using <Binding x:Name="ActiveLicence" /> or <Binding Path="ActiveLicence" /> as the second parameter and removing the invisible FrameworkElement does not work, the ViewModel property is not being attached to the binding.
I'm not necessarily tied to using a MultiBinding. Anything similar to the Caliburn.Micro action like the one handling the Checked event would be welcome too. Any ideas?
From my point of view, you're pretty close to a good solution here, if adding a flag on the LicenceViewModel is not an option:
Instead of using the container framework element, try the following multi binding:
<MultiBinding Converter="{StaticResource MyConverter}" Mode="OneWay">
<Binding Path="DataContext" RelativeSource="{RelativeSource Self}" />
<Binding Path="DataContext.ActiveLicense" RelativeSource="{RelativeSource FindAncestor, AncestorType=ItemsControl}" />
</MultiBinding>
Modify the converter to compare two objects using Equals(), agnostic of the concrete type. That way, you're not messing around with unnecessary objects, still separating Views and ViewModels properly.
EDIT:
Regarding the alternative solution with a flag: I didn't notice, there is no LicenseViewModel involved in your code... Adding a flag to License info is not a good solution, I agree. You can consider to wrap the LicenseInfos inside LicenseInfoViewModels, though this would require a bit of infrastructure for the synchronization between the original collection of LicenseInfos on the model and the collection containing the ViewModels.
I have posted an extensive answer on that topic here.
Then you could set the flag of the active license's ViewModel to true and all others to false, when the ActiveLicense property changes.
It's a question of the specific context, whether it makes sense to go the extra mile here. If you don't plan to extend features over time etc, and it's just a simple selection of licenses, the first solution is sufficient, probably.

Can I data bind to a indexed property where a property of the parent is the index

Using WPF with MVVM, my VM has an indexed property
IObject1 this[string key]
I need to bind a property of the view to a property of IObject1, and the key of the object1 that I need is the name of the control in the view. Essentially I need nested bindings
<TextBlock x:Name="Key1" Text="{Binding ["Key1"].DisplayText}
but, the name will very for different items so I need the nested binding
<TextBlock x:Name="Key1" Text="{Binding [{Binding Name, RelativeSource={RelativeSource Self}].DisplayText}
My actual case is more complicated than this, but if I can get this far I think that I can figure out the rest.
I'm using Blend, and I'd love a way to teach my designer to do this type of thing within Blend, but I'm happy to use code if I need to.
Am I overlooking something obvious? I can't figure out how to do this and I haven't stumbled upon the correct Google / Stack Overflow search term.
Thanks.
That's a weird solution lol, anyhose, you can solve it with MultiBinding & converters.
<TextBlock.Text>
<MultiBinding Converter={StaticResource combine}>
<Binding Path=Dictionary />
<Binding Path=Name />
</Multibinding>

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.

Why does my IMultiBindingConverter get an array of strings when used to set TextBox.Text?

I'm trying to use a MultiBinding with a converter where the child elements also have a converter.
The XAML looks like so:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource localizedMessageConverter}" ConverterParameter="{x:Static res:Resources.RecordsFound}" >
<Binding Converter="{StaticResource localizedMessageParameterConverter}" ConverterParameter="ALIAS" Path="Alias" Mode="OneWay" />
<Binding Converter="{StaticResource localizedMessageParameterConverter}" ConverterParameter="COUNT" Path="Count" Mode="OneWay" />
</MultiBinding>
</TextBlock.Text>
The problem I'm facing here is, whenever this is used with a TextBlock to specify the Text property, my IMultiValueConverter implementation gets an object collection of strings instead of the class returned by the IValueConverter. It seems that the ToString() method is called on the result of the inner converter and passed to the IMultiValueConverter. If used to specify the Content property of Label, all is well.
It seems to me that the framework is assuming that the return type will be string, but why? I can see this for the MultiBinding since it should yield a result that is compatible with TextBlock.Text, but why would this also be the case for the Bindings inside a MultiBinding?
If I remove the converter from the inner Binding elements, the native types are returned. In my case string and int.
Probably the targetType parameter of your localizedMessageParameterConverter converter is System.String. This is because the target type of the Bindings is inherited from the MultiBinding, and the targetType of the MultiBinding is System.String because TextBlock.Text is a string property.
See the following article for a similar problem: Multi-Value Converters, Value Converters and the Case of the Wrong Target Type
According to Microsoft Connect, this has been fixed in WPF 4.0. See: Microsoft Connect
The above article also explains a workaround.

Resources