Search code examples
c#wpfmvvmwpfdatagrid

WPF MVVM DataGridColumn with conditional StringFormat


My WPF MVVM app's DataGridColumn 'Amount' has two patterns depends on 'Currency':

- USD amount: "1,000.12" (Allow decimal point)
- JPY amount: "5,000" (Prohibit decimal point)

Now, it has only USD's StringFormat. To implement two StringFormat patterns, should I use <Style.Triggers>?

<DataGridTextColumn x:Name="PayAmt"
    Header="Amount" Binding="{Binding Amount, Mode=TwoWay,
    StringFormat={}{0:N2}, TargetNullValue='', UpdateSourceTrigger=LostFocus}" >
    <DataGridTextColumn.ElementStyle>
        <Style>
            <Setter Property="TextBlock.TextAlignment" Value="Right"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Amount}" Value="0"></DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

<DataGridTextColumn x:Name="Currency" Header="Currency" Binding="{Binding Currency, Mode=TwoWay}">
    <DataGridTextColumn.ElementStyle>
        <Style>
            <Setter Property="TextBlock.TextAlignment" Value="Left" />
        </Style>
    </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>


ViewModel: (UPDATED: After I changed the above binding Amount to FormattedString this worked!)

private Nullable<decimal> _amount { get; set; }
public Nullable<decimal> Amount {
    get { return _amount; }
    set
    {
        if (Equals(_amount, value)) return;
        _amount = value;
        NotifyPropertyChanged("Amount");
        NotifyPropertyChanged("FormattedAmount");
    }
}

private string _currency;

public string Currency
{
    get => _currency;
    set
    {
        if (_currency == value) return;
        _currency = value;
        NotifyPropertyChanged("Currency");
        NotifyPropertyChanged("FormattedAmount");
    }
}

public string FormattedAmount
{
    get
    {
        switch (Currency)
        {
            case "JPY":
                return Amount?.ToString("N0");
            default:
                return Amount?.ToString("N2");
        }
    }
    set
    {
        if (decimal.TryParse(value, out var amount))
            Amount = amount;
        else
            NotifyPropertyChanged("FormattedAmount");
    }
}

Solution

  • I'd suggest doing it in the view model. You can make a new property called FormattedAmount and bind to that. In the getter just format the string based on currency. Here's an example.

        private double _amount;
    
        public double Amount
        {
            get => _amount;
            set
            {
                if (_amount == value) return;
                _amount = value;
                OnPropertyChanged(nameof(Amount));
                OnPropertyChanged(nameof(FormattedAmount));
            }
        }
    
        private string _currency;
    
        public string Currency
        {
            get => _currency;
            set
            {
                if (_currency == value) return;
                _currency = value;
                OnPropertyChanged(nameof(Currency));
                OnPropertyChanged(nameof(FormattedAmount));
            }
        }
    
        public string FormattedAmount
        {
            get
            {
                switch (Currency)
                {
                    case "JPY":
                        return Amount.ToString("N0");
                    default:
                        return Amount.ToString("N2");
                }
            }
            set
            {
                if (double.TryParse(value, out var amount))
                    Amount = amount;
                else
                    OnPropertyChanged(nameof(FormattedAmount));
            }
        }
    

    Then change your xaml to bind to the new property

    <DataGridTextColumn x:Name="PayAmt"
    Header="Amount" Binding="{Binding FormattedAmount}" >
      <DataGridTextColumn.ElementStyle>
          <Style>
              <Setter Property="TextBlock.TextAlignment" Value="Right"/>
          </Style>
      </DataGridTextColumn.ElementStyle>
    </DataGridTextColumn>