xamluwpc++-cx

Generic Enum <-> Int converter for data binding


Is it possible to create a generic converter between enum values and ints? I have a set of similar converters in my project:

Object^ SpecificEnumIntConverter::Convert(Object^ value, TypeName targetType, Object^ parameter, String^ language)
{
    const auto asEnum = static_cast<SpecificEnumType>(value);
    const auto asInt = static_cast<int>(asEnum);
    return asInt;
}

Object^ SpecificEnumIntConverter::ConvertBack(Object^ value, TypeName targetType, Object^ parameter, String^ language)
{
    const auto asInt = static_cast<int>(value);
    const auto asEnum = static_cast<SpecificEnumType>(asInt);
    return asEnum;
}

These converters are usually used to binding with ViewModel, i.e:

<ListBox SelectedIndex="{x:Bind VM.EnumTypeProperty, Mode=TwoWay, Converter={StaticResource SpecificEnumIntConverter}}">

I am wondering if it is possible to implement a generic converter.

Thanks in advance and best regards!


Solution

  • It is possible to define generic converter by using template<...> somehow.

    public enum class EnumType1 : int
    {
        Foo1, Bar1
    };
    public enum class EnumType2 : int
    {
        Foo2, Bar2
    };
    
    // C++/CLI's syntax "generic<...> public ref class ... " cannot be used in C++/CX.
    
    template <class _T>
    ref class GenericEnumConverter : Windows::UI::Xaml::Data::IValueConverter
    {
    public:
        virtual Platform::Object ^ Convert(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language)
        {
            const auto asEnum = static_cast<_T>(value);
            const auto asInt = static_cast<int>(asEnum);
            return asInt;
        }
        virtual Platform::Object ^ ConvertBack(Platform::Object ^value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object ^parameter, Platform::String ^language)
        {
            const auto asInt = static_cast<int>(value);
            const auto asEnum = static_cast<_T>(asInt);
            return asEnum;
        }
    };
    

    However it's not possible to lay such converters directly in Resources via XAML (because templated-class in C++/CX cannot take "public" accessibility-modifier). So you need to deploy them in code behind.

    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class MainPage sealed
    {
    public:
        MainPage()
        {
            Resources->Insert(L"EnumType1Converter", ref new GenericEnumConverter<EnumType1>());
            Resources->Insert(L"EnumType2Converter", ref new GenericEnumConverter<EnumType2>());
    
            InitializeComponent();
        }
    
        property EnumType1 Value1 {
            EnumType1 get() { return m_Value1; }
            void set(EnumType1 value) {
                m_Value1 = value; 
                //OutputDebugString((m_Value1.ToString() + L"\r")->Begin());
            }
        }
        property EnumType2 Value2 { 
            EnumType2 get() { return m_Value2; }
            void set(EnumType2 value) { 
                m_Value2 = value;
                //OutputDebugString((m_Value2.ToString() + L"\r")->Begin());
            }
        }
    private:
        EnumType1 m_Value1 = EnumType1::Foo1;
        EnumType2 m_Value2 = EnumType2::Bar2;
    };
    

    Then you can use the converters in binding.

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel>
            <ListBox SelectedIndex="{x:Bind Value1, Converter={StaticResource EnumType1Converter}, Mode=TwoWay}" Margin="16">
                <ListBoxItem>Foo1</ListBoxItem>
                <ListBoxItem>Bar1</ListBoxItem>
            </ListBox>
            <ListBox SelectedIndex="{x:Bind Value2, Converter={StaticResource EnumType2Converter}, Mode=TwoWay}" Margin="16">
                <ListBoxItem>Foo2</ListBoxItem>
                <ListBoxItem>Bar2</ListBoxItem>
            </ListBox>
        </StackPanel>
    </Grid>