Archive

Archive for the ‘C#’ Category

Model Binder

December 15th, 2009

When working with a multi-tier application I often find myself converting from one of the tiers object models to my own similar (but often different) model.  I often write code that would set one by one each property from a web tier object to my object.  In order to make this easier I wrote this object binder.  It is a fake deep copy in a way.  You tell it what destination type you want to build and give it a source object and it will go through each property (matching by names) and checks if it can copy from that property from the source  to the destination.  If it can’t it will call itself again on that object to try to copy its properties.

Here is the code:

   1: using System;
   2: using System.Reflection;
   3: using System.Collections;
   4:  
   5: namespace Binder
   6: {
   7:     public class ObjectBinder
   8:     {
   9:         /// <summary>
  10:         /// Copies one object to an instance of a different type.
  11:         /// 
  12:         /// Assumptions:
  13:         /// Object which implements IList'1 also implements IList 
  14:         /// Object which implements IDictionary'2 also implements IDictionary 
  15:         /// </summary>
  16:         /// <typeparam name="T"></typeparam>
  17:         /// <param name="source">The source.</param>
  18:         /// <returns></returns>
  19:         public static T Copy<T>(object source) where T : new()
  20:         {
  21:             return (T)Copy(source, typeof(T));
  22:         }
  23:  
  24:  
  25:         private static object Copy(object source, Type destinationType)
  26:         {
  27:             if (source == null) throw new ArgumentNullException("source", "source must not be null");
  28:  
  29:             var destination = Activator.CreateInstance(destinationType);
  30:  
  31:             var sourceProperties = source.GetType().GetProperties();
  32:             foreach (var sourceProp in sourceProperties)
  33:             {
  34:                 var sourcePropType = sourceProp.PropertyType;
  35:                 var destinationProp = destination.GetType().GetProperty(sourceProp.Name);
  36:  
  37:                 if (sourceProp.CanRead && destinationProp != null && destinationProp.CanWrite)
  38:                 {
  39:                     var destinationPropType = destinationProp.PropertyType;
  40:                     Type sourcePropInterfaceType = null;
  41:                     Type destinationPropInterfaceType = null;
  42:  
  43:                     // Skip indexer properties
  44:                     if (sourceProp.GetIndexParameters().Length > 0) continue;
  45:                     
  46:                     var sourceValue = sourceProp.GetValue(source, null);
  47:                     if (sourceValue == null) continue;
  48:  
  49:                     object destinationValue = null;
  50:                     if (destinationPropType.IsAssignableFrom(sourcePropType))
  51:                     {
  52:                         destinationValue = sourceValue;
  53:                     }
  54:                     else if (
  55:                         (sourcePropInterfaceType = sourcePropType.GetInterface("IList`1", true)) != null &&
  56:                         (destinationPropInterfaceType = destinationPropType.GetInterface("IList`1", true)) != null)
  57:                     {
  58:  
  59:                         var sourceArgType = sourcePropInterfaceType.GetGenericArguments()[0];
  60:                         var destArgType = destinationPropInterfaceType.GetGenericArguments()[0];
  61:                         bool isAssignable = destArgType.IsAssignableFrom(sourceArgType);
  62:  
  63:                         IList listCopy = null;
  64:  
  65:                         if (destinationPropType.IsArray)
  66:                         {
  67:                             // If array there must be a one argument constructor
  68:                             listCopy = (IList)Activator.CreateInstance(destinationPropType, ((IList)sourceValue).Count);
  69:                         }
  70:                         else
  71:                         {
  72:                             // If not an array require a parameterless constructor
  73:                             if (destinationPropType.GetConstructor(Type.EmptyTypes) == null) continue;
  74:                             listCopy = (IList)Activator.CreateInstance(destinationPropType);
  75:                         }
  76:  
  77:                         int index = 0;
  78:                         foreach (var item in ((IList)sourceValue))
  79:                         {
  80:                             var itemCopy = item;
  81:                             if (!isAssignable)
  82:                                 itemCopy = Copy(item, destArgType);
  83:  
  84:                             if (destinationPropType.IsArray)
  85:                                 listCopy[index] = itemCopy;
  86:                             else
  87:                                 listCopy.Add(itemCopy);
  88:  
  89:                             index++;
  90:                         }
  91:  
  92:                         destinationValue = listCopy;
  93:                     }
  94:                     else if (
  95:                         (sourcePropInterfaceType = sourcePropType.GetInterface("IDictionary`2", true)) != null &&
  96:                         (destinationPropInterfaceType = destinationPropType.GetInterface("IDictionary`2", true)) != null)
  97:                     {
  98:  
  99:                         var sourceArgTypes = sourcePropInterfaceType.GetGenericArguments();
 100:                         var destArgTypes = destinationPropInterfaceType.GetGenericArguments();
 101:                         bool isKeyAssignable = destArgTypes[0].IsAssignableFrom(sourceArgTypes[0]);
 102:                         bool isValueAssignable = destArgTypes[1].IsAssignableFrom(sourceArgTypes[1]);
 103:  
 104:                         var defaultConstructor = destinationPropType.GetConstructor(Type.EmptyTypes);
 105:                         if (defaultConstructor == null) continue;
 106:                         var dictionaryCopy = Activator.CreateInstance(destinationPropType) as IDictionary;
 107:  
 108:                         foreach (DictionaryEntry pair in ((IDictionary)sourceValue))
 109:                         {
 110:                             var keyCopy = pair.Key;
 111:                             var valueCopy = pair.Value;
 112:                             if (!isKeyAssignable)
 113:                                 keyCopy = Copy(keyCopy, destArgTypes[0]);
 114:                             if (!isValueAssignable)
 115:                                 valueCopy = Copy(valueCopy, destArgTypes[1]);
 116:  
 117:                             dictionaryCopy.Add(keyCopy, valueCopy);
 118:                         }
 119:  
 120:                         destinationValue = dictionaryCopy;
 121:                     }
 122:                     else
 123:                     {
 124:                         destinationValue = Copy(sourceValue, destinationPropType);
 125:                     }
 126:  
 127:  
 128:                     destinationProp.SetValue(destination, destinationValue, null);
 129:  
 130:                 }
 131:             }
 132:  
 133:  
 134:             return destination;
 135:         }
 136:     }
 137: }


And the tests using XUnit.Net:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using Xunit;
   6: using System.Collections.ObjectModel;
   7:  
   8: namespace Binder
   9: {
  10:     public class ObjectBinderFacts
  11:     {
  12:         public class The_Copy_Method
  13:         {
  14:  
  15:             [Fact]
  16:             public void throws_if_source_is_null()
  17:             {
  18:  
  19:                 Exception ex = Record.Exception(() => ObjectBinder.Copy<Destination>(null));
  20:  
  21:                 Assert.IsType<ArgumentNullException>(ex);
  22:                 Assert.Equal("source", ((ArgumentNullException)ex).ParamName);
  23:             }
  24:  
  25:             [Fact]
  26:             public void will_copy_assignable_scalar_property()
  27:             {
  28:                 var source = new Source();
  29:  
  30:                 var res = ObjectBinder.Copy<Destination>(source);
  31:  
  32:                 Assert.Equal(source.A, res.A);
  33:                 Assert.Equal(source.B, res.B);
  34:                 Assert.Equal(source.C, res.C);
  35:                 Assert.Equal(source.D, res.D);
  36:                 Assert.Equal(source.E, res.E);
  37:  
  38:             }
  39:  
  40:             [Fact]
  41:             public void will_copy_assignable_generic_list_property()
  42:             {
  43:                 var source = new Source();
  44:  
  45:                 var res = ObjectBinder.Copy<Destination>(source);
  46:  
  47:                 Assert.Equal(source.F.Count, res.F.Count);
  48:                 Assert.Equal(source.F[0], res.F[0]);
  49:                 Assert.Equal(source.F[1], res.F[1]);
  50:  
  51:             }
  52:  
  53:             [Fact]
  54:             public void will_copy_assignable_array_property()
  55:             {
  56:                 var source = new Source();
  57:  
  58:                 var res = ObjectBinder.Copy<Destination>(source);
  59:  
  60:                 Assert.Equal(source.N.Length, res.N.Length);
  61:                 Assert.Equal(source.N[0], res.N[0]);
  62:                 Assert.Equal(source.N[1], res.N[1]);
  63:  
  64:             }
  65:  
  66:             [Fact]
  67:             public void will_copy_unassignable_array_property()
  68:             {
  69:                 var source = new Source();
  70:  
  71:                 var res = ObjectBinder.Copy<Destination>(source);
  72:  
  73:                 Assert.Equal(source.O.Length, res.O.Length);
  74:                 Assert.Equal(source.O[0].Value, res.O[0].Value);
  75:                 Assert.Equal(source.O[1].Value, res.O[1].Value);
  76:  
  77:             }
  78:  
  79:             [Fact]
  80:             public void will_copy_assignable_generic_dictionary_property()
  81:             {
  82:                 var source = new Source();
  83:  
  84:                 var res = ObjectBinder.Copy<Destination>(source);
  85:  
  86:                 Assert.Equal(source.J.Count, res.J.Count);
  87:                 Assert.Equal(source.J["a"], res.J["a"]);
  88:                 Assert.Equal(source.J["b"], res.J["b"]);
  89:  
  90:             }
  91:  
  92:             [Fact]
  93:             public void will_copy_unassignable_generic_dictionary_with_assignable_keys_and_values()
  94:             {
  95:                 var source = new Source();
  96:  
  97:                 var res = ObjectBinder.Copy<Destination>(source);
  98:  
  99:                 Assert.Equal(source.K.Count, res.K.Count);
 100:                 Assert.Equal(source.K["a"], res.K["a"]);
 101:                 Assert.Equal(source.K["b"], res.K["b"]);
 102:  
 103:             }
 104:  
 105:             [Fact]
 106:             public void will_copy_unassignable_generic_dictionary_with_assignable_keys_and_unassignable_values()
 107:             {
 108:                 var source = new Source();
 109:  
 110:                 var res = ObjectBinder.Copy<Destination>(source);
 111:  
 112:                 Assert.Equal(source.L.Count, res.L.Count);
 113:                 Assert.Equal(source.L["a"].Value, res.L["a"].Value);
 114:                 Assert.Equal(source.L["b"].Value, res.L["b"].Value);
 115:  
 116:             }
 117:  
 118:             [Fact]
 119:             public void will_copy_unassignable_generic_dictionary_with_unassignable_keys_and_unassignable_values()
 120:             {
 121:                 var source = new Source();
 122:  
 123:                 var res = ObjectBinder.Copy<Destination>(source);
 124:  
 125:                 Assert.Equal(source.M.Count, res.M.Count);
 126:                 Assert.Equal(source.M[new SomeObject { Value = 1 }].Value, res.M[new OtherObject { Value = 1 }].Value);
 127:                 Assert.Equal(source.M[new SomeObject { Value = 2 }].Value, res.M[new OtherObject { Value = 2 }].Value);
 128:  
 129:             }
 130:  
 131:             [Fact]
 132:             public void will_copy_unassignable_generic_list_with_assignable_values()
 133:             {
 134:                 var source = new Source();
 135:  
 136:                 var res = ObjectBinder.Copy<Destination>(source);
 137:  
 138:                 Assert.Equal(source.G.Count, res.G.Count);
 139:                 Assert.Equal(source.G[0], res.G[0]);
 140:                 Assert.Equal(source.G[1], res.G[1]);
 141:  
 142:             }
 143:  
 144:             [Fact]
 145:             public void will_copy_unassignable_object_property()
 146:             {
 147:                 var source = new Source();
 148:  
 149:                 var res = ObjectBinder.Copy<Destination>(source);
 150:  
 151:                 Assert.Equal(source.H.Value, res.H.Value);
 152:  
 153:             }
 154:  
 155:             [Fact]
 156:             public void will_copy_unassignable_generic_list_properties()
 157:             {
 158:                 var source = new Source();
 159:  
 160:                 var res = ObjectBinder.Copy<Destination>(source);
 161:  
 162:                 Assert.Equal(source.I.Count, res.I.Count);
 163:                 Assert.Equal(source.I[0].Value, res.I[0].Value);
 164:                 Assert.Equal(source.I[1].Value, res.I[1].Value);
 165:  
 166:             }
 167:  
 168:             [Fact]
 169:             public void will_return_object_with_default_values_if_no_properties_match()
 170:             {
 171:                 var source = new Source();
 172:  
 173:                 var res = ObjectBinder.Copy<SomeObject>(source);
 174:  
 175:                 Assert.Equal(0, res.Value);
 176:             }
 177:  
 178:             class SomeObject
 179:             {
 180:                 public int Value { get; set; }
 181:  
 182:                 public override int GetHashCode()
 183:                 {
 184:                     return Value;
 185:                 }
 186:  
 187:                 public override bool Equals(object obj)
 188:                 {
 189:                     var some = obj as SomeObject;
 190:                     if (obj == null) return false;
 191:                     return some.Value == Value;
 192:                 }
 193:             }
 194:  
 195:             class OtherObject
 196:             {
 197:                 public int Value { get; set; }
 198:  
 199:                 public override int GetHashCode()
 200:                 {
 201:                     return Value;
 202:                 }
 203:  
 204:                 public override bool Equals(object obj)
 205:                 {
 206:                     var some = obj as OtherObject;
 207:                     if (obj == null) return false;
 208:                     return some.Value == Value;
 209:                 }
 210:             }
 211:  
 212:             class OtherDictionary<TKey, TValue> : Dictionary<TKey, TValue>
 213:             {
 214:  
 215:             }
 216:  
 217:             class Source
 218:             {
 219:                 public Source()
 220:                 {
 221:                     A = "String";
 222:                     B = 5;
 223:                     C = Guid.NewGuid();
 224:                     D = 5.5;
 225:                     E = 'a';
 226:                     F = new List<int> { 1, 2 };
 227:                     G = new List<string> { "a", "b" };
 228:                     H = new SomeObject();
 229:                     I = new List<SomeObject> { new SomeObject { Value = 1 }, new SomeObject { Value = 2 } };
 230:                     J = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 231:                     K = new Dictionary<string, int> { { "a", 1 }, { "b", 2 } };
 232:                     L = new Dictionary<string, SomeObject> { { "a", new SomeObject { Value = 1 } }, { "b", new SomeObject { Value = 2 } } };
 233:                     M = new Dictionary<SomeObject, SomeObject> {
 234:                         { new SomeObject { Value = 1 }, new SomeObject { Value = 10 } }, 
 235:                         { new SomeObject { Value = 2 }, new SomeObject { Value = 20 } } 
 236:                     };
 237:                     N = new[] { 1, 2 };
 238:                     O = new[] { new SomeObject { Value = 1 }, new SomeObject { Value = 2 } };
 239:                 }
 240:  
 241:                 // Assignable
 242:                 public string A { get; set; }
 243:                 public int B { get; set; }
 244:                 public Guid C { get; set; }
 245:                 public double D { get; set; }
 246:                 public char E { get; set; }
 247:                 public List<int> F { get; set; }
 248:                 public Dictionary<string, int> J { get; set; }
 249:                 public int[] N { get; set; }
 250:  
 251:                 // Not assignable
 252:                 public List<string> G { get; set; }
 253:                 public SomeObject H { get; set; }
 254:                 public List<SomeObject> I { get; set; }
 255:                 public Dictionary<string, int> K { get; set; }
 256:                 public Dictionary<string, SomeObject> L { get; set; }
 257:                 public Dictionary<SomeObject, SomeObject> M { get; set; }
 258:                 public SomeObject[] O { get; set; }
 259:             }
 260:  
 261:  
 262:             class Destination
 263:             {
 264:                 public string A { get; set; }
 265:                 public int B { get; set; }
 266:                 public Guid C { get; set; }
 267:                 public double D { get; set; }
 268:                 public char E { get; set; }
 269:                 public List<int> F { get; set; }
 270:                 public Dictionary<string, int> J { get; set; }
 271:                 public int[] N { get; set; }
 272:  
 273:                 public Collection<string> G { get; set; }
 274:                 public OtherObject H { get; set; }
 275:                 public List<OtherObject> I { get; set; }
 276:                 public OtherDictionary<string, int> K { get; set; }
 277:                 public Dictionary<string, OtherObject> L { get; set; }
 278:                 public Dictionary<OtherObject, OtherObject> M { get; set; }
 279:                 public OtherObject[] O { get; set; }
 280:             }
 281:         }
 282:     }
 283: }

