Tuesday, September 21, 2021

String Map Converter

I find myself frequently having to convert one string to another in my XAML. For example I have a control that should be hidden when one option is chosen in a dropdown list but visible for the other options. This can be done through a trigger or a custom converter, but I wrote a generic converter that takes a value and a parameter 'a=b,c=d,e' which returns b if a is passed in, d if c is passed in, otherwise e.

For example, suppose I have a property called TransactionType that can take the value 'STORES' or 'VENDOR'. I want a warehouse warning to be visible when the value is STORES, otherwise hidden. I can use the visibility binding of 

Visibility="{Binding TransactionType, Converter={StaticResource StringMapConverter}, ConverterParameter='STORES=Visible,Collapsed'}"

You can use this converter for text, colors, masks, or any property that can take a string value including numerics and booleans. This is much more compact than triggers and more intuitive than custom converters. It can also be used on any binding and does not require access to a style.

Here's an example showing the converter and how to use it.

Start a Visual Studio WPF, Framework, Visual Basic project (because I'm rolling that way today) and call it StringMapConverter. Add a Converters class to it. 

Add this one converter to the Converter class. As you can see, the last parameter that does not contain an equals sign becomes the default. Note I'm not trimming spaces - you can add that if you want.

<ValueConversion(GetType(String), GetType(String))>
Public Class StringMapConverter
    Implements IValueConverter
 
    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
 
        If value Is Nothing Then Return ""
        If parameter Is Nothing Then Return value
 
        If TypeOf parameter IsNot String Then Return value
 
        Dim Pairs As New Dictionary(Of String, String)()
        Dim d As String = ""
 
        For Each p As String In parameter.ToString().Split(","c)
            If p.Contains("=") Then
                Pairs.Add(p.Split("="c)(0), p.Split("="c)(1))
            Else
                d = p
            End If
        Next
 
        If Pairs.ContainsKey(value.ToString()) Then
            Return Pairs(value.ToString())
        Else
            Return d
        End If
    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 it's time for the MainWindow.xaml. It's just a combo box and a text block. The use of the converter is near the end.

<Window x:Class="StringMapConverterTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StringMapConverter"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="String Map Converter" Height="450" Width="800">
    <Window.Resources>
        <local:StringMapConverter x:Key="StringMapConverter"/>
    </Window.Resources>
    <StackPanel Orientation="Horizontal" Height="22">
        <ComboBox SelectedValue="{Binding TransactionType}" SelectedValuePath="Content" Width="100">
            <ComboBoxItem Content="STORES"/>
            <ComboBoxItem Content="VENDOR"/>
        </ComboBox>
        <TextBlock Text="Warehouse Required" Foreground="Red" Margin="20,0,0,0"
                   Visibility="{Binding TransactionType, Converter={StaticResource StringMapConverter}, ConverterParameter='STORES=Visible,Collapsed'}"/>
    </StackPanel>
</Window>

Lastly we have the code-behind in Window.xaml.vb. It's trivial.

Imports System.ComponentModel 
Class StringMapConverterTest
    Implements INotifyPropertyChanged
 
    Private _TransactionType As String = "STORES"
    Public Property TransactionType As String
        Get
            Return _TransactionType
        End Get
        Set(value As String)
            _TransactionType = value
            NotifyPropertyChanged()
        End Set
    End Property
 
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
 
    Public Sub NotifyPropertyChanged(<System.Runtime.CompilerServices.CallerMemberName> Optional PropertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
    End Sub
 
End Class

The result looks like this.



  



 


No comments:

Post a Comment