WPF - Silverlight Dynamically Change Data Binding Attributes At Runtime

Learn how to use code behind to dynamically wire up Binding classes to IValueConverters for data validation at runtime.

In many grid based user interfaces, we have to redefine the data entry validation and formatting for TextBoxes at runtime.
Changing a TextBox from currency to percentages or straight numbers to currency.

The code below uses the IValueConverter interface to implement a converter for numbers and currency along
with a second converter to handle percentages. It also contains a button to trigger the change from percentages
to currency and currency back to percentages. While not demonstrated here, you can also adjust the control's binding
validation rules for other data types.

I opted to use an IValueConverter over the StringFormat attribute because the IValueConverters gives me a way
to auto correct invalid string entries that don't match the number type the converter expects.

//  Window1.xaml

<Window x:Class="WPFTutorial.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="518" Width="658" WindowStartupLocation="CenterScreen"
     xmlns:XAMLConverters="clr-namespace:XAMLConverters;assembly=XAMLConverters"
     >
    
    <Window.Resources>
     <XAMLConverters:PercentConverter x:Key="percentConverter" />
     <XAMLConverters:NumberConverter x:Key="numberConverter" />
    </Window.Resources>

        <StackPanel VerticalAlignment="Top">  
           <StackPanel Orientation="Horizontal" VerticalAlignment="Top">  
              <Label Margin="3,3,3,3" Height="25" VerticalAlignment="Top" >Test 1:</Label>
              <TextBox Name="TextBox1" Height="30" Width="200" VerticalAlignment="Center" VerticalContentAlignment="Center"
                       Text="{Binding Path=MoneyRaised, Converter={StaticResource numberConverter}, ConverterParameter=C2}"
                       TextChanged="TextBox1_TextChanged">
              </TextBox>
             </StackPanel>        
              <StackPanel Orientation="Horizontal" VerticalAlignment="Top">  
              <Label Margin="3,3,3,3" Height="25" VerticalAlignment="Top" >Test 2:</Label>
              <TextBox Name="TextBox2" Height="30" Width="200" VerticalAlignment="Center" VerticalContentAlignment="Center"
                       Text="{Binding Path=PercentElectoralVotes, Converter={StaticResource percentConverter}, ConverterParameter=P1}"
                       TextChanged="TextBox2_TextChanged">
              </TextBox>
                  <Button Content="Switch To Currency" Click="Button_Click" Margin="3,3,3,3"/>
             </StackPanel>  

        </StackPanel>            

</Window>

// Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Threading;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Diagnostics;
using BusinessLogic;
using BusinessLogic.Enums;

namespace WPFTutorial
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {

         private Candidate _candidate = new Candidate(1,
                                                     "John",
                                                     "McCain",
                                                     (int)PoliticalPartyTypes.Republican,
                                                     15000000,
                                                     System.Convert.ToDecimal(.52));
      
              public Candidate Candidate
        {
            get { return _candidate; }
            set { _candidate = value; }
        }

         public Window1()
        {
             InitializeComponent();
            this.Loaded += new RoutedEventHandler(Window1_Loaded);
             this.DataContext = Candidate;
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            this.TextBox1.Focus();
        }
    
        private void TextBox1_TextChanged(object sender, TextChangedEventArgs e)
         {
             Debug.WriteLine(_candidate.MoneyRaised.ToString());
         }

         private void TextBox2_TextChanged(object sender, TextChangedEventArgs e)
        {
            Debug.WriteLine(_candidate.PercentElectoralVotes.ToString());
        }

         private void Button_Click(object sender, RoutedEventArgs e)
        {
            Button button = sender as Button;

             this.TextBox2.Text = "0"
                      // Release previous binding by setting the property value
           // directly versus the class property it was bound to.

            Binding binding = new Binding("PercentElectoralVotes");
            binding.Mode = BindingMode.TwoWay;
            binding.Source = _candidate;

             if (button.Content.ToString() == "Switch To Currency")
            {
                binding.Converter = new XAMLConverters.NumberConverter();
                binding.ConverterParameter = "C2";
                button.Content = "Switch To Percentage";
                 this.TextBox2.SetBinding(TextBox.TextProperty, binding);
                 return;
            }

            binding.Converter = new XAMLConverters.NumberConverter();
            binding.ConverterParameter = "P1";
            button.Content = "Switch To Currency";
            this.TextBox2.SetBinding(TextBox.TextProperty, binding);

         }
  
    }
}


// XAMLConvert.NumberConverter

using System;
using System.Windows.Data;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;

namespace XAMLConverters
{
    [ValueConversion(typeof(object),typeof(string))]
    public class NumberConverter: IValueConverter
    {
          public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return Format(value, parameter);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             return Format(value, parameter);
        }

        object Format(object value, object param)
        {
             if (value == null)
                 return value;

             string newValue = value.ToString();
             double rd = 0;

             try
            {
                 if (double.TryParse(newValue, out rd))
                 {
                     return rd.ToString(param.ToString());
                 }
             }
             catch { }

             return rd.ToString(param.ToString());
            
        }

    }
}

// XAMLConverter.PercentConverter

using System;
using System.Windows.Data;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using Shared;

namespace XAMLConverters
{
    [ValueConversion(typeof(object), typeof(string))]
    public class PercentConverter : IValueConverter
    {
         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
         {
             try
            {
                 return System.Convert.ToDecimal(value).ToString(parameter as string);
            }
            catch { }
            return value;
        }
         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
         {
             decimal returnValue = 0;

             try
            {
                 if (value == null) { returnValue.ToString(parameter as string); }

                 try
                 {
                     if (decimal.TryParse(value.ToString(), out returnValue))
                     {
                          return MathHelper.SafeDivide(returnValue, 100).ToString();
                      }
                 }
                 catch { }

             }
             catch { }
            return returnValue.ToString();
        }
    }
}

// MathHelper.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Shared
{
    public class MathHelper
    {
         public static decimal SafeDivide(decimal value1, decimal value2)
        {
             decimal returnValue = 0;

             try
            {
                 if (value1 == 0) { return returnValue; }
                 if (value2 == 0) { return returnValue; }

                 return value1 / value2;
             }
             catch { }
            return returnValue;
        }

        public static double SafeDivide(double value1, double value2)
        {
             double returnValue = 0;

             try
            {
                 if (value1 == 0) { return returnValue; }
                 if (value2 == 0) { return returnValue; }

                 return value1 / value2;
             }
             catch { }
            return returnValue;
        }
      
    }
}

// Candidate.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BusinessLogic
{
    public class Candidate
    {

         public Candidate(int candidateID,
                         string firstName,
                         string lastName,
                         int politicalPartyID,
                         double moneyRaised,
                         decimal percentElectoralVotes)
        {
            _candidateID = candidateID;
            _firstName = firstName;
            _lastName = lastName;
            _politicalPartyTypeID = politicalPartyID;
            _moneyRaised = moneyRaised;
            _percentElectoralVotes = percentElectoralVotes;
        }

        private int _candidateID = 0;
        public int CandidateID
        {
            get { return _candidateID; }
            set { _candidateID = value; }
        }

         public string CandidateName
        {
            get { return FirstName + " " + LastName; }
        }

        private string _firstName = string.Empty;
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value.Trim(); }
        }

        private string _lastName = string.Empty;
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value.Trim(); }
        }

        private int _politicalPartyTypeID = 0;
        public int PoliticalPartyTypeID
        {
            get { return _politicalPartyTypeID; }
            set { _politicalPartyTypeID = value; }
        }

         public Enums.PoliticalPartyTypes PoliticalPartyType
        {
            get { return (Enums.PoliticalPartyTypes)_politicalPartyTypeID; }
         }



         private double _moneyRaised = 0;
        public double MoneyRaised
        {
            get { return _moneyRaised; }
            set
            {
                _moneyRaised = value;
            }
        }

         private decimal _percentElectoralVotes = 0;
        public decimal PercentElectoralVotes
        {
            get { return _percentElectoralVotes; }
            set
            {
                _percentElectoralVotes = value;
            }
        }

     }
}
By Robbe Morris   Popularity  (8277 Views)
Picture
Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.  Robbe also loves to scuba dive and go deep sea fishing in the Florida Keys or off the coast of Daytona Beach. Microsoft MVP