by Jim McCurdy
23. July 2010 09:51
Update: As of 8/11/2010, I have released the following update of the source code for my CloneObject class. This update adds the ability to clone any attached properties found in the loaded assemblies, and also removes an unnecessary dependency property loop which will speed up some operations…
Update: As of 7/19/2010, I have updated the source code for my CloneObject class. The update improves on the ability to clone arrays and other IEnumerable objects, and simplifies some other operations…
The hardest part of developing this CloneObject class, was testing all of the potential use cases. The solution I offer here works well for the common use cases that I was able to test, but I am sure that others may find ways to make it more robust. Readers are encouraged to comment with modifications. Thanks to Justin Angel and Tamir Khason for their inspirational articles. My solution took a slightly different direction over time, but they were the ones that got me started.
The code below has been tested and used with Silverlight 3 and Silverlight 4.
//#define DEBUG_TRACE
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Resources;
namespace ClassLibrary
{
//[Obfuscation(Exclude = true)]
internal static class CloneObject
{
private static List<FieldInfo> _attachedProperties = null;
// Extension for any class object
internal static TT DeepClone<TT>(this TT source, bool? cloneAttachedProperties = null)
{ // Jim McCurdy's DeepClone
if (cloneAttachedProperties == null)
cloneAttachedProperties = (source is DependencyObject);
// The first time this method is called, compute a list of all
// attached properties that exist in this XAP's assemblies
if (cloneAttachedProperties == true && _attachedProperties == null)
{
_attachedProperties = new List<FieldInfo>();
List<Assembly> assemblies = GetLoadedAssemblies();
foreach (Assembly assembly in assemblies)
GetAttachedProperties(_attachedProperties, assembly);
}
TT clone = CloneRecursive(source);
if (clone is FrameworkElement)
{
FrameworkElement cloneElement = (clone as FrameworkElement);
cloneElement.Arrange(new Rect(0, 0, cloneElement.ActualWidth, cloneElement.ActualHeight));
}
return clone;
}
private static TT CloneRecursive<TT>(TT source)
{
if (source == null || source.GetType().IsValueType)
return source;
// Common types that do not have parameterless constructors
if (source is string || source is Type || source is Uri || source is DependencyProperty)
return source;
TT clone = CloneCreate(source);
if (clone == null)
return source;
if (source is IList)
CloneList(source as IList, clone as IList);
CloneProperties(source, clone);
return clone;
}
private static TT CloneCreate<TT>(TT source)
{
try
{
#if DEBUG_TRACE
string.Format("Clone create object Type={0}", SimpleType(source.GetType())).Trace();
#endif
Array sourceArray = (source as Array);
if (sourceArray == null)
return (TT)Activator.CreateInstance(source.GetType());
if (sourceArray.Rank == 1)
return (TT)(object)Array.CreateInstance(source.GetType().GetElementType(),
sourceArray.GetLength(0));
if (sourceArray.Rank == 2)
return (TT)(object)Array.CreateInstance(source.GetType().GetElementType(),
sourceArray.GetLength(0), sourceArray.GetLength(1));
}
catch (Exception ex)
{
if (ex.Message.Contains("No parameterless constructor"))
return default(TT);
string.Format("Can't create object Type={0}", SimpleType(source.GetType())).Trace();
ex.DebugOutput();
}
return default(TT);
}
private static void CloneProperties(object source, object clone)
{
// The binding flags indicate what properties we will clone
// Unfortunately, we cannot clone "internal" or "protected" properties
BindingFlags flags =
BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public;
if (source is DependencyObject)
{
DependencyObject sourcedp = source as DependencyObject;
DependencyObject clonedp = clone as DependencyObject;
// Clone attached properties
if (_attachedProperties != null && _attachedProperties.Count > 0)
foreach (FieldInfo field in _attachedProperties)
CloneDependencyProperty(sourcedp, clonedp, field, true);
// Clone dependency properties
FieldInfo[] fields = source.GetType().GetFields(flags | BindingFlags.Static);
foreach (FieldInfo field in fields)
CloneDependencyProperty(sourcedp, clonedp, field, false);
}
// Clone CLR properties
PropertyInfo[] properties = source.GetType().GetProperties(flags);
foreach (PropertyInfo property in properties)
CloneProperty(source, clone, property);
}
private static void CloneDependencyProperty(DependencyObject sourceObject,
DependencyObject cloneObject, FieldInfo field, bool isAttached)
{
try
{
// Blacklisted properties that can't (or shouldn't) be set
if (field.Name == "NameProperty" && sourceObject is FrameworkElement) return;
DependencyProperty dp = field.GetValue(sourceObject) as DependencyProperty;
if (dp == null) // Event DependencyProperties will be null
return;
object sourceValue = null;
try
{
sourceValue = sourceObject.GetValue(dp);
}
catch (Exception)
{
}
if (sourceValue == null)
return;
// Don't set attached properties if we don't have to
if (isAttached)
{
Type sourceType = sourceValue.GetType();
if (sourceType.IsValueType && sourceValue.Equals(Activator.CreateInstance(sourceType)))
return;
}
#if DEBUG_TRACE
string.Format("Clone dependency property Name={0}, Value={2} for source Type={1}",
field.Name, SimpleType(sourceObject.GetType()), sourceValue).Trace();
#endif
// Blacklisted properties that can't (or don't need to be) cloned
bool doClone = true;
if (field.Name == "DataContextProperty") doClone = false;
//if (field.Name == "TargetPropertyProperty") doClone = false;
object cloneValue = (doClone ? CloneRecursive(sourceValue) : sourceValue);
cloneObject.SetValue(dp, cloneValue);
}
catch (Exception ex)
{
if (ex.Message.Contains("read-only"))
return;
if (ex.Message.Contains("read only"))
return;
if (ex.Message.Contains("does not fall within the expected range"))
return;
string.Format("Can't clone dependency property Name={0}, for source Type={1}",
field.Name, SimpleType(sourceObject.GetType())).Trace();
ex.DebugOutput();
}
}
private static void CloneProperty(object source, object clone, PropertyInfo property)
{
try
{
if (!property.CanRead || !property.CanWrite || property.GetIndexParameters().Length != 0)
return;
// Blacklisted properties that can't (or shouldn't) be set
if (property.Name == "Name" && source is FrameworkElement) return;
if (property.Name == "InputScope" && source is TextBox) return; // Can't get
if (property.Name == "Watermark" && source is TextBox) return; // Can't get
if (property.Name == "Source" && source is ResourceDictionary) return; // Can't set
if (property.Name == "TargetType" && source is ControlTemplate) return; // Can't set
bool publicSetter = (source.GetType().GetMethod("set_" + property.Name) != null);
bool isList = (property.PropertyType.GetInterface("IList", true) != null);
if (!publicSetter && !isList)
return;
object sourceValue = property.GetValue(source, null);
if (sourceValue == null)
return;
if (!publicSetter && isList)
{
IList cloneList = property.GetValue(clone, null) as IList;
if (cloneList != null)
CloneList(sourceValue as IList, cloneList);
return;
}
#if DEBUG_TRACE
string.Format("Clone property Type={0}, Name={1}, Value={3} for source Type={2}",
SimpleType(property.PropertyType), property.Name, SimpleType(source.GetType()),
sourceValue).Trace();
#endif
// Blacklisted properties that can't (or don't need to be) cloned
bool doClone = true;
if (source is FrameworkElement && property.Name == "DataContext") doClone = false;
//if (property.Name == "TargetProperty") doClone = false;
object cloneValue = (doClone ? CloneRecursive(sourceValue) : sourceValue);
property.SetValue(clone, cloneValue, null); // possible MethodAccessException
}
catch (Exception ex)
{
string.Format("Can't clone property Type={0}, Name={1}, for source Type={2}",
SimpleType(property.PropertyType), property.Name, SimpleType(source.GetType())).Trace();
ex.DebugOutput();
}
}
private static void CloneList(IList sourceList, IList cloneList)
{
try
{
IEnumerator sourceEnumerator = sourceList.GetEnumerator();
Array sourceArray = sourceList as Array;
Array cloneArray = cloneList as Array;
int dim0 = (sourceArray != null && sourceArray.Rank > 0 ? sourceArray.GetLowerBound(0) : 0);
int dim1 = (sourceArray != null && sourceArray.Rank > 1 ? sourceArray.GetLowerBound(1) : 0);
while (sourceEnumerator.MoveNext())
{
object sourceValue = sourceEnumerator.Current;
#if DEBUG_TRACE
string.Format("Clone IList item {0}", sourceValue).Trace();
#endif
object cloneValue = CloneRecursive(sourceValue);
if (sourceArray == null)
cloneList.Add(cloneValue);
else
if (sourceArray.Rank == 1)
cloneArray.SetValue(cloneValue, dim0++);
else
if (sourceArray.Rank == 2)
{
cloneArray.SetValue(cloneValue, dim0, dim1);
if (++dim1 > sourceArray.GetUpperBound(1))
{
dim1 = sourceArray.GetLowerBound(1);
if (++dim0 > sourceArray.GetUpperBound(0))
dim0 = sourceArray.GetLowerBound(0);
}
}
}
}
catch (Exception ex)
{
string.Format("Can't clone IList item Type={0}", SimpleType(sourceList.GetType())).Trace();
ex.DebugOutput();
}
}
private static string SimpleType(Type type)
{
string typeName = type.ToString();
int index = typeName.LastIndexOf('[');
if (index < 0)
return typeName.Substring(typeName.LastIndexOf('.') + 1);
string collectionName = typeName.Substring(index);
collectionName = collectionName.Substring(collectionName.LastIndexOf('.') + 1);
typeName = typeName.Substring(0, index);
typeName = typeName.Substring(typeName.LastIndexOf('.') + 1);
return typeName + '[' + collectionName;
}
private static List<Assembly> GetLoadedAssemblies()
{
List<Assembly> assemblies = new List<Assembly>();
foreach (AssemblyPart part in Deployment.Current.Parts)
{
StreamResourceInfo sri =
Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
if (sri == null)
continue;
Assembly assembly = new AssemblyPart().Load(sri.Stream);
if (assembly != null && !assemblies.Contains(assembly))
assemblies.Add(assembly);
}
// Additional assemblies that are not found when examining of Deployment.Current.Parts above
Type[] types =
{
typeof(System.Windows.Application), // System.Windows.dll,
#if INCLUDE_ASSEMBLIES_WITHOUT_ATTACHED_PROPERTIES
typeof(System.Action), // mscorlib.dll,
typeof(System.Uri), // System.dll,
typeof(System.Lazy<int>), // System.Core.dll,
typeof(System.Net.Cookie), // System.Net.dll,
typeof(System.Runtime.Serialization.StreamingContext), // System.Runtime.Serialization.dll,
typeof(System.ServiceModel.XmlSerializerFormatAttribute), // System.ServiceModel.dll,
typeof(System.Windows.Browser.BrowserInformation), // System.Windows.Browser.dll,
typeof(System.Xml.ConformanceLevel), // System.Xml.dll,
#endif
};
foreach (Type type in types)
{
Assembly assembly = type.Assembly;
if (assembly != null && !assemblies.Contains(assembly))
assemblies.Add(assembly);
}
return assemblies;
}
private static bool GetAttachedProperties(List<FieldInfo> attachedProperties, Assembly assembly)
{
BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static;
foreach (Type type in assembly.GetTypes())
{
FieldInfo[] fields = type.GetFields(flags);
MethodInfo[] methods = null;
foreach (FieldInfo field in fields)
{
if (!field.FieldType.Is(typeof(DependencyProperty)))
continue;
if (!field.Name.EndsWith("Property"))
continue;
string fieldName = field.Name.Replace("Property", "");
string getName = "Get" + fieldName;
string setName = "Set" + fieldName;
bool foundGet = false;
bool foundSet = false;
if (methods == null)
methods = type.GetMethods(flags);
foreach (MethodInfo method in methods)
{
if (method.Name == getName && method.GetParameters().Length == 1 &&
method.GetParameters()[0].ParameterType.Is(typeof(DependencyObject)))
foundGet = true;
else
if (method.Name == setName && method.GetParameters().Length == 2 &&
method.GetParameters()[0].ParameterType.Is(typeof(DependencyObject)))
foundSet = true;
if (foundGet && foundSet)
break;
}
if (!(foundGet && foundSet))
continue;
try
{
DependencyProperty dp = field.GetValue(null) as DependencyProperty;
}
catch (Exception)
{
continue;
}
// Found an attached Dependency Property
attachedProperties.Add(field);
}
}
return true;
}
}
internal static class Extensions
{
// Extension for Type
internal static bool Is(this Type type, Type baseType)
{
return (type.Equals(baseType) || type.IsSubclassOf(baseType));
}
// Extension for Exception
internal static void DebugOutput(this Exception ex)
{
#if DEBUG
Debug.WriteLine(ex.GetType().ToString() + ": " + ex.Message + Environment.NewLine);
#endif
}
// Extension for string
internal static void Trace(this string message)
{
Debug.WriteLine(message);
}
}
}
by Jim McCurdy
29. January 2010 10:43
In the upcoming Silverlight 4 release, mousewheel support for controls will be implemented out of the box for the ScrollViewer, DataGrid, and ListBox controls. However Silverlight 3 still requires you to roll your own mouse wheel support. And Silverlight 4 does not offer a scrolling solution for Slider and TreeView.
The following MouseWheelProps class provides generic mousewheel support for all Silverlight controls that support the IScrollProvider interface (ScrollViewer, ListBox, DataGrid) or the IRangeValueProvider interface (Slider). This class implements a single attached property called Enable that can be added directly to a control, or as a style for a control.
Step1: Add the following MouseWheelProps class to your Silverlight project:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Automation.Provider;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace ClassLibrary
{
public static class MouseWheelProps
{
// The Enabled attached property
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(MouseWheelProps),
new PropertyMetadata(false, OnEnabledPropertyChanged));
public static bool GetEnabled(FrameworkElement element)
{
return (bool)element.GetValue(EnabledProperty);
}
public static void SetEnabled(FrameworkElement element, bool value)
{
element.SetValue(EnabledProperty, value);
}
private static void OnEnabledPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
if (element == null)
return;
bool enabled = (bool)e.NewValue;
if (enabled)
element.MouseWheel += OnMouseWheel;
else
element.MouseWheel -= OnMouseWheel;
}
private static void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Handled)
return;
UIElement element = sender as UIElement;
if (element == null)
return;
AutomationPeer peer = FrameworkElementAutomationPeer.FromElement(element);
if (peer == null)
peer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
if (peer == null)
return;
// try to get the scroll provider or the range provider
IScrollProvider m_ScrollProvider = peer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
IRangeValueProvider m_RangeValueProvider = null;
if ((element is Control) && (element as Control).HasFocus())
m_RangeValueProvider = peer.GetPattern(PatternInterface.RangeValue) as IRangeValueProvider;
if (m_ScrollProvider == null && m_RangeValueProvider == null)
return;
// set scroll amount
const double kMultiplier = 5.0; // x times the default
const double kFactor = kMultiplier / (30 * 120);
double delta = e.Delta;
delta *= kFactor;
int direction = Math.Sign(delta);
bool shiftKey = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
bool controlKey = (Keyboard.Modifiers & ModifierKeys.Control) != 0;
if (m_ScrollProvider != null)
{
e.Handled = true;
#if true
if (m_ScrollProvider.VerticallyScrollable && !controlKey)
{
double percent = m_ScrollProvider.VerticalScrollPercent - (delta * m_ScrollProvider.VerticalViewSize);
if (percent < 0) percent = 0;
if (percent > 100) percent = 100;
m_ScrollProvider.SetScrollPercent(m_ScrollProvider.HorizontalScrollPercent, percent);
}
else
if (m_ScrollProvider.HorizontallyScrollable && controlKey)
{
double percent = m_ScrollProvider.HorizontalScrollPercent - (delta * m_ScrollProvider.HorizontalViewSize);
if (percent < 0) percent = 0;
if (percent > 100) percent = 100;
m_ScrollProvider.SetScrollPercent(percent, m_ScrollProvider.VerticalScrollPercent);
}
#else
ScrollAmount scrollAmount = (direction < 0) ? ScrollAmount.SmallIncrement : ScrollAmount.SmallDecrement;
if (m_ScrollProvider.VerticallyScrollable && !controlKey)
m_ScrollProvider.Scroll(ScrollAmount.NoAmount, scrollAmount);
else
if (m_ScrollProvider.HorizontallyScrollable && controlKey)
m_ScrollProvider.Scroll(scrollAmount, ScrollAmount.NoAmount);
#endif
}
if (m_RangeValueProvider != null)
{
e.Handled = true;
double newValue = m_RangeValueProvider.Value + (direction < 0 ? -m_RangeValueProvider.LargeChange : m_RangeValueProvider.LargeChange);
if (newValue >= m_RangeValueProvider.Minimum && newValue <= m_RangeValueProvider.Maximum)
m_RangeValueProvider.SetValue(newValue);
}
}
}
internal static class ExtensionMethods
{
// Extension for UIElement
internal static bool HasFocus(this UIElement element)
{
if (element == null)
return false;
DependencyObject focusedElement = FocusManager.GetFocusedElement() as DependencyObject;
while (focusedElement != null)
{
if (element == focusedElement)
return true;
focusedElement = VisualTreeHelper.GetParent(focusedElement);
}
return false;
}
}
}
Step 2: Add the attached property c:MouseWheelProps.Enabled="True" either directly to a control, or as a Style that can be referenced by a control.
Added directly to a control:
<UserControl x:Class="SilverlightApplication.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ClassLibrary"
xmlns:DataGrid="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
>
<Grid>
<DataGrid:DataGrid c:MouseWheelProps.Enabled="True" />
</Grid>
</UserContro>
Added as a Style:
<UserControl x:Class="SilverlightApplication.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ClassLibrary"
xmlns:DataGrid="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
>
<Grid>
<Grid.Resources>
<Style x:Key="CommonDataGrid" TargetType="DataGrid:DataGrid">
<Setter Property="c:MouseWheelProps.Enabled" Value="True" />
</Style>
</Grid.Resources>
<DataGrid:DataGrid Style="{StaticResource CommonDataGrid}" />
</Grid>
</UserContro>
That's all there is to it!
by Jim McCurdy
19. November 2009 22:53
Silverlight application developers have several choices when deploying file resources for a Silverlight application. For my own Silverlight application, YinYangMoney, I developed a ResourceFile class to make access to those resource files flexible and simple - regardless of the deployment choice. I will be sharing that class with you in this blog post.
Background on Silverlight File Resources
As I talk about Silverlight File Resources here, do not confuse them with Silverlight StaticResources defined and used in your XAML files:
<Application.Resources> <System:String x:Key="Greeting">Hello World!</System:String> </Application.Resources>
and can be accessed in XAML as:
<TextBlock Text="{StaticResource Greeting}" />
or in C# code as:
string greeting = Application.Current.Resources["Greeting"];
Silverlight File Resources are different. They are typically non-executable data files used by your Silverlight application. This includes resources such as text, XAML, XML, HTML, font, image (JPG or PNG), audio, or video files. You can organize these resource files in folders, sub-folders, or even package them in ZIP files. My ResourceFile class will get you to these resources regardless of the file type, size, or folder organization.
Get Started
Start by using Visual Studio to add the resource files to your Silverlight application or library projects. Organize them in any project folders you want, but I would typically root them under a common Content or Resource folder. Secondly, configure the files for deployment by setting the Build Action property in the files' Visual Studio Properties Window. Set the Build Action as appropriate:
Content. This build action will include the file in the application XAP package.
Resource. This build action will embed the file in the project assembly. This applies to project assemblies that you deploy either inside or outside the application XAP package.
None. Use this build action for resource files that you want to retrieve on demand. You will typically deploy on-demand files at the same server location as your application package. I am not aware of any case where using this build action is practical.
The Build Action drop-down list also offers several other settings. However, as of this writing, you can only use Resource, Content, and None with Silverlight projects. For example, do not use the Embedded Resource build action, which uses a format that Silverlight cannot recognize.
Accessing Silverlight File Resources
Accessing Silverlight file resources that are deployed with different Build Actions (and in or out of ZIP files), normally require different access methods. However, my ResourceFile class makes these differences transparent to the application, so you can change any file's Build Action without breaking your code's access to its resources. It also makes accessing file resources in a library assembly very easy.
ResourceFile is a static class with public methods to read entire file resources, or get file streams to read them yourself. I also added a little helper class which I called From. The From class is used to indicate the assembly that contains resource files. The code below has been tested and used with Silverlight 3 and Silverlight 4. You can also find my simple test project here: ResourceFileTest.zip
using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using System.Xml;
namespace ClassLibrary
{
// Get resource files, or resource file streams, either from...
// o an assembly in the XAP (Resource build action)
// o the XAP itself (Content build action)
//
// Typical usage:
//
// Get content directly: bitmaps, file text, fonts, XAML, or XML
//
// BitmapImage bitmap1 = ResourceFile.GetBitmap("Images/Clouds.jpg", From.App);
//
// Get content from a ZIP:
//
// BitmapImage bitmap2 = ResourceFile.GetBitmapFromZip("Content/Zips/Files.zip", "Clouds.jpg", From.App);
//
// Get content streams:
//
// Stream bitmapStream = ResourceFile.GetStream("Images/Clouds.jpg", From.App);
// Stream zipStream = ResourceFile.GetStream("Content/Zips/Files.zip", From.App);
internal static class ResourceFile
{
// Get a bitmap from an assembly or the XAP
internal static BitmapImage GetBitmap(string relativeUri, From assembly)
{
Stream stream = GetStream(relativeUri, assembly);
return GetBitmapFromStream(stream);
}
// Get a bitmap from a zip file in an assembly or the XAP
internal static BitmapImage GetBitmapFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream stream = GetStreamFromZip(relativeZipUri, relativeUri, assembly);
return GetBitmapFromStream(stream);
}
// Get a bitmap from a stream
internal static BitmapImage GetBitmapFromStream(Stream stream)
{
if (stream == null)
return null;
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(stream);
return bitmap;
}
// Get file text from an assembly or the XAP
internal static string GetFileText(string relativeUri, From assembly)
{
Stream stream = GetStream(relativeUri, assembly);
return GetFileTextFromStream(stream);
}
// Get file text from a zip file in an assembly or the XAP
internal static string GetFileTextFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream stream = GetStreamFromZip(relativeZipUri, relativeUri, assembly);
return GetFileTextFromStream(stream);
}
// Get file text from a stream
internal static string GetFileTextFromStream(Stream stream)
{
if (stream == null)
return null;
using (StreamReader reader = new StreamReader(stream))
{
if (reader == null)
return null;
return reader.ReadToEnd();
}
}
// Get a font from an assembly or the XAP
internal static FontSource GetFont(string relativeUri, From assembly)
{
Stream stream = GetStream(relativeUri, assembly);
return GetFontFromStream(stream);
}
// Get a font from a zip file in an assembly or the XAP
internal static FontSource GetFontFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream stream = GetStreamFromZip(relativeZipUri, relativeUri, assembly);
return GetFontFromStream(stream);
}
// Get a font from a stream
internal static FontSource GetFontFromStream(Stream stream)
{
if (stream == null)
return null;
return new FontSource(stream);
}
// Get XAML from an assembly or the XAP
internal static UIElement GetXaml(string relativeUri, From assembly)
{
Stream stream = GetStream(relativeUri, assembly);
return GetXamlFromStream(stream);
}
// Get XAML from a zip file in an assembly or the XAP
internal static UIElement GetXamlFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream stream = GetStreamFromZip(relativeZipUri, relativeUri, assembly);
return GetXamlFromStream(stream);
}
// Get XAML from a stream
internal static UIElement GetXamlFromStream(Stream stream)
{
if (stream == null)
return null;
return XamlReader.Load(GetFileTextFromStream(stream)) as UIElement;
}
// Get an XML Reader from an assembly or the XAP
internal static XmlReader GetXmlReader(string relativeUri, From assembly)
{
Stream stream = GetStream(relativeUri, assembly);
return GetXmlReaderFromStream(stream);
}
// Get an XML Reader from a zip file in an assembly or the XAP
internal static XmlReader GetXmlReaderFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream stream = GetStreamFromZip(relativeZipUri, relativeUri, assembly);
return GetXmlReaderFromStream(stream);
}
// Get an XML Reader from a stream
internal static XmlReader GetXmlReaderFromStream(Stream stream)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Ignore;
settings.IgnoreWhitespace = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
try
{
return XmlReader.Create(stream, settings);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Alert", MessageBoxButton.OK);
return null;
}
}
// Get an XML text string from an XmlReader
internal static string GetXmlFromXmlReader(XmlReader xmlReader)
{
return (xmlReader != null ? xmlReader.ReadOuterXml() : null);
}
// Get a resource file stream info from an assembly or the XAP
internal static StreamResourceInfo GetStreamInfo(string relativeUri, From assembly)
{
StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri(relativeUri, UriKind.Relative));
if (streamInfo != null) // found in the XAP
return streamInfo;
string componentFormat = assembly.Name + ";component/{0}";
string assemblyUri = string.Format(componentFormat, relativeUri);
streamInfo = Application.GetResourceStream(new Uri(assemblyUri, UriKind.Relative));
if (streamInfo != null) // found in the assembly
return streamInfo;
// Last chance!
// Resource files added to a VS project as "links" (Add / Existing Item / Add As Link)
// must be accessed without a path
int pathMarker = relativeUri.LastIndexOf('/');
if (pathMarker < 0)
return null;
assemblyUri = string.Format(componentFormat, relativeUri.Substring(pathMarker + 1));
streamInfo = Application.GetResourceStream(new Uri(assemblyUri, UriKind.Relative));
if (streamInfo != null) // found in the assembly
return streamInfo;
return null; // not found
}
// Get a resource file stream from an assembly or the XAP
internal static Stream GetStream(string relativeUri, From assembly)
{
StreamResourceInfo streamInfo = GetStreamInfo(relativeUri, assembly);
if (streamInfo == null)
return null;
return streamInfo.Stream;
}
// Get a resource file stream from inside a ZIP stream
internal static Stream GetStreamFromZip(string relativeZipUri, string relativeUri, From assembly)
{
Stream zipStream = GetStream(relativeZipUri, assembly);
if (zipStream == null)
return null;
return GetStreamFromZipStream(zipStream, relativeUri);
}
// Get a resource file stream from inside a ZIP stream
internal static Stream GetStreamFromZipStream(Stream zipStream, string relativeUri)
{
if (zipStream == null)
return null;
StreamResourceInfo zipInfo = new StreamResourceInfo(zipStream, null);
StreamResourceInfo streamInfo = Application.GetResourceStream(zipInfo, new Uri(relativeUri, UriKind.Relative));
if (streamInfo == null)
streamInfo = Application.GetResourceStream(new Uri(relativeUri, UriKind.Relative));
if (streamInfo == null)
return null;
return streamInfo.Stream;
}
}
// Calls to access file resources are assisted by a tiny static class called "From"
// which is used to indicate the assembly that contains resource files.
// From.App - the current application assembly
// From.This - the caller's assembly
// From.Library - a library assembly
// From.Type<ClassLibrary> - an assembly containing a known type
internal class From
{
internal Assembly Assembly { get; set; }
internal string Name { get { return (Assembly != null ? Assembly.FullName.Substring(0, Assembly.FullName.IndexOf(',')) : null); } }
// From current application assembly
internal static From App { get { return new From() { Assembly = Application.Current.GetType().Assembly }; } }
// From the caller's assembly
internal static From This { get { return new From() { Assembly = Assembly.GetCallingAssembly() }; } }
// From a library assembly
internal static From Library { get { return new From() { Assembly = Assembly.GetExecutingAssembly() }; } }
// From an assembly containing a known type
internal static From Type<TT>() { return new From() { Assembly = typeof(TT).Assembly }; }
}
}
As a side note, I discovered a shortcut to accessing embedded font resources directly from XAML:
<TextBlock x:Name="x_TestText" FontFamily="CacMoose.ttf#CAC Moose" />
or…
<TextBlock x:Name="x_TestText" FontFamily="Content/Zips/Files.zip#CAC Moose" />
You will need to set the CacMose.ttf file's Build Action to Resource.
If you want to read more about Silverlight resource files, check this out from MSDN: http://msdn.microsoft.com/en-us/library/cc296240(VS.95).aspx
by Jim McCurdy
28. August 2009 22:36
A minor annoyance of mine is that there is no way to wire up a standard Silverlight TextBox to select its text when it receives the keyboard focus; either via a mouse click or a tab key.
And since users are accustomed to web apps, browsers, and desktop applications that offer the the convenience of selecting textbox text upon focus, I wanted that behavior in my Silverlight applications. So to satisfy user expectations as a matter of consistency, I wrote a very simple derived class, TextBoxEx, that will offer this functionality. The TextBoxEx class derives from TextBox, and can be referenced in XAML for any and all of your TextBox’s. There are no methods to call. It just listens for Focus events and selects it own text. Very simple.
Usage is as follows:
In XAML, reference the assembly where you implement the TextBoxEx class listed below, and add as many TextBoxEx elements as you need. The example below uses data binding to display a username.
<UserControl x:Class="MyApp.MainPage"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:ClassLibrary;assembly=ClassLibrary"
>
.
.
.
<c:TextBoxEx Text="{Binding User.Name, Mode=TwoWay}" Width="120" />
The code below has been tested and used with Silverlight 3 and Silverlight 4.
using System.Windows;
using System.Windows.Controls;
namespace ClassLibrary
{
// This TextBox derived class selects all text when it receives focus
public class TextBoxEx : TextBox
{
public TextBoxEx()
{
base.GotFocus += OnGotFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
base.SelectAll();
}
}
}
by Jim McCurdy
14. August 2009 22:34
Here is a simple static class, Settings, that offers methods to Read and Write application settings and preferences from Silverlight applications. These settings are similar to the ones that Windows apps would store in the registry or in an INI file, but in this case, are written to Silverlight’s isolated storage.
This class wraps Silverlight’s IsolatedStorageSettings class, so remember; since a user has the freedom to purge isolated storage at will, this class is mostly useful for storing application preferences like layout sizes and locations, and other creature comforts for regular users. This class can read or write any data type to settings storage; the IsolatedStorageSettings class will automatically serialize and deserialize the settings for you.
The code below has been tested and used with Silverlight 3 and Silverlight 4.
using System.IO.IsolatedStorage;
namespace ClassLibrary
{
public static class Settings
{
public static TT Read<TT>(string name)
{
return Read<TT>(name, default(TT));
}
public static TT Read<TT>(string name, TT defaultValue)
{
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
TT value;
if (settings == null || !settings.TryGetValue<TT>(name, out value))
return defaultValue;
return value;
}
public static void Write<TT>(string name, TT value)
{
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
if (settings == null)
return;
if (settings.Contains(name))
settings[name] = value;
else
settings.Add(name, value);
settings.Save();
}
}
}
by Jim McCurdy
7. August 2009 22:32
Here is a simple static class, MouseButtonHelper, that offers a single method, IsDoubleClick, to determine if a standard MouseLeftButtonDown or MouseLeftButtonUp event is a double click. In the past I have used timers, and Triggers and Behaviors to accomplish the same thing, but this approach is less code, less XAML, and uses a lot less resources.
Usage is as follows:
In XAML or in code behind, handle the standard MouseLeftButtonDown or MouseLeftButtonUp event for the object you wish to perform double click handling.
Implement the handler function for the above event as follows:
public void OnMouseButtonDown(object sender, MouseButtonEventArgs e)
{
bool doubleClick = MouseButtonHelper.EventIsDoubleClick(sender, e);
if (doubleClick)
MessageBox.Show("Double click detected!", "Alert", MessageBoxButton.OK);
}
The code below has been tested and used with Silverlight 4.
using System;
using System.Windows;
using System.Windows.Input;
namespace ClassLibrary
{
internal static class MouseButtonHelper
{
private const long k_DoubleClickSpeed = 500;
private const double k_MaxMoveDistance = 10;
private static long _LastClickTicks = 0;
private static Point _LastPosition;
private static WeakReference _LastSender;
internal static bool IsDoubleClick(object sender, MouseButtonEventArgs e)
{
Point position = e.GetPosition(null);
long clickTicks = DateTime.Now.Ticks;
long elapsedTicks = clickTicks - _LastClickTicks;
long elapsedTime = elapsedTicks / TimeSpan.TicksPerMillisecond;
bool quickClick = (elapsedTime <= k_DoubleClickSpeed);
bool senderMatch = (_LastSender != null && sender.Equals(_LastSender.Target));
if (senderMatch && quickClick && position.Distance(_LastPosition) <= k_MaxMoveDistance)
{
// Double click!
_LastClickTicks = 0;
_LastSender = null;
return true;
}
// Not a double click
_LastClickTicks = clickTicks;
_LastPosition = position;
if (!quickClick)
_LastSender = new WeakReference(sender);
return false;
}
private static double Distance(this Point pointA, Point pointB)
{
double x = pointA.X - pointB.X;
double y = pointA.Y - pointB.Y;
return Math.Sqrt(x * x + y * y);
}
}
}
by Jim McCurdy
31. July 2009 22:30
Here is a simple static class, Cookie, that offers methods to Read and Write cookies from Silverlight applications. You can also specify the cookie expiration as a number of days:
expireDays = 0, indicates a session cookie that will not be written to disk
expireDays = -1, indicates that the cookie will not expire and will be permanent
expireDays = n, indicates that the cookie will expire in “n” days
The code below has been tested and used with Silverlight 3 and Silverlight 4.
using System;
using System.Windows.Browser;
namespace ClassLibrary
{
public static class Cookie
{
public static bool Exists(string key, string value)
{
return HtmlPage.Document.Cookies.Contains(key + "=" + value);
}
public static string Read(string key)
{
string[] cookies = HtmlPage.Document.Cookies.Split(';');
foreach (string cookie in cookies)
{
string[] keyValuePair = cookie.Split('=');
if (keyValuePair.Length == 2 && key == keyValuePair[0].Trim())
return keyValuePair[1].Trim();
}
return null;
}
public static void Write(string key, string value, int expireDays)
{
// expireDays = 0, indicates a session cookie that will not be written to disk
// expireDays = -1, indicates that the cookie will not expire and will be permanent
// expireDays = n, indicates that the cookie will expire in “n” days
string expires = "";
if (expireDays != 0)
{
DateTime expireDate = (expireDays > 0 ?
DateTime.Now + TimeSpan.FromDays(expireDays) :
DateTime.MaxValue);
expires = ";expires=" + expireDate.ToString("R");
}
string cookie = key + "=" + value + expires;
HtmlPage.Document.SetProperty("cookie", cookie);
}
public static void Delete(string key)
{
DateTime expireDate = DateTime.Now - TimeSpan.FromDays(1); // yesterday
string expires = ";expires=" + expireDate.ToString("R");
string cookie = key + "=" + expires;
HtmlPage.Document.SetProperty("cookie", cookie);
}
}
}
by Jim McCurdy
24. July 2009 18:59
Here is a simple static class, ColorFromString, that offers a single string extension method, ToColor, to create Color objects from various strings representations of color:
Named colors
“Red”
“Yellow”
“Chartreuse”
ARGB colors as Hex (8 nibbles)
“#FF00FF00” for opaque green
“#80FF00FF” for translucent magenta
RGB colors as Hex (6 nibbles)
“#0000FF” for blue
“#FFFF00” for yellow
ARGB colors as comma or space separated decimal
“255,0,255,0” for opaque green
“128,255,0,255” for translucent magenta
RGB colors as comma or space separated decimal
“0,0,255” for blue
“255,255,0” for yellow
The code below has been tested and used with Silverlight 3 and Silverlight 4., but can be made to work with .NET by changing the System references.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Media;
namespace ClassLibrary
{
public static class ColorFromString
{
private static Dictionary<string, Color> namedColors =
new Dictionary<string, Color>();
// Extension for string
public static Color ToColor(this string value)
{
if (value == null)
return Colors.Red;
// Named Colors
string valueLower = value.ToLower();
if (namedColors.ContainsKey(valueLower))
return namedColors[valueLower];
// #ARGB and #RGB Hex Colors
if (value[0] == '#')
value = value.Remove(0, 1);
int length = value.Length;
if ((length == 6 || length == 8) && IsHexColor(value))
{
if (length == 8)
return Color.FromArgb(
byte.Parse(value.Substring(0, 2), NumberStyles.HexNumber),
byte.Parse(value.Substring(2, 2), NumberStyles.HexNumber),
byte.Parse(value.Substring(4, 2), NumberStyles.HexNumber),
byte.Parse(value.Substring(6, 2), NumberStyles.HexNumber));
if (length == 6)
return Color.FromArgb(0xff,
byte.Parse(value.Substring(0, 2), NumberStyles.HexNumber),
byte.Parse(value.Substring(2, 2), NumberStyles.HexNumber),
byte.Parse(value.Substring(4, 2), NumberStyles.HexNumber));
}
// A,R,G,B and R,G,B Colors
string[] argb = value.Split(
new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (argb != null)
{
if (argb.Length == 4)
return Color.FromArgb(
byte.Parse(argb[0]), byte.Parse(argb[1]), byte.Parse(argb[2]),
byte.Parse(argb[3]));
if (argb.Length == 3)
return Color.FromArgb(0xff,
byte.Parse(argb[0]), byte.Parse(argb[1]), byte.Parse(argb[2]));
}
return Colors.Red;
}
private static bool IsHexColor(string value)
{
if (value == null)
return false;
foreach (char c in value.ToCharArray())
if (!Uri.IsHexDigit(c))
return false;
return true;
}
static ColorFromString()
{
namedColors.Add("aliceblue", ToColor("#f0f8ff"));
namedColors.Add("antiquewhite", ToColor("#faebd7"));
namedColors.Add("aqua", ToColor("#00ffff"));
namedColors.Add("aquamarine", ToColor("#7fffd4"));
namedColors.Add("azure", ToColor("#f0ffff"));
namedColors.Add("beige", ToColor("#f5f5dc"));
namedColors.Add("bisque", ToColor("#ffe4c4"));
namedColors.Add("black", ToColor("#000000"));
namedColors.Add("blanchedalmond", ToColor("#ffebcd"));
namedColors.Add("blue", ToColor("#0000ff"));
namedColors.Add("blueviolet", ToColor("#8a2be2"));
namedColors.Add("brown", ToColor("#a52a2a"));
namedColors.Add("burlywood", ToColor("#deb887"));
namedColors.Add("cadetblue", ToColor("#5f9ea0"));
namedColors.Add("chartreuse", ToColor("#7fff00"));
namedColors.Add("chocolate", ToColor("#d2691e"));
namedColors.Add("coral", ToColor("#ff7f50"));
namedColors.Add("cornflowerblue", ToColor("#6495ed"));
namedColors.Add("cornsilk", ToColor("#fff8dc"));
namedColors.Add("crimson", ToColor("#dc143c"));
namedColors.Add("cyan", ToColor("#00ffff"));
namedColors.Add("darkblue", ToColor("#00008b"));
namedColors.Add("darkcyan", ToColor("#008b8b"));
namedColors.Add("darkgoldenrod", ToColor("#b8860b"));
namedColors.Add("darkgray", ToColor("#a9a9a9"));
namedColors.Add("darkgreen", ToColor("#006400"));
namedColors.Add("darkkhaki", ToColor("#bdb76b"));
namedColors.Add("darkmagenta", ToColor("#8b008b"));
namedColors.Add("darkolivegreen", ToColor("#556b2f"));
namedColors.Add("darkorange", ToColor("#ff8c00"));
namedColors.Add("darkorchid", ToColor("#9932cc"));
namedColors.Add("darkred", ToColor("#8b0000"));
namedColors.Add("darksalmon", ToColor("#e9967a"));
namedColors.Add("darkseagreen", ToColor("#8fbc8f"));
namedColors.Add("darkslateblue", ToColor("#483d8b"));
namedColors.Add("darkslategray", ToColor("#2f4f4f"));
namedColors.Add("darkturquoise", ToColor("#00ced1"));
namedColors.Add("darkviolet", ToColor("#9400d3"));
namedColors.Add("deeppink", ToColor("#ff1493"));
namedColors.Add("deepskyblue", ToColor("#00bfff"));
namedColors.Add("dimgray", ToColor("#696969"));
namedColors.Add("dodgerblue", ToColor("#1e90ff"));
namedColors.Add("firebrick", ToColor("#b22222"));
namedColors.Add("floralwhite", ToColor("#fffaf0"));
namedColors.Add("forestgreen", ToColor("#228b22"));
namedColors.Add("fuchsia", ToColor("#ff00ff"));
namedColors.Add("gainsboro", ToColor("#dcdcdc"));
namedColors.Add("ghostwhite", ToColor("#f8f8ff"));
namedColors.Add("gold", ToColor("#ffd700"));
namedColors.Add("goldenrod", ToColor("#daa520"));
namedColors.Add("gray", ToColor("#808080"));
namedColors.Add("green", ToColor("#008000"));
namedColors.Add("greenyellow", ToColor("#adff2f"));
namedColors.Add("honeydew", ToColor("#f0fff0"));
namedColors.Add("hotpink", ToColor("#ff69b4"));
namedColors.Add("indianred", ToColor("#cd5c5c"));
namedColors.Add("indigo", ToColor("#4b0082"));
namedColors.Add("ivory", ToColor("#fffff0"));
namedColors.Add("khaki", ToColor("#f0e68c"));
namedColors.Add("lavender", ToColor("#e6e6fa"));
namedColors.Add("lavenderblush", ToColor("#fff0f5"));
namedColors.Add("lawngreen", ToColor("#7cfc00"));
namedColors.Add("lemonchiffon", ToColor("#fffacd"));
namedColors.Add("lightblue", ToColor("#add8e6"));
namedColors.Add("lightcoral", ToColor("#f08080"));
namedColors.Add("lightcyan", ToColor("#e0ffff"));
namedColors.Add("lightgoldenrodyellow", ToColor("#fafad2"));
namedColors.Add("lightgreen", ToColor("#90ee90"));
namedColors.Add("lightgrey", ToColor("#d3d3d3"));
namedColors.Add("lightpink", ToColor("#ffb6c1"));
namedColors.Add("lightsalmon", ToColor("#ffa07a"));
namedColors.Add("lightseagreen", ToColor("#20b2aa"));
namedColors.Add("lightskyblue", ToColor("#87cefa"));
namedColors.Add("lightslategray", ToColor("#778899"));
namedColors.Add("lightsteelblue", ToColor("#b0c4de"));
namedColors.Add("lightyellow", ToColor("#ffffe0"));
namedColors.Add("lime", ToColor("#00ff00"));
namedColors.Add("limegreen", ToColor("#32cd32"));
namedColors.Add("linen", ToColor("#faf0e6"));
namedColors.Add("magenta", ToColor("#ff00ff"));
namedColors.Add("maroon", ToColor("#800000"));
namedColors.Add("mediumaquamarine", ToColor("#66cdaa"));
namedColors.Add("mediumblue", ToColor("#0000cd"));
namedColors.Add("mediumorchid", ToColor("#ba55d3"));
namedColors.Add("mediumpurple", ToColor("#9370db"));
namedColors.Add("mediumseagreen", ToColor("#3cb371"));
namedColors.Add("mediumslateblue", ToColor("#7b68ee"));
namedColors.Add("mediumspringgreen", ToColor("#00fa9a"));
namedColors.Add("mediumturquoise", ToColor("#48d1cc"));
namedColors.Add("mediumvioletred", ToColor("#c71585"));
namedColors.Add("midnightblue", ToColor("#191970"));
namedColors.Add("mintcream", ToColor("#f5fffa"));
namedColors.Add("mistyrose", ToColor("#ffe4e1"));
namedColors.Add("moccasin", ToColor("#ffe4b5"));
namedColors.Add("navajowhite", ToColor("#ffdead"));
namedColors.Add("navy", ToColor("#000080"));
namedColors.Add("oldlace", ToColor("#fdf5e6"));
namedColors.Add("olive", ToColor("#808000"));
namedColors.Add("olivedrab", ToColor("#6b8e23"));
namedColors.Add("orange", ToColor("#ffa500"));
namedColors.Add("orangered", ToColor("#ff4500"));
namedColors.Add("orchid", ToColor("#da70d6"));
namedColors.Add("palegoldenrod", ToColor("#eee8aa"));
namedColors.Add("palegreen", ToColor("#98fb98"));
namedColors.Add("paleturquoise", ToColor("#afeeee"));
namedColors.Add("palevioletred", ToColor("#db7093"));
namedColors.Add("papayawhip", ToColor("#ffefd5"));
namedColors.Add("peachpuff", ToColor("#ffdab9"));
namedColors.Add("peru", ToColor("#cd853f"));
namedColors.Add("pink", ToColor("#ffc0cb"));
namedColors.Add("plum", ToColor("#dda0dd"));
namedColors.Add("powderblue", ToColor("#b0e0e6"));
namedColors.Add("purple", ToColor("#800080"));
namedColors.Add("red", ToColor("#ff0000"));
namedColors.Add("rosybrown", ToColor("#bc8f8f"));
namedColors.Add("royalblue", ToColor("#4169e1"));
namedColors.Add("saddlebrown", ToColor("#8b4513"));
namedColors.Add("salmon", ToColor("#fa8072"));
namedColors.Add("sandybrown", ToColor("#f4a460"));
namedColors.Add("seagreen", ToColor("#2e8b57"));
namedColors.Add("seashell", ToColor("#fff5ee"));
namedColors.Add("sienna", ToColor("#a0522d"));
namedColors.Add("silver", ToColor("#c0c0c0"));
namedColors.Add("skyblue", ToColor("#87ceeb"));
namedColors.Add("slateblue", ToColor("#6a5acd"));
namedColors.Add("slategray", ToColor("#708090"));
namedColors.Add("snow", ToColor("#fffafa"));
namedColors.Add("springgreen", ToColor("#00ff7f"));
namedColors.Add("steelblue", ToColor("#4682b4"));
namedColors.Add("tan", ToColor("#d2b48c"));
namedColors.Add("teal", ToColor("#008080"));
namedColors.Add("thistle", ToColor("#d8bfd8"));
namedColors.Add("tomato", ToColor("#ff6347"));
namedColors.Add("turquoise", ToColor("#40e0d0"));
namedColors.Add("violet", ToColor("#ee82ee"));
namedColors.Add("wheat", ToColor("#f5deb3"));
namedColors.Add("white", ToColor("#ffffff"));
namedColors.Add("whitesmoke", ToColor("#f5f5f5"));
namedColors.Add("yellow", ToColor("#ffff00"));
namedColors.Add("yellowgreen", ToColor("#9acd32"));
}
}
}
Here are the color patches:
Aliceblue F0F8FF
Antiquewhite FAEBD7
Aqua 00FFFF
Aquamarine 7FFFD4
Azure F0FFFF
Beige F5F5DC
Bisque FFE4C4
Black 000000
Blanchedalmond FFEBCD
Blue 0000FF
Blueviolet 8A2BE2
Brown A52A2A
Burlywood DEB887
Cadetblue 5F9EA0
Chartreuse 7FFF00
Chocolate D2691E
Coral FF7F50
Cornflowerblue 6495ED
Cornsilk FFF8DC
Crimson DC143C
Cyan 00FFFF
Darkblue 00008B
Darkcyan 008B8B
Darkgoldenrod B8860B
Darkgray A9A9A9
Darkgreen 006400
Darkkhaki BDB76B
Darkmagenta 8B008B
Darkolivegreen 556B2F
Darkorange FF8C00
Darkorchid 9932CC
Darkred 8B0000
Darksalmon E9967A
Darkseagreen 8FBC8F
Darkslateblue 483D8B
Darkslategray 2F4F4F
Darkturquoise 00CED1
Darkviolet 9400D3
deeppink FF1493
Deepskyblue 00BFFF
Dimgray 696969
Dodgerblue 1E90FF
Firebrick B22222
Floralwhite FFFAF0
Forestgreen 228B22
Fuchsia FF00FF
Gainsboro DCDCDC
Ghostwhite F8F8FF
Gold FFD700
Goldenrod DAA520
Gray 808080
Green 008000
Greenyellow ADFF2F
Honeydew F0FFF0
Hotpink FF69B4
Indianred CD5C5C
Indigo 4B0082
Ivory FFFFF0
Khaki F0E68C
Lavender E6E6FA
Lavenderblush FFF0F5
Lawngreen 7CFC00
Lemonchiffon FFFACD
Lightblue ADD8E6
Lightcoral F08080
Lightcyan E0FFFF
Lightgoldenrodyellow FAFAD2
Lightgreen 90EE90
Lightgrey D3D3D3
Lightpink FFB6C1
Lightsalmon FFA07A
Lightseagreen 20B2AA
Lightskyblue 87CEFA
Lightslategray 778899
Lightsteelblue B0C4DE
Lightyellow FFFFE0
Lime 00FF00
Limegreen 32CD32
Linen FAF0E6
Magenta FF00FF
Maroon 800000
Mediumaquamarine 66CDAA
Mediumblue 0000CD
Mediumorchid BA55D3
Mediumpurple 9370D8
Mediumseagreen 3CB371
Mediumslateblue 7B68EE
Mediumspringgreen 00FA9A
Mediumturquoise 48D1CC
Mediumvioletred C71585
Midnightblue 191970
Mintcream F5FFFA
Mistyrose FFE4E1
Moccasin FFE4B5
Navajowhite FFDEAD
Navy 000080
Oldlace FDF5E6
Olive 808000
Olivedrab 688E23
Orange FFA500
Orangered FF4500
Orchid DA70D6
Palegoldenrod EEE8AA
Palegreen 98FB98
Paleturquoise AFEEEE
Palevioletred D87093
Papayawhip FFEFD5
Peachpuff FFDAB9
Peru CD853F
Pink FFC0CB
Plum DDA0DD
Powderblue B0E0E6
Purple 800080
Red FF0000
Rosybrown BC8F8F
Royalblue 4169E1
Saddlebrown 8B4513
Salmon FA8072
Sandybrown F4A460
Seagreen 2E8B57
Seashell FFF5EE
Sienna A0522D
Silver C0C0C0
Skyblue 87CEEB
Slateblue 6A5ACD
Slategray 708090
Snow FFFAFA
Springgreen 00FF7F
Steelblue 4682B4
Tan D2B48C
Teal 008080
Thistle D8BFD8
Tomato FF6347
Turquoise 40E0D0
Violet EE82EE
Wheat F5DEB3
White FFFFFF
Whitesmoke F5F5F5
Yellow FFFF00
YellowGreen 9ACD32