ea15e249761a3df76312a4efdc041e1be1dfeca7
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Toolbox / ToolboxItemWrapper.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Toolbox
6 {
7     using System;
8     using System.Activities.Presentation.Utility;
9     using System.Activities.Presentation.View;
10     using System.Collections.Generic;
11     using System.ComponentModel;
12     using System.Diagnostics.CodeAnalysis;
13     using System.Drawing;
14     using System.Drawing.Design;
15     using System.Globalization;
16     using System.IO;
17     using System.Reflection;
18     using System.Runtime.InteropServices;
19     using System.Windows;
20     using System.Windows.Media;
21     using System.Windows.Media.Imaging;
22
23     // This class is a wrapper for ToolboxItem objects. It adds support
24     // for cate----zation of toolbox items. ResolveToolboxItem method
25     // establishes link between actual ToolboxItem instance and Tool representation
26     // in the toolbox (via qualified assembly and type name properties)
27
28     public sealed class ToolboxItemWrapper : INotifyPropertyChanged
29     {
30         string toolName;
31         string assemblyName;
32         string bitmapName;
33         string customDisplayName;
34         string defaultDisplayName;
35         Bitmap defaultBitmap;
36         ToolboxItem toolboxItem;
37         Type toolType;
38         IDataObject dataObject;
39         //LogPixelsXIndex and LogPixelsYIndex are the parameters you pass to GetDeviceCaps to get the current monitor resolution, they are constant.
40         private const int LogPixelsXIndex = 88;
41         private const int LogPixelsYIndex = 90;
42         private const int defaultDpi = 96;
43
44         public ToolboxItemWrapper()
45             : this(string.Empty, string.Empty, string.Empty, string.Empty)
46         {
47         }
48
49         public ToolboxItemWrapper(Type toolType)
50             : this(toolType, string.Empty)
51         {
52         }
53
54         public ToolboxItemWrapper(Type toolType, string displayName)
55             : this(toolType, string.Empty, displayName)
56         {
57         }
58
59         public ToolboxItemWrapper(Type toolType, string bitmapName, string displayName)
60             : this(toolType.FullName, toolType.Assembly.FullName, bitmapName, displayName)
61         {
62         }
63
64         public ToolboxItemWrapper(string toolName, string assemblyName, string bitmapName, string displayName)
65             : this(toolName, assemblyName, bitmapName, displayName, null)
66         {
67         }
68
69         internal ToolboxItemWrapper(string toolName, string assemblyName, string bitmapName, string displayName, IDataObject dataObject)
70         {
71             this.ToolName = toolName;
72             this.AssemblyName = assemblyName;
73             this.BitmapName = bitmapName;
74             this.DisplayName = displayName;
75             this.defaultDisplayName = string.Empty;
76             this.dataObject = dataObject;
77         }
78         public event PropertyChangedEventHandler PropertyChanged;
79
80         internal ToolboxItem ToolboxItem
81         {
82             get { return this.toolboxItem; }
83             private set
84             {
85                 if (this.toolboxItem != value)
86                 {
87                     this.toolboxItem = value;
88                     RaisePropertyChanged("IsValid");
89                     RaisePropertyChanged("ToolboxItem");
90                 }
91             }
92         }
93
94         public bool IsValid
95         {
96             get
97             {
98                 return (null != this.toolboxItem);
99             }
100         }
101
102         public string ToolName
103         {
104             get { return this.toolName; }
105             set
106             {
107                 if (null != this.toolboxItem)
108                 {
109                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToolboxItemFrozenDescription));
110                 }
111                 bool isChanged = !string.Equals(value, this.toolName);
112                 if (isChanged)
113                 {
114                     this.toolName = value;
115                     RaisePropertyChanged("ToolName");
116                     this.ToolboxItem = null;
117                 }
118             }
119         }
120
121         public string AssemblyName
122         {
123             get { return this.assemblyName; }
124             set
125             {
126                 if (null != this.toolboxItem)
127                 {
128                     throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ToolboxItemFrozenDescription));
129                 }
130                 bool isChanged = !string.Equals(value, this.assemblyName);
131                 if (isChanged)
132                 {
133                     this.assemblyName = value;
134                     RaisePropertyChanged("AssemblyName");
135                     this.ToolboxItem = null;
136                 }
137             }
138         }
139
140         public string BitmapName
141         {
142             get { return this.bitmapName; }
143             set
144             {
145                 bool isChanged = !string.Equals(value, this.bitmapName);
146                 if (isChanged)
147                 {
148                     this.bitmapName = value;
149                     RaisePropertyChanged("BitmapName");
150                     LoadBitmap();
151                 }
152             }
153         }
154
155         internal IDataObject DataObject
156         {
157             get
158             {
159                 return this.dataObject;
160             }
161
162             set
163             {
164                 this.dataObject = value;
165             }
166         }
167
168         public Bitmap Bitmap
169         {
170             get { return this.ToolboxItem.Bitmap; }
171         }
172
173         public string DisplayName
174         {
175             get
176             {
177                 return this.ToolboxItem != null ? this.ToolboxItem.DisplayName : this.customDisplayName;
178             }
179             set
180             {
181                 bool isChanged = !string.Equals(value, this.customDisplayName);
182                 if (isChanged)
183                 {
184                     this.customDisplayName = value;
185                     ChangeToolboxDisplayName();
186                     RaisePropertyChanged("DisplayName");
187                 }
188             }
189         }
190
191         [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "By design.")]
192         public Type Type
193         {
194             get { return this.toolType; }
195             private set
196             {
197                 bool isChanged = !Type.Equals(this.toolType, value);
198                 if (isChanged)
199                 {
200                     this.toolType = value;
201                     RaisePropertyChanged("Type");
202                 }
203             }
204         }
205
206         internal bool ResolveToolboxItem()
207         {
208             if (null != this.ToolboxItem)
209             {
210                 return true;
211             }
212             try
213             {
214                 if (null == this.AssemblyName || null == this.ToolName)
215                 {
216                     throw FxTrace.Exception.AsError(new ArgumentNullException(null == AssemblyName ? "AssemblyName" : "ToolName"));
217                 }
218
219                 Assembly toolAssembly = Assembly.Load(this.AssemblyName);
220                 Type discoveredToolType = toolAssembly.GetType(this.ToolName, true, true);
221                 ValidateTool(discoveredToolType);
222                 ToolboxItemAttribute[] toolboxItemAttributes
223                     = discoveredToolType.GetCustomAttributes(typeof(ToolboxItemAttribute), true) as ToolboxItemAttribute[];
224                 ToolboxItem instance = null;
225                 if (0 != toolboxItemAttributes.Length)
226                 {
227                     instance =
228                         Activator.CreateInstance(toolboxItemAttributes[0].ToolboxItemType) as ToolboxItem;
229                 }
230                 else
231                 {
232                     instance = new ToolboxItem(discoveredToolType);
233                 }
234                 this.ToolboxItem = instance;
235                 this.defaultDisplayName = instance.DisplayName;
236                 this.defaultBitmap = instance.Bitmap;
237                 LoadBitmap();
238                 ChangeToolboxDisplayName();
239                 this.Type = discoveredToolType;
240                 return true;
241             }
242             catch
243             {
244                 this.ToolboxItem = null;
245                 this.Type = null;
246                 throw;
247             }
248         }
249
250         internal static Bitmap CreateBitmapFromDrawingBrush(DrawingBrush resource)
251         {
252             if (resource == null)
253             {
254                 return null;
255             }
256
257             const int defaultPixelWidth = 16;
258             const int defaultPixelHeight = 16;
259             double dpiX = GetSystemDpi(LogPixelsXIndex);
260             double dpiY = GetSystemDpi(LogPixelsYIndex);
261             int pixelWidth = (int)(defaultPixelWidth * dpiX / defaultDpi);
262             int pixelHeight = (int)(defaultPixelHeight * dpiY / defaultDpi);
263             var renderTargetBitmap = new RenderTargetBitmap(pixelWidth, pixelHeight, dpiX, dpiY, PixelFormats.Pbgra32);
264             var drawingVisual = new DrawingVisual();
265             using (var context = drawingVisual.RenderOpen())
266             {
267                 context.DrawRectangle(((DrawingBrush)resource), null, new Rect(0, 0, defaultPixelWidth, defaultPixelHeight));
268             }
269
270             renderTargetBitmap.Render(drawingVisual);
271             MemoryStream bitmapStream = new MemoryStream();
272             BitmapEncoder bitmapEncode = new PngBitmapEncoder
273             {
274                 Interlace = PngInterlaceOption.Off,
275             };
276             bitmapEncode.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
277             bitmapEncode.Save(bitmapStream);
278             //Reposition the MemoryStream pointer to the start of the stream after the save
279             bitmapStream.Position = 0;
280             Bitmap bitmap = new Bitmap(bitmapStream);
281             return bitmap;
282         }
283
284         internal static Bitmap GetBitmapFromResource(string resourceString)
285         {
286             DrawingBrush drawingBrush = IconHelper.GetBrushFromResource(resourceString);
287             return CreateBitmapFromDrawingBrush(drawingBrush);
288         }
289
290         void LoadBitmap()
291         {
292             try
293             {
294                 if (this.toolboxItem != null)
295                 {
296                     if (!string.IsNullOrEmpty(this.BitmapName))
297                     {
298                         this.toolboxItem.Bitmap = new Bitmap(this.BitmapName);
299                     }
300                     else
301                     {
302                         Bitmap bitmap = GetBitmapFromResource(this.toolboxItem.TypeName);
303                         if (bitmap != null)
304                         {
305                             this.toolboxItem.Bitmap = bitmap;
306                         }
307                         else
308                         {
309                             if (WorkflowDesignerIcons.IsDefaultCutomActivitySetByUser)
310                             {
311                                 this.toolboxItem.Bitmap = CreateBitmapFromDrawingBrush(WorkflowDesignerIcons.Activities.DefaultCustomActivity);
312                             }
313                             else if (WorkflowDesignerIcons.Activities.ToolboxDefaultCustomActivity != null)
314                             {
315                                 this.toolboxItem.Bitmap = CreateBitmapFromDrawingBrush(WorkflowDesignerIcons.Activities.ToolboxDefaultCustomActivity);
316                             }
317                             else
318                             {
319                                 this.toolboxItem.Bitmap = this.defaultBitmap;
320                             }
321                         }
322                     }
323                 }
324             }
325             catch (ArgumentException)
326             {
327                 this.toolboxItem.Bitmap = this.defaultBitmap;
328             }
329
330             RaisePropertyChanged("ToolboxItem");
331             RaisePropertyChanged("Bitmap");
332         }
333
334         void ChangeToolboxDisplayName()
335         {
336             if (null != this.toolboxItem)
337             {
338                 if (!string.IsNullOrEmpty(this.customDisplayName))
339                 {
340                     this.toolboxItem.DisplayName = this.customDisplayName;
341                 }
342                 else
343                 {
344                     this.toolboxItem.DisplayName = this.defaultDisplayName;
345                 }
346                 RaisePropertyChanged("ToolboxItem");
347             }
348         }
349
350         void RaisePropertyChanged(string propertyName)
351         {
352             if (null != PropertyChanged)
353             {
354                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
355             }
356         }
357
358         void ValidateTool(Type toolType)
359         {
360             bool isInvalid = toolType.IsAbstract;
361             isInvalid |= toolType.IsInterface;
362             isInvalid |= !toolType.IsVisible;
363             ConstructorInfo ctor = toolType.GetConstructor(Type.EmptyTypes);
364             isInvalid |= (null == ctor);
365             if (isInvalid)
366             {
367                 string reason = string.Empty;
368                 if (toolType.IsAbstract)
369                 {
370                     reason = "IsAbstract == true ";
371                 }
372                 if (toolType.IsInterface)
373                 {
374                     reason += "IsInterface == true ";
375                 }
376                 if (!toolType.IsVisible)
377                 {
378                     reason += "IsVisible == false ";
379                 }
380                 if (null == ctor)
381                 {
382                     reason += SR.NoDefaultCtorError;
383                 }
384
385                 string error = string.Format(CultureInfo.CurrentCulture, SR.NotSupportedToolboxTypeFormatString, toolType.Name, reason);
386                 throw FxTrace.Exception.AsError(new NotSupportedException(error));
387             }
388         }
389
390         public override string ToString()
391         {
392             return this.ToolName;
393         }
394
395         [DllImport("User32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
396         private static extern IntPtr GetDC(IntPtr hWnd);
397
398         [DllImport("User32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
399         private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
400
401         [DllImport("Gdi32.dll", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
402         private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
403
404         internal static int GetSystemDpi(int index)
405         {
406             IntPtr dc = GetDC(IntPtr.Zero);
407             //if dc is null, just return the common Dpi value
408             if (dc == null)
409             {
410                 return defaultDpi;
411             }
412
413             try
414             {
415                 return GetDeviceCaps(dc, index);
416             }
417             finally
418             {
419                 ReleaseDC(IntPtr.Zero, dc);
420             }
421         }
422     }
423 }