次の方法で共有


Integer to Roman Numeral Value Converter

Databinding uses Value Converters to bind a value in one component's "Language" to another. For instance, there's a converter that converts a boolean to a "Visibility" in WPF. So, True is mapped to Visible, False is mapped to Hidden, etc... By using it, we can virtually databind any two elements together however we like.

I was having a random discussion with Jen Rowe, and we were talking about Roman Numerals. I decided to mess around for a few minutes, and created a Value Converter that takes a value, converts it to an integer, then outputs that integer as a roman numeral. I thought I'd post the results of that here. And, as a kicker, a quick explination of how to use it in Expression Interactive Designer. (Come on, you don't think I'd miss that opportunity, do you?)

Anyway, first the code:

 using System;using System.Windows.Data;using System.Collections.Generic;using System.Text; namespace ValueConverters{   public class RomanNumeralizer : IValueConverter   {      public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)      {         return this.ConvertToRomanNumeral(System.Convert.ToInt32(value));      }
      public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)      {         return null;      }
      private List<RomanNumeralPair> PairSet      {         get         {            List<RomanNumeralPair> output = new List<RomanNumeralPair>();            output.Add(new RomanNumeralPair(1000, "M"));            output.Add(new RomanNumeralPair(900, "CM"));            output.Add(new RomanNumeralPair(500, "D"));            output.Add(new RomanNumeralPair(400, "CD"));            output.Add(new RomanNumeralPair(100, "C"));            output.Add(new RomanNumeralPair(90, "XC"));            output.Add(new RomanNumeralPair(50, "L"));            output.Add(new RomanNumeralPair(40, "XL"));            output.Add(new RomanNumeralPair(10, "X"));            output.Add(new RomanNumeralPair(9, "IX"));            output.Add(new RomanNumeralPair(5, "V"));            output.Add(new RomanNumeralPair(4, "IV"));            output.Add(new RomanNumeralPair(1, "I"));            return output;         }      }
      private string ConvertToRomanNumeral(int input)      {         StringBuilder myBuilder = new StringBuilder();         foreach (RomanNumeralPair thisPair in this.PairSet)         {            while (input >= thisPair.Value)            {               myBuilder.Append(thisPair.StringValue);               input -= thisPair.Value;            }         }         return myBuilder.ToString();      }   }
   public class RomanNumeralPair   {      private int value;      private string stringValue;      public int Value      {         get         {            return this.value;         }      }       public string StringValue      {         get         {            return this.stringValue;         }      }
      public RomanNumeralPair(int value, string stringValue)      {         this.value = value;         this.stringValue = stringValue;      }   }}

So, a few words on this. The RomanNumeralPair class is really just an easy way to keep a int/string combination together. There may be an easier way, but this is what I threw together quickly. The PairSet returns the basic rules for Roman Numbers. Then, ConvertToRomanNumeral just keeps checking to see if our input is greater then the current Value, and if so, append the StringValue onto our string builder and decrement the input accordingly.

Here's a 'stack trace', if you want to understand it.

Input Current Pair Is it larger or equal? myBuilder New Input
369 M, 1000 No   369
369 CM, 900 No   369
369 D, 500 No   369
369 DC, 400 No   369
369 C, 100 Yes C 269
269 C, 100 Yes CC 169
169 C, 100 Yes CCC 69
69 C, 100 No CCC 69
69 XC, 90 No CCC 69
69 L, 50 Yes CCCL 19
19 L, 50 No CCCL 19
19 XL, 40 No CCCL 19
19 X, 10 Yes CCCLX 9
9 X, 10 No CCCLX 9
9 IX, 9 Yes CCCLXIX 0
0 IX, 9 No CCCLXIX 0
0 V, 5 No CCCLXIX 0
0 IV, 4 No CCCLXIX 0
0 I, 1 No CCCLXIX 0

Yes, I could have accelerated the algorithm, by including a (if input == 0) {return input;}, but in this case, I left the code as is for "Elegance". So, you can see how we get our roman numeral from our input.

Then, looking at the top of the code, you can see the class that implements IValueConverter. This class is a WPF class, that only requires the two methods you see. Convert and ConvertBack. If I wanted to have a two way binding (I.e. let the user type in a roman numeral, and parse it back to an integer), I'd have to supply the ConvertBack. But, I didn't feel like implementing that part right now.

So, now how do we hook it up in Expression? This part is easy.

First, you'll need to create the class library for the code above. Pop that into VS, and it's pretty easy to do. You will need to add the WPF PresentationFramework as a referenced assembly, but I'm assuming if you're running ExprID, you've got that on your machine already. Mileage may vary, but on my machine it's in %ProgramFiles%\Reference Assemblies\Microsoft\WinFX\v3.0\PresentationFramework.dll.

Once you've got that library, open ExprID.

  1. Create a new project.
  2. Add a reference (Project/Add Reference)
  3. Browse to the Assembly you just created with the Value Converter
  4. Place a Slider and a Label on the scene (You'll probably want a WIDE slider)
  5. Set the Slider's Maximum value to 2001
  6. Find the Content property on the Label.
  7. Right click Content and choose Databind.
  8. In the Databinding dialog, select Element Property as the Binding Source.
  9. Expand the "More Binding Options..." panel at the bottom.
  10. Click the '...' button to the right of the ValueConverter field.
  11. The Add value converter dialog comes up. Find your assembly and expand it. You should see the RomanNumeralizer inside it. Choose it.
  12. Hit Ok, and you'll see that RomanNumeralizer has been set as the Value Converter.
  13. Hit Finish
  14. Press F5 to test the project

If you've done everything correctly, as you slide the slider, you'll see the label get updated with the Roman Numeral representation of whatever number the slider's value is at.

This isn't the most elegant solution in the world, but you should see at least how to create and use a Value Converter. A couple examples I've done are things like changing the integer to a literal "Score: 1,283,439". Putting the commas and the "Score: " string are done by initializing the StringBuilder differently and using a String.Format.

--Dante

Comments

  • Anonymous
    March 23, 2006
    Dante Gagne, a Software Design Engineer in Test in the Expression Interactive Designer team, has published...