One possible enhancement I thought about was to add a attribute to indicate on the destination model that is maps to a property of a different name.  I haven’t had a need for it yet but I will surely add it when I do.

Author: Matt Categories: C# Tags:

Useful Moq Extension Method

November 24th, 2009
Comments Off

I have been working with ASP .NET MVC and I use the Moq mocking library to help test the code I write.   Often in ASP MVC anonymous objects are passed around as function arguments.  This is especially common in calls to RouteUrl.  Since I want to be able to test this and verify that it is called correctly I wrote a handy extension method to make it easier called AsMatch.

To be able to test the UrlHelper (since it doesn't have an interface or virtual methods) I wrote a simple wrapper around it with an interface to enable testing.  Using that with this extension method makes testing route generation a breeze.

Given a call to generate a Url like this:

var url = Url.RouteUrl("Show", new { projectName="matt" });

You can write a setup on your Mock of the Url helper using a similar syntax:

UrlHelperMock
.Setup(x => x.RouteUrl("Show",new{ projectName="matt"}.AsMatch()))
.Return("someUrlToTest");

Here is the code that defines the AsMatch extension method:

    public static class ObjectMoqExtensions
    {
        public static object AsMatch(this object @object)
        {
            return Match<object>.Create(testObject => DoObjectsMatch(@object, testObject));
        }
        private static bool DoObjectsMatch(object object1, object object2)
        {
            var props1 = ToDictionary(object1);
            var props2 = ToDictionary(object2);
            var query = from prop1 in props1
                        join prop2 in props2 on prop1.Key equals prop2.Key
                        select prop1.Value.Equals(prop2.Value);
            return query.Count(x => x) == Math.Max(props1.Count(), props2.Count());
        }
        public static Dictionary<string, object> ToDictionary(object @object)
        {
            var dictionary = new Dictionary<string, object>();
            var properties = TypeDescriptor.GetProperties(@object);
            foreach (PropertyDescriptor property in properties)
                dictionary.Add(property.Name, property.GetValue(@object));
            return dictionary;
        }
    }
Author: MattManela Categories: ASP .NET MVC, C#, Moq Tags:

Useful Moq Extension Method

November 24th, 2009
Comments Off

I have been working with ASP .NET MVC and I use the Moq mocking library to help test the code I write.   Often in ASP MVC anonymous objects are passed around as function arguments.  This is especially common in calls to RouteUrl.  Since I want to be able to test this and verify that it is called correctly I wrote a handy extension method to make it easier called AsMatch.

To be able to test the UrlHelper (since it doesn't have an interface or virtual methods) I wrote a simple wrapper around it with an interface to enable testing.  Using that with this extension method makes testing route generation a breeze.

Given a call to generate a Url like this:

var url = Url.RouteUrl("Show", new { projectName="matt" });

You can write a setup on your Mock of the Url helper using a similar syntax:

UrlHelperMock
.Setup(x => x.RouteUrl("Show",new{ projectName="matt"}.AsMatch()))
.Return("someUrlToTest");

Here is the code that defines the AsMatch extension method:

    public static class ObjectMoqExtensions
    {
        public static object AsMatch(this object @object)
        {
            return Match<object>.Create(testObject => DoObjectsMatch(@object, testObject));
        }
        private static bool DoObjectsMatch(object object1, object object2)
        {
            var props1 = ToDictionary(object1);
            var props2 = ToDictionary(object2);
            var query = from prop1 in props1
                        join prop2 in props2 on prop1.Key equals prop2.Key
                        select prop1.Value.Equals(prop2.Value);
            return query.Count(x => x) == Math.Max(props1.Count(), props2.Count());
        }
        public static Dictionary<string, object> ToDictionary(object @object)
        {
            var dictionary = new Dictionary<string, object>();
            var properties = TypeDescriptor.GetProperties(@object);
            foreach (PropertyDescriptor property in properties)
                dictionary.Add(property.Name, property.GetValue(@object));
            return dictionary;
        }
    }
Author: MattManela Categories: ASP .NET MVC, C#, Moq Tags:

Useful Moq Extension Method

November 24th, 2009
Comments Off

I have been working with ASP .NET MVC and I use the Moq mocking library to help test the code I write.   Often in ASP MVC anonymous objects are passed around as function arguments.  This is especially common in calls to RouteUrl.  Since I want to be able to test this and verify that it is called correctly I wrote a handy extension method to make it easier called AsMatch.

To be able to test the UrlHelper (since it doesn't have an interface or virtual methods) I wrote a simple wrapper around it with an interface to enable testing.  Using that with this extension method makes testing route generation a breeze.

Given a call to generate a Url like this:

var url = Url.RouteUrl("Show", new { projectName="matt" });

You can write a setup on your Mock of the Url helper using a similar syntax:

UrlHelperMock
.Setup(x => x.RouteUrl("Show",new{ projectName="matt"}.AsMatch()))
.Return("someUrlToTest");

Here is the code that defines the AsMatch extension method:

    public static class ObjectMoqExtensions
    {
        public static object AsMatch(this object @object)
        {
            return Match<object>.Create(testObject => DoObjectsMatch(@object, testObject));
        }
        private static bool DoObjectsMatch(object object1, object object2)
        {
            var props1 = ToDictionary(object1);
            var props2 = ToDictionary(object2);
            var query = from prop1 in props1
                        join prop2 in props2 on prop1.Key equals prop2.Key
                        select prop1.Value.Equals(prop2.Value);
            return query.Count(x => x) == Math.Max(props1.Count(), props2.Count());
        }
        public static Dictionary<string, object> ToDictionary(object @object)
        {
            var dictionary = new Dictionary<string, object>();
            var properties = TypeDescriptor.GetProperties(@object);
            foreach (PropertyDescriptor property in properties)
                dictionary.Add(property.Name, property.GetValue(@object));
            return dictionary;
        }
    }
Author: MattManela Categories: ASP .NET MVC, C#, Moq Tags:

Converting RTF to HTML

September 28th, 2009
Comments Off

Have you ever had the desire to convert some RTF text into HTML? Probably not. But if you do, then you are in luck! I recently had the need to do this conversion and after some searching found out a way to do it by enhancing a sample distributed in the MSDN library.  The sample is called: XAML to HTML Conversion Demo

The sample has code which converts HTML to and from a XAML Flow Document.  But this doesn’t make things easier until you realize that there is a way to convert RTF to XAML easily. The key is to use System.Windows.Controls.RichTextBox which can load RTF from a stream and save it as XAML.  This conversion is shown below:

        private static string ConvertRtfToXaml(string rtfText)
        {
            var richTextBox = new RichTextBox();
            if (string.IsNullOrEmpty(rtfText)) return "";
            var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            using (var rtfMemoryStream = new MemoryStream())
            {
                using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
                {
                    rtfStreamWriter.Write(rtfText);
                    rtfStreamWriter.Flush();
                    rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    textRange.Load(rtfMemoryStream, DataFormats.Rtf);
                }
            }
            using (var rtfMemoryStream = new MemoryStream())
            {
                textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                textRange.Save(rtfMemoryStream, DataFormats.Xaml);
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
                {
                    return rtfStreamReader.ReadToEnd();
                }
            }
        }

With this code we have all we need to convert RTF to HTML. I modified the sample to add this RTF To XAML conversation and then I run that XAML through HTML converter which results in the HTML text. I added an interface to these conversion utilities and converted the sample into a library so that I would be able to use it from other projects.  Here is the interface:

 public interface IMarkupConverter
    {
        string ConvertXamlToHtml(string xamlText);
        string ConvertHtmlToXaml(string htmlText);
        string ConvertRtfToHtml(string rtfText);
    }
    public class MarkupConverter : IMarkupConverter
    {
        public string ConvertXamlToHtml(string xamlText)
        {
            return HtmlFromXamlConverter.ConvertXamlToHtml(xamlText, false);
        }
        public string ConvertHtmlToXaml(string htmlText)
        {
            return HtmlToXamlConverter.ConvertHtmlToXaml(htmlText, true);
        }
        public string ConvertRtfToHtml(string rtfText)
        {
            return RtfToHtmlConverter.ConvertRtfToHtml(rtfText);
        }
    }

With this I am now able to convert from RTF to HTML.  However, there is one catch - the conversion uses the RichTextBox WPF control which requires a single threaded apartment (STA).  Therefore in order to run your code that calls the ConvertRtfToHtml function, it must also be running in a STA.  If you can’t have your program run in a STA then you must create a new STA thread to run the conversion. Like this:

MarkupConverter markupConverter = new MarkupConverter();
private string ConvertRtfToHtml(string rtfText)
{
   var thread = new Thread(ConvertRtfInSTAThread);
   var threadData = new ConvertRtfThreadData { RtfText = rtfText };
   thread.SetApartmentState(ApartmentState.STA);
   thread.Start(threadData);
   thread.Join();
   return threadData.HtmlText;
}
private void ConvertRtfInSTAThread(object rtf)
{
   var threadData = rtf as ConvertRtfThreadData;
   threadData.HtmlText = markupConverter.ConvertRtfToHtml(threadData.RtfText);
}
        
private class ConvertRtfThreadData
{
   public string RtfText { get; set; }
   public string HtmlText { get; set; }
}

Here is the zip contain the code for the Markup converter: MarkupConverter.zip

Author: MattManela Categories: C#, HTML, RTF, WPF, XAML Tags:

Converting RTF to HTML

September 28th, 2009
Comments Off

Have you ever had the desire to convert some RTF text into HTML? Probably not. But if you do, then you are in luck! I recently had the need to do this conversion and after some searching found out a way to do it by enhancing a sample distributed in the MSDN library.  The sample is called: XAML to HTML Conversion Demo

The sample has code which converts HTML to and from a XAML Flow Document.  But this doesn’t make things easier until you realize that there is a way to convert RTF to XAML easily. The key is to use System.Windows.Controls.RichTextBox which can load RTF from a stream and save it as XAML.  This conversion is shown below:

        private static string ConvertRtfToXaml(string rtfText)
        {
            var richTextBox = new RichTextBox();
            if (string.IsNullOrEmpty(rtfText)) return "";
            var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            using (var rtfMemoryStream = new MemoryStream())
            {
                using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
                {
                    rtfStreamWriter.Write(rtfText);
                    rtfStreamWriter.Flush();
                    rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    textRange.Load(rtfMemoryStream, DataFormats.Rtf);
                }
            }
            using (var rtfMemoryStream = new MemoryStream())
            {
                textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                textRange.Save(rtfMemoryStream, DataFormats.Xaml);
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
                {
                    return rtfStreamReader.ReadToEnd();
                }
            }
        }

With this code we have all we need to convert RTF to HTML. I modified the sample to add this RTF To XAML conversation and then I run that XAML through HTML converter which results in the HTML text. I added an interface to these conversion utilities and converted the sample into a library so that I would be able to use it from other projects.  Here is the interface:

 public interface IMarkupConverter
    {
        string ConvertXamlToHtml(string xamlText);
        string ConvertHtmlToXaml(string htmlText);
        string ConvertRtfToHtml(string rtfText);
    }
    public class MarkupConverter : IMarkupConverter
    {
        public string ConvertXamlToHtml(string xamlText)
        {
            return HtmlFromXamlConverter.ConvertXamlToHtml(xamlText, false);
        }
        public string ConvertHtmlToXaml(string htmlText)
        {
            return HtmlToXamlConverter.ConvertHtmlToXaml(htmlText, true);
        }
        public string ConvertRtfToHtml(string rtfText)
        {
            return RtfToHtmlConverter.ConvertRtfToHtml(rtfText);
        }
    }

With this I am now able to convert from RTF to HTML.  However, there is one catch - the conversion uses the RichTextBox WPF control which requires a single threaded apartment (STA).  Therefore in order to run your code that calls the ConvertRtfToHtml function, it must also be running in a STA.  If you can’t have your program run in a STA then you must create a new STA thread to run the conversion. Like this:

MarkupConverter markupConverter = new MarkupConverter();
private string ConvertRtfToHtml(string rtfText)
{
   var thread = new Thread(ConvertRtfInSTAThread);
   var threadData = new ConvertRtfThreadData { RtfText = rtfText };
   thread.SetApartmentState(ApartmentState.STA);
   thread.Start(threadData);
   thread.Join();
   return threadData.HtmlText;
}
private void ConvertRtfInSTAThread(object rtf)
{
   var threadData = rtf as ConvertRtfThreadData;
   threadData.HtmlText = markupConverter.ConvertRtfToHtml(threadData.RtfText);
}
        
private class ConvertRtfThreadData
{
   public string RtfText { get; set; }
   public string HtmlText { get; set; }
}

Here is the zip contain the code for the Markup converter: MarkupConverter.zip

Author: MattManela Categories: C#, HTML, RTF, WPF, XAML Tags:

Converting RTF to HTML

September 28th, 2009
Comments Off

Have you ever had the desire to convert some RTF text into HTML? Probably not. But if you do, then you are in luck! I recently had the need to do this conversion and after some searching found out a way to do it by enhancing a sample distributed in the MSDN library.  The sample is called: XAML to HTML Conversion Demo

The sample has code which converts HTML to and from a XAML Flow Document.  But this doesn’t make things easier until you realize that there is a way to convert RTF to XAML easily. The key is to use System.Windows.Controls.RichTextBox which can load RTF from a stream and save it as XAML.  This conversion is shown below:

        private static string ConvertRtfToXaml(string rtfText)
        {
            var richTextBox = new RichTextBox();
            if (string.IsNullOrEmpty(rtfText)) return "";
            var textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
            using (var rtfMemoryStream = new MemoryStream())
            {
                using (var rtfStreamWriter = new StreamWriter(rtfMemoryStream))
                {
                    rtfStreamWriter.Write(rtfText);
                    rtfStreamWriter.Flush();
                    rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                    textRange.Load(rtfMemoryStream, DataFormats.Rtf);
                }
            }
            using (var rtfMemoryStream = new MemoryStream())
            {
                textRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd);
                textRange.Save(rtfMemoryStream, DataFormats.Xaml);
                rtfMemoryStream.Seek(0, SeekOrigin.Begin);
                using (var rtfStreamReader = new StreamReader(rtfMemoryStream))
                {
                    return rtfStreamReader.ReadToEnd();
                }
            }
        }

With this code we have all we need to convert RTF to HTML. I modified the sample to add this RTF To XAML conversation and then I run that XAML through HTML converter which results in the HTML text. I added an interface to these conversion utilities and converted the sample into a library so that I would be able to use it from other projects.  Here is the interface:

 public interface IMarkupConverter
    {
        string ConvertXamlToHtml(string xamlText);
        string ConvertHtmlToXaml(string htmlText);
        string ConvertRtfToHtml(string rtfText);
    }
    public class MarkupConverter : IMarkupConverter
    {
        public string ConvertXamlToHtml(string xamlText)
        {
            return HtmlFromXamlConverter.ConvertXamlToHtml(xamlText, false);
        }
        public string ConvertHtmlToXaml(string htmlText)
        {
            return HtmlToXamlConverter.ConvertHtmlToXaml(htmlText, true);
        }
        public string ConvertRtfToHtml(string rtfText)
        {
            return RtfToHtmlConverter.ConvertRtfToHtml(rtfText);
        }
    }

With this I am now able to convert from RTF to HTML.  However, there is one catch - the conversion uses the RichTextBox WPF control which requires a single threaded apartment (STA).  Therefore in order to run your code that calls the ConvertRtfToHtml function, it must also be running in a STA.  If you can’t have your program run in a STA then you must create a new STA thread to run the conversion. Like this:

MarkupConverter markupConverter = new MarkupConverter();
private string ConvertRtfToHtml(string rtfText)
{
   var thread = new Thread(ConvertRtfInSTAThread);
   var threadData = new ConvertRtfThreadData { RtfText = rtfText };
   thread.SetApartmentState(ApartmentState.STA);
   thread.Start(threadData);
   thread.Join();
   return threadData.HtmlText;
}
private void ConvertRtfInSTAThread(object rtf)
{
   var threadData = rtf as ConvertRtfThreadData;
   threadData.HtmlText = markupConverter.ConvertRtfToHtml(threadData.RtfText);
}
        
private class ConvertRtfThreadData
{
   public string RtfText { get; set; }
   public string HtmlText { get; set; }
}

Here is the zip contain the code for the Markup converter: MarkupConverter.zip

Author: MattManela Categories: C#, HTML, RTF, WPF, XAML Tags:

I finally got fed up with Enum.Parse

July 24th, 2009
Comments Off

I don’t know why I didn’t do this long ago, but I am done writing this:

var val = (SomeEnum)Enum.Parse(typeof(SomeEnum),”someString”);

I have typed this too many times and it annoys me. 

I wrote a small extension method on the string type to make this better:

public static class StringExtensions
{
    public static T ToEnum<T>(this string @string)
    {
        return (T)Enum.Parse(typeof (T), @string);
    }
}

With this I can now write the previous line as:

var val = "someString".ToEnum<SomeEnum>()

It is a bit shorter and I think much more readable.
Author: MattManela Categories: C# Tags:

I finally got fed up with Enum.Parse

July 24th, 2009
Comments Off

I don’t know why I didn’t do this long ago, but I am done writing this:

var val = (SomeEnum)Enum.Parse(typeof(SomeEnum),”someString”);

I have typed this too many times and it annoys me. 

I wrote a small extension method on the string type to make this better:

public static class StringExtensions
{
    public static T ToEnum<T>(this string @string)
    {
        return (T)Enum.Parse(typeof (T), @string);
    }
}

With this I can now write the previous line as:

var val = "someString".ToEnum<SomeEnum>()

It is a bit shorter and I think much more readable.
Author: MattManela Categories: C#, Programming Tags:

My xUnit.net Visual Studio Code Snippets

March 2nd, 2009
Comments Off

The xUnit .Net codeplex page lists one useful Visual Studio code snippet for creating a Fact.  As you can tell I am fairly fond of code snippets so I created a few more which I use when writing xUnit.net facts. These are one line snippets that I find very convenient when writing my assertions.

Below is a table which shows the shortcut you use to access the snippet and the code the snippet generates

Shortcut Snippet
ae Assert.Equal($expected$,$actual$)
ane Assert.NotEqual($expected$,$actual$)
an Assert.Null($actual$)
ann Assert.NotNull($actual$)
at Assert.True($actual$)
af Assert.False($actual$)

I have included a zip containing these snippets.   Enjoy!

Author: MattManela Categories: C#, Snippets, Visual Studio Tags: