Search code examples
c#.netwpfxamlcasting

Can I unbox an integer with a cast in XAML?


I've hit a case in which I have a class that returns type object that I happen to know is merely a boxed int value. The control I am using can bind to an int property or a double property but it cannot bind to an object that is a boxed int or double. I have to work around this limitation. I'd like to do so in a very simple manner.

Edited to add: The exception that occurs when I try to do this is System.NotSupportedException with the message DoubleConverter cannot convert from System.Int32

So is there a way to make a cast to int or double in XAML that is the equivalent of an unboxing operation? I already know how to cast a reference type in XAML so I can access some specific, derived-class property. But I am having trouble with this one because int is not a type with properties.

Below is the class (a simplified version). It represents a ranged set of integers or doubles or even other types for which "min" and "max" values would be meaningful. So it has to return them all as object. Regardless, all 3 properties will return the same underlying type in the boxed value, be it double or int or something else.

public class Parameter
{
    virtual object Lower      { get; }     
    virtual object Upper      { get; }   
    virtual object Value      { get; set; } 
}

I bind to the control like this

<tk:RadNumericUpDown MinWidth="100"
    IsInteger="True"
    Value="{Binding Value, Mode=TwoWay}"
    Minimum="{Binding Lower, Mode=OneWay}"
    Maximum="{Binding Upper, Mode=OneWay}"
    />

Now if I know that the Lower property is an int, can I bind it with some sort of XAML cast? I've tried a few things

Minimum="{Binding Lower(sys:Int32), Mode=OneWay}"
Minimum="{Binding Lower.(sys:Int32), Mode=OneWay}"

I didn't expect any of these to work and they didn't. But the syntax (if there is one) eludes me. About the only way I can get this to work is to define a separate property in the class that returns the Lower as an int value

Is there a way to do an unboxing cast in XAML?


Solution

  • You could run it through an IValueConverter:

    public class NonConverter : IValueConverter
    {
        public object Convert(object v, Type t, object p, CultureInfo c) => v;
    
        public object ConvertBack(object v, Type t, object p, CultureInfo c) => v;
    }
    

    Resource:

    <Window.Resources>
        <local:NonConverter x:Key="nonConverter"></local:NonConverter>
    </Window.Resources>
    

    Binding:

    Minimum="{Binding Lower, Mode=OneWay, Converter={StaticResource nonConverter}}"
    

    I'm not sure if there is a simpler way.


    Here is a minimal implementation using a ProgressBar, .NET Framework 4.7.2. I'm not sure what happens with two-way binding.

    MainWindow.xaml

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApp1"
            Title="MainWindow" Height="200" Width="400">
        <Window.Resources>
            <local:NonConverter x:Key="nonConverter"></local:NonConverter>
        </Window.Resources>
        <Grid>
            <ProgressBar Height="20" Width="200"
                Minimum="{Binding Lower}"
                Maximum="{Binding Upper, Converter={StaticResource nonConverter}}"
                Value="{Binding Value, Converter={StaticResource nonConverter}}"
            />
        </Grid>
    </Window>
    

    MainWindow.xaml.cs

    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public object Lower { get; set; }
            public object Upper { get; set; }
            public object Value { get; set; }
    
            public MainWindow()
            {
                InitializeComponent();
    
                DataContext = this;
                
                Lower = 1000.0d; // double
                Upper = "2000"; // string 
                Value = 1500.0m; // decimal
            }
        }
    
        public class NonConverter : IValueConverter
        {
            public object Convert(object v, Type t, object p, CultureInfo c) => v;
    
            public object ConvertBack(object v, Type t, object p, CultureInfo c) => v;
        }
    }
    

    Each of the three properties are assigned a different type for illustration purposes.

    The NonConverter isn't converting, casting, or un/boxing anything, it's simply returning the same input v that it received. The actual conversion is being handled by the framework.

    Because Lower is a (boxed) double, the converter isn't required here.