Posted by: Zeeshan Amjad | October 13, 2009

Creating Debugger Visualizer in WPF: Part 5


Till now we studied how to created a debugger visualizer to support multiple data types in WPF. But till now we were using the serializable data types. What will be the situation if we want to create a debugger visualizer for non serializable data type, such as Color, Brush etc. Today we are going to extend our visualizer to support non serializable data type i.e. Color.

The first thing we have to do is to add the attribute in our namespace for Color type. For simplicity we create another class name MyColorClass to handle color type. Here is the declaration of the namespace.

  1: [assembly: System.Diagnostics.DebuggerVisualizer(
  2: typeof(MyVisualizer.MyInt32Class), typeof(VisualizerObjectSource),
  3: Target = typeof(Int32),Description = "My Visualizer")]
  4: [assembly: System.Diagnostics.DebuggerVisualizer(
  5: typeof(MyVisualizer.MyStringClass), typeof(VisualizerObjectSource),
  6: Target = typeof(String), Description = "My Visualizer")]
  7: [assembly: System.Diagnostics.DebuggerVisualizer(
  8: typeof(MyVisualizer.MyColorClass), typeof(MyVisualizer.ColorSource),
  9: Target = typeof(Color), Description = "My Visualizer")]
 10: namespace MyVisualizer
 11: {
 12: }

Our visualizer support 3 data types, integer, string and color.

In addition there is one more difference. Here instead of using VisualizerObjectSource we are going to use ColorSource class which is in fact inherited by VisualizerObjectSource class.

  1: public class ColorSource : VisualizerObjectSource
  2: {
  3: }

For every non serializable class we have to do searlization ourselves and that’s exactly the reason to inherit class from visualizerObjectSource class. Here we override GetData function and perform serialization of the object.

  1: public class ColorSource : VisualizerObjectSource
  2: {
  3:     public override void GetData(object target, Stream outgoingData)
  4:     {
  5:         if (target != null && target is Color)
  6:         {
  7:             Color color = (Color)target;
  8:             MyColor obj = new MyColor(color);
  9: 
 10:             BinaryFormatter formatter = new BinaryFormatter();
 11:             formatter.Serialize(outgoingData, obj);
 12:         }
 13:     }
 14: }
 15: 

We have to include one namespace for BinaryFormatter.

  1: using System.Runtime.Serialization.Formatters.Binary;

But the color class is non serializable and whenever we are going to perform serlaiization on it we will get SerializationException. Its solution is to craete a wrapper class of that data type, implement ISerializable interface and define Serializable attribute to our class.

  1: [Serializable]
  2: public class MyColor : ISerializable
  3: {
  4: }

Here is a complete code of our wrapper class to perform serialization.

  1: [Serializable]
  2: public class MyColor : ISerializable
  3: {
  4:     private Color _color;
  5: 
  6:     public MyColor()
  7:     {
  8:     }
  9: 
 10:     public MyColor(Color color)
 11:     {
 12:         this.color = color;
 13:     }
 14: 
 15:     public Color color
 16:     {
 17:         get { return _color; }
 18:         set { _color = value; }
 19:     }
 20: 
 21:     protected MyColor(SerializationInfo info, StreamingContext context)
 22:     {
 23:         _color.A = info.GetByte("A");
 24:         _color.R = info.GetByte("R");
 25:         _color.G = info.GetByte("G");
 26:         _color.B = info.GetByte("B");
 27:     }
 28: 
 29:     [SecurityPermissionAttribute(SecurityAction.Demand,
 30:     SerializationFormatter = true)]
 31:     public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
 32:     {
 33:         info.AddValue("A", color.A);
 34:         info.AddValue("R", color.R);
 35:         info.AddValue("G", color.G);
 36:         info.AddValue("B", color.B);
 37:     }
 38: 
 39:     public override string ToString()
 40:     {
 41:         return String.Format("({0}, {1}, {2}, {3})",
 42:             color.A.ToString(),
 43:             color.R.ToString(),
 44:             color.G.ToString(),
 45:             color.B.ToString());
 46:     }
 47: }
 48: 

We also override ToString method to display the different component of color.

Once we are done with it then rest of the stuff is quite easy. We created different XAML file to display the name of color and display one rounded border in that color. Here is complete XAML file for this project.

  1: <Window 
  2:   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4:   Title="My Vsualizer" Height="400" Width="400" Background="Wheat" 
  5:   WindowStartupLocation="CenterScreen">
  6:     <StackPanel>
  7:         <ListBox Name="listBox" Margin="10" HorizontalContentAlignment="Stretch">
  8:             <ListBox.ItemTemplate>
  9:                 <DataTemplate>
 10:                     <Border Background="LightYellow" BorderBrush="Brown" BorderThickness="5">
 11:                         <StackPanel Margin="5">
 12:                             <TextBlock Foreground="Black" FontWeight="Bold" Text="{Binding Path=Type}"/>
 13:                             <TextBlock Foreground="Black" Text="{Binding Path=Value}"/>
 14:                         </StackPanel>
 15:                     </Border>
 16:                 </DataTemplate>
 17:             </ListBox.ItemTemplate>
 18:         </ListBox>
 19:         <Border Name="colorBorder" Margin="10" Height="250" BorderBrush="Black" BorderThickness="1" CornerRadius="10" />
 20:     </StackPanel>
 21: </Window>
 22: 

The other XAML file for other visualizers are same.

  1: <Window 
  2:   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4:   Title="My Vsualizer" Height="400" Width="400" Background="Wheat" 
  5:   WindowStartupLocation="CenterScreen">
  6:     <StackPanel>
  7:         <Border Margin="10" Background="AliceBlue" BorderBrush="Navy" BorderThickness="5" CornerRadius="5">
  8:             <StackPanel>
  9:                 <TextBlock Margin="5">Enter new Value</TextBlock>
 10:                 <TextBox Name="txtValue" Margin="5"/>
 11:             </StackPanel>    
 12:         </Border>
 13:         
 14:         <ListBox Name="listBox" Margin="10" HorizontalContentAlignment="Stretch">
 15:         <ListBox.ItemTemplate>
 16:             <DataTemplate>
 17:                 <Border Background="LightYellow" BorderBrush="Brown" BorderThickness="5">
 18:                     <StackPanel Margin="5">
 19:                         <TextBlock Foreground="Black" FontWeight="Bold" Text="{Binding Path=Type}"/>
 20:                         <TextBlock Foreground="Black" Text="{Binding Path=Value}"/>
 21:                     </StackPanel>
 22:                 </Border>
 23:             </DataTemplate>
 24:         </ListBox.ItemTemplate>
 25:     </ListBox>
 26:     <Button Name="btnOK" Margin="10" Width="75">OK</Button>
 27:     </StackPanel>
 28: </Window>
 29: 

Here is a complete code of the project

  1: using System;
  2: using System.Collections.Generic;
  3: using System.Linq;
  4: using System.Text;
  5: using Microsoft.VisualStudio.DebuggerVisualizers;
  6: using System.Windows;
  7: using System.Windows.Controls;
  8: using System.Windows.Markup;
  9: using System.IO;
 10: using System.Runtime.Serialization;
 11: using System.Runtime.Serialization.Formatters.Binary;
 12: using System.Windows.Media;
 13: using System.ComponentModel;
 14: using System.Diagnostics;
 15: using System.Security;
 16: using System.Security.Permissions;
 17: 
 18: 
 19: [assembly: System.Diagnostics.DebuggerVisualizer(
 20: typeof(MyVisualizer.MyInt32Class), typeof(VisualizerObjectSource),
 21: Target = typeof(Int32),Description = "My Visualizer")]
 22: [assembly: System.Diagnostics.DebuggerVisualizer(
 23: typeof(MyVisualizer.MyStringClass), typeof(VisualizerObjectSource),
 24: Target = typeof(String), Description = "My Visualizer")]
 25: [assembly: System.Diagnostics.DebuggerVisualizer(
 26: typeof(MyVisualizer.MyColorClass), typeof(MyVisualizer.ColorSource),
 27: Target = typeof(Color), Description = "My Visualizer")]
 28: namespace MyVisualizer
 29: {
 30:     [Serializable]
 31:     public class MyColor : ISerializable
 32:     {
 33:         private Color _color;
 34: 
 35:         public MyColor()
 36:         {
 37:         }
 38: 
 39:         public MyColor(Color color)
 40:         {
 41:             this.color = color;
 42:         }
 43: 
 44:         public Color color
 45:         {
 46:             get { return _color; }
 47:             set { _color = value; }
 48:         }
 49: 
 50:         protected MyColor(SerializationInfo info, StreamingContext context)
 51:         {
 52:             _color.A = info.GetByte("A");
 53:             _color.R = info.GetByte("R");
 54:             _color.G = info.GetByte("G");
 55:             _color.B = info.GetByte("B");
 56:         }
 57: 
 58:         [SecurityPermissionAttribute(SecurityAction.Demand,
 59:         SerializationFormatter = true)]
 60:         public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
 61:         {
 62:             info.AddValue("A", color.A);
 63:             info.AddValue("R", color.R);
 64:             info.AddValue("G", color.G);
 65:             info.AddValue("B", color.B);
 66:         }
 67: 
 68:         public override string ToString()
 69:         {
 70:             return String.Format("({0}, {1}, {2}, {3})",
 71:                 color.A.ToString(),
 72:                 color.R.ToString(),
 73:                 color.G.ToString(),
 74:                 color.B.ToString());
 75:         }
 76:     }
 77: 
 78:     public class ColorSource : VisualizerObjectSource
 79:     {
 80:         public override void GetData(object target, Stream outgoingData)
 81:         {
 82:             if (target != null && target is Color)
 83:             {
 84:                 Color color = (Color)target;
 85:                 MyColor obj = new MyColor(color);
 86: 
 87:                 BinaryFormatter formatter = new BinaryFormatter();
 88:                 formatter.Serialize(outgoingData, obj);
 89:             }
 90:         }
 91:     }
 92: 
 93:     public class MyColorClass : DialogDebuggerVisualizer
 94:     {
 95:         private Window win;
 96:         private IVisualizerObjectProvider objProvider;
 97: 
 98:         protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
 99:         {
100:             BinaryFormatter formatter = new BinaryFormatter();
101:             Stream stm = objectProvider.GetData();
102:             object obj = formatter.Deserialize(stm);
103:             MyColor objColor = (MyColor)obj;
104: 
105:             objProvider = objectProvider;
106: 
107:             FileStream fs = new FileStream("C:\\zamjad\\Projects\\MyVisualizer\\MyVisualizer\\ColorWindow.xaml", FileMode.Open, FileAccess.Read);
108: 
109:             win = (Window)XamlReader.Load(fs);
110: 
111:             fs.Close();
112: 
113:             ListBox listBox = win.FindName("listBox") as ListBox;
114: 
115:             List<TypeValue> listType = new List<TypeValue>();
116:             listType.Add(new TypeValue("Color", objColor.ToString()));
117:             listBox.ItemsSource = listType;
118: 
119:             SolidColorBrush brush = new SolidColorBrush(objColor.color);
120:             Border border = win.FindName("colorBorder") as Border;
121:             border.Background = brush;
122: 
123:             win.ShowDialog();
124:         }
125: 
126:         // This function is only for debugging purpose
127:         public static void TestShowVisualizer(object obj)
128:         {
129:             VisualizerDevelopmentHost host = new VisualizerDevelopmentHost(obj, typeof(MyColorClass),
130:                 typeof(ColorSource));
131:             host.ShowVisualizer();
132:         }
133:     }
134: 
135:     public class MyStringClass : DialogDebuggerVisualizer
136:     {
137:         private Window win;
138:         private String obj;
139:         private IVisualizerObjectProvider objProvider;
140: 
141:         protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
142:         {
143:             objProvider = objectProvider;
144:             obj = (String)objProvider.GetObject();
145: 
146:             System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
147:             List<TypeValue> listType = new List<TypeValue>();
148: 
149:             listType.Add(new TypeValue("String", obj));
150:             listType.Add(new TypeValue("Upper", obj.ToUpper()));
151:             listType.Add(new TypeValue("Lower", obj.ToLower()));
152:             listType.Add(new TypeValue("Length", obj.Length.ToString()));
153:             listType.Add(new TypeValue("Base 64 Encoding", Convert.ToBase64String(encoding.GetBytes(obj))));
154: 
155:             FileStream fs = new FileStream("C:\\zamjad\\Projects\\MyVisualizer\\MyVisualizer\\VisualizerWindow.xaml", FileMode.Open, FileAccess.Read);
156: 
157:             win = (Window)XamlReader.Load(fs);
158:             win.Height = 450;
159: 
160:             fs.Close();
161: 
162:             ListBox listBox = win.FindName("listBox") as ListBox;
163:             listBox.ItemsSource = listType;
164:             Button buttonOK = win.FindName("btnOK") as Button;
165: 
166:             buttonOK.Click += new RoutedEventHandler(buttonOK_Click);
167: 
168:             win.ShowDialog();
169: 
170:         }
171: 
172:         void buttonOK_Click(object sender, RoutedEventArgs e)
173:         {
174:             TextBox text = win.FindName("txtValue") as TextBox;
175: 
176:             String newValue = text.Text;
177: 
178:             if (objProvider.IsObjectReplaceable)
179:             {
180:                 objProvider.ReplaceObject(newValue);
181:             }
182: 
183:             win.Close();
184:         }
185: 
186:         // This function is only for debugging purpose
187:         public static void TestShowVisualizer(object obj)
188:         {
189:             VisualizerDevelopmentHost host = new VisualizerDevelopmentHost(obj, typeof(MyStringClass));
190:             host.ShowVisualizer();
191:         }
192:     }
193: 
194:     public class MyInt32Class : DialogDebuggerVisualizer
195:     {
196:         private Int32 obj;
197:         private Window win;
198:         private IVisualizerObjectProvider objProvider;
199: 
200:         protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
201:         {
202:             objProvider = objectProvider;
203:             obj = (Int32)objProvider.GetObject();
204: 
205:             List<TypeValue> listType = new List<TypeValue>();
206: 
207:             listType.Add(new TypeValue("Decimal", obj.ToString()));
208:             listType.Add(new TypeValue("Hex", obj.ToString("X")));
209:             listType.Add(new TypeValue("Octal", DecimalToBase(obj, 8)));
210:             listType.Add(new TypeValue("Binary", DecimalToBase(obj, 2)));
211: 
212:             FileStream fs = new FileStream("C:\\zamjad\\Projects\\MyVisualizer\\MyVisualizer\\VisualizerWindow.xaml", FileMode.Open, FileAccess.Read);
213: 
214:             win = (Window)XamlReader.Load(fs);
215: 
216:             fs.Close();
217: 
218:             ListBox listBox = win.FindName("listBox") as ListBox;
219:             listBox.ItemsSource = listType;
220: 
221:             Button buttonOK = win.FindName("btnOK") as Button;
222: 
223:             buttonOK.Click += new RoutedEventHandler(buttonOK_Click);
224: 
225:             win.ShowDialog();
226:         }
227: 
228:         void buttonOK_Click(object sender, RoutedEventArgs e)
229:         {
230:             TextBox text = win.FindName("txtValue") as TextBox;
231: 
232:             Int32 newValue = Convert.ToInt32(text.Text);
233: 
234:             if (objProvider.IsObjectReplaceable)
235:             {
236:                 objProvider.ReplaceObject(newValue);
237:             }
238: 
239:             win.Close();
240:         }
241: 
242:         // This function is only for debugging purpose
243:         public static void TestShowVisualizer(object obj)
244:         {
245:             VisualizerDevelopmentHost host = new VisualizerDevelopmentHost(obj, typeof(MyInt32Class));
246:             host.ShowVisualizer();
247:         }
248: 
249:         // Orignally written by Balamurali Balaji
250:         // Changed little bit to handle the negative sign
251:         // http://www.codeproject.com/KB/cs/balamurali_balaji.aspx
252:         private string DecimalToBase(int number, int basenumber)
253:         {
254:             string strRetVal = "";
255:             const int base10 = 10;
256:             char[] cHexa = new char[] { 'A', 'B', 'C', 'D', 'E', 'F' };
257:             int[] result = new int[32];
258:             int MaxBit = 32;
259:             bool isNegative = false;
260: 
261:             if (number < 0)
262:             {
263:                 isNegative = true;
264:                 number *= -1;
265:             }
266: 
267:             for (; number > 0; number /= basenumber)
268:             {
269:                 int rem = number % basenumber;
270:                 result[--MaxBit] = rem;
271:             }
272: 
273:             for (int i = 0; i < result.Length; i++)
274:             {
275:                 if ((int)result.GetValue(i) >= base10)
276:                 {
277:                     strRetVal += cHexa[(int)result.GetValue(i) % base10];
278:                 }
279:                 else
280:                 {
281:                     strRetVal += result.GetValue(i);
282:                 }
283:             }
284: 
285:             strRetVal = strRetVal.TrimStart(new char[] { '0' });
286: 
287:             if (isNegative)
288:             {
289:                 strRetVal = strRetVal.Insert(0, "-");
290:             }
291: 
292:             return strRetVal;
293:         }
294:     }
295: 
296:     public class TypeValue
297:     {
298:         public TypeValue()
299:         {
300:         }
301: 
302:         public TypeValue(String type, String value)
303:         {
304:             Type = type;
305:             Value = value;
306:         }
307: 
308:         public String Type
309:         { get; set; }
310: 
311:         public String Value
312:         { get; set; }
313:     }
314: }
315: 

Here is the output of the visualizer when see the value of any color type variable.

Visualizer_06

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: