Junior
Junior

Reputation: 11990

How do I pass decimal value as CommandParameter for a command using WPF?

I have an app written using c# and WPF on the top of Prism 6 framework.

I am trying to create a button using XAMl, when clicked, I want to be pass a fixed decimal value to my view-model.

<Button Content="5"
        VerticalAlignment="Center"
        HorizontalAlignment="Center"
        Command="{Binding ProcessAmount}"
        CommandParameter="5.00000"
        FontSize="24"
        Height="75"
        Width="85" />

In my view-model I have the following

public class ViewModel : BindableBase
{
    public DelegateCommand<decimal?> ProcessAmount { get; set; }

    public ViewModel()
    {
        ProcessAmount = new DelegateCommand<decimal?>(HandleProcessAmount, CanProcessAmount);
    }

    private bool CanProcessAmount(decimal? amount)
    {
        return amount.HasValue && amount.Value != 0;
    }

    private void HandleProcessAmount(decimal? amount)
    {
        // do something with the amount
    }
}

But when I compile the app, I get an error in the XAML view Specified cast is not valid.

How can I send a fixed decimal value from the view?

Upvotes: 3

Views: 11028

Answers (3)

Rekshino
Rekshino

Reputation: 7325

The problem you have is, that you assign string "5" to the CommandParameter. There are several solutions. One of them to set Decimal in XAML:

<Window x:Class="....." 
xmlns:sys="clr-namespace:System;assembly=mscorlib"  
/>
<Button Content="5"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    Command="{Binding ProcessAmount}"
    FontSize="24"
    Height="75"
    Width="85">
    <Button.CommandParameter><sys:Decimal>5.00000</sys:Decimal></Button.CommandParameter>
    <!-- <Button.CommandParameter><x:Null/></Button.CommandParameter> If you want to set value to null-->
</Button>


or handle ComandParameter as a string:

public class ViewModel : BindableBase
{
    public DelegateCommand<string> ProcessAmount { get; set; }

    public ViewModel()
    {
        ProcessAmount = new DelegateCommand<string>(HandleProcessAmount, CanProcessAmount);
    }

    private bool CanProcessAmount(string amountStr)
    {
        try
        {
            return Convert.ToDecimal(amountStr) != 0;
        }
        catch (Exception)
        {
            return false;
        }
    }

    private void HandleProcessAmount(string amount)
    {
        // do something with the amount
    }
}

Upvotes: 0

kurakura88
kurakura88

Reputation: 2305

In Xaml world, every number is treated as Double. You would need to cast/ convert manually, otherwise unexpected may occurs. This means, in your view model, retrieve the number as double? instead of decimal?, then cast/ convert it to decimal as appropriately. For ex:

public class ViewModel : BindableBase
{
    public DelegateCommand<double?> ProcessAmount { get; set; }

    public ViewModel()
    {
        ProcessAmount = new DelegateCommand<double?>(HandleProcessAmount, CanProcessAmount);
    }

    private bool CanProcessAmount(double? amount)
    {
        return amount.HasValue && amount.Value != 0;
    }

    private void HandleProcessAmount(double? amount)
    {
        decimal amountInDecimal = Convert.ToDecimal(amount.Value); // convert it to decimal

        // do something with the amount
    }
}

The good thing about this is that it is easier to perform calculation on double type number than decimal type number. But, I guess this is not what you wanted.

The solution to your question is specify the type explicitly to be Decimal in the xaml. You need to import the System mscorlib and then assign that System's Decimal to the CommandParameter.

xmlns:s="clr-namespace:System;assembly=mscorlib"

<Button Content="5"
        VerticalAlignment="Center"
        HorizontalAlignment="Center"
        Command="{Binding ProcessAmount}"
        FontSize="24"
        Height="75"
        Width="85">
    <Button.CommandParameter>
        <s:Decimal>5.00000</s:Decimal>
    </Button.CommandParameter>
</Button>

This eliminates the necessary to convert number type on the VM side.

Upvotes: 7

Mikolaj Kieres
Mikolaj Kieres

Reputation: 4403

Instead of literally assigning 5.00000 to CommandParameter, try defining the value in XAML, as a resource (as per this topic). Your XAML code could look something like this

<Window x:Class="WpfApp1.MainWindow"
        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"
        mc:Ignorable="d"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow"
        Height="450"
        Width="800">

    <Window.Resources>
        <system:Double x:Key="MyDouble">5.0000</system:Double>
    </Window.Resources>

    <Grid>
        <Button Content="5"
                VerticalAlignment="Center"
                HorizontalAlignment="Center"
                Command="{Binding ProcessAmount}"
                CommandParameter="{StaticResource MyDouble}"
                FontSize="24"
                Height="75"
                Width="85" />
    </Grid>
</Window>

Upvotes: 2

Related Questions