I have a converter that returns true if the current user has the specified security code. I have another converter that returns Visible if I pass in a true value otherwise Collapsed. I have a menu control that I want to only show if the user has a specified security code. I wish I could join my two converters so I didn't have to write a third one. Well it turns out I can...
Let's start with the two converters I have already written. Note I removed error handling for simplicity.
<ValueConversion(GetType(String), GetType(Boolean))>
Public Class SecurityToBooleanConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Return Application.Current.Properties.User_SecurityCodes.ContainsKey(parameter)
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New System.NotImplementedException("SecurityConverter")
End Function
End Class
<ValueConversion(GetType(Boolean), GetType(Visibility))>
Public Class BooleanToVisibilityConverter
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Return IIf(CBool(value), Visibility.Visible, Visibility.Collapsed)
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException
End Function
End Class
In my resources I have created StaticResources like this...
<local:SecurityToBooleanConverter x:Key="SecurityToBooleanConverter"/>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
Now here is the clever bit. In my list of converter classes I add the following generic code. This is the new class that will allow me to string as many converters together as I need.
Public Class ValueConverterGroup
Inherits Collections.Generic.List(Of IValueConverter)
Implements IValueConverter
Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
Return Me.Aggregate(value, Function(current, converter) converter.Convert(current, targetType, parameter, culture))
End Function
Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
Now I add a resource that describes how I want to string the converters together. In my example I want to convert from a security code to a boolean and then to a visibility. The new resource to do this looks like this. It could be defined globally in the Application.XAML or on a single page's XAML.
<local:ValueConverterGroup x:Key="SecurityToVisibilityConverter">
<local:SecurityToBooleanConverter></local:SecurityToBooleanConverter>
<local:BooleanToVisibilityConverter></local:BooleanToVisibilityConverter>
</local:ValueConverterGroup>
When InitializeComponent finds this resource it creates an instance of the ValueConverterGroup and populates the list of converters with a SecurityToBooleanConverter and a BooleanToVisibilityConverter. The ValidConverterGroup has a very clever use of Aggregate that essentially calls each converter in turn passing the value output from the previous converter and with the parameter and culture that was passed to the ValueConverterGroup.
The binding of my control's visibility looks like this...
<MenuItem Header="Requisitions" Visibility="{Binding Converter={StaticResource SecurityToVisibilityConverter}, ConverterParameter='Requisition'}"/>
The only drawback I see with this solution is that you cannot pass different parameters to different steps in the conversion.
No comments:
Post a Comment