/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ /**************************************************************************\ Copyright Microsoft Corporation. All Rights Reserved. \**************************************************************************/ // This file contains general utilities to aid in development. // Classes here generally shouldn't be exposed publicly since // they're not particular to any library functionality. // Because the classes here are internal, it's likely this file // might be included in multiple assemblies. namespace Standard { using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; internal static partial class Utility { private static readonly Version _osVersion = Environment.OSVersion.Version; private static readonly Version _presentationFrameworkVersion = Assembly.GetAssembly(typeof(Window)).GetName().Version; [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private static bool _MemCmp(IntPtr left, IntPtr right, long cb) { int offset = 0; for (; offset < (cb - sizeof(Int64)); offset += sizeof(Int64)) { Int64 left64 = Marshal.ReadInt64(left, offset); Int64 right64 = Marshal.ReadInt64(right, offset); if (left64 != right64) { return false; } } for (; offset < cb; offset += sizeof(byte)) { byte left8 = Marshal.ReadByte(left, offset); byte right8 = Marshal.ReadByte(right, offset); if (left8 != right8) { return false; } } return true; } /// The native RGB macro. /// /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int RGB(Color c) { return c.R | (c.G << 8) | (c.B << 16); } /// Convert a native integer that represent a color with an alpha channel into a Color struct. /// The integer that represents the color. Its bits are of the format 0xAARRGGBB. /// A Color representation of the parameter. public static Color ColorFromArgbDword(uint color) { return Color.FromArgb( (byte)((color & 0xFF000000) >> 24), (byte)((color & 0x00FF0000) >> 16), (byte)((color & 0x0000FF00) >> 8), (byte)((color & 0x000000FF) >> 0)); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int GET_X_LPARAM(IntPtr lParam) { return LOWORD(lParam.ToInt32()); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int GET_Y_LPARAM(IntPtr lParam) { return HIWORD(lParam.ToInt32()); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int HIWORD(int i) { return (short)(i >> 16); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int LOWORD(int i) { return (short)(i & 0xFFFF); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static bool AreStreamsEqual(Stream left, Stream right) { if (null == left) { return right == null; } if (null == right) { return false; } if (!left.CanRead || !right.CanRead) { throw new NotSupportedException("The streams can't be read for comparison"); } if (left.Length != right.Length) { return false; } var length = (int)left.Length; // seek to beginning left.Position = 0; right.Position = 0; // total bytes read int totalReadLeft = 0; int totalReadRight = 0; // bytes read on this iteration int cbReadLeft = 0; int cbReadRight = 0; // where to store the read data var leftBuffer = new byte[512]; var rightBuffer = new byte[512]; // pin the left buffer GCHandle handleLeft = GCHandle.Alloc(leftBuffer, GCHandleType.Pinned); IntPtr ptrLeft = handleLeft.AddrOfPinnedObject(); // pin the right buffer GCHandle handleRight = GCHandle.Alloc(rightBuffer, GCHandleType.Pinned); IntPtr ptrRight = handleRight.AddrOfPinnedObject(); try { while (totalReadLeft < length) { Assert.AreEqual(totalReadLeft, totalReadRight); cbReadLeft = left.Read(leftBuffer, 0, leftBuffer.Length); cbReadRight = right.Read(rightBuffer, 0, rightBuffer.Length); // verify the contents are an exact match if (cbReadLeft != cbReadRight) { return false; } if (!_MemCmp(ptrLeft, ptrRight, cbReadLeft)) { return false; } totalReadLeft += cbReadLeft; totalReadRight += cbReadRight; } Assert.AreEqual(cbReadLeft, cbReadRight); Assert.AreEqual(totalReadLeft, totalReadRight); Assert.AreEqual(length, totalReadLeft); return true; } finally { handleLeft.Free(); handleRight.Free(); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool GuidTryParse(string guidString, out Guid guid) { Verify.IsNeitherNullNorEmpty(guidString, "guidString"); try { guid = new Guid(guidString); return true; } catch (FormatException) { } catch (OverflowException) { } // Doesn't seem to be a valid guid. guid = default(Guid); return false; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsFlagSet(int value, int mask) { return 0 != (value & mask); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsFlagSet(uint value, uint mask) { return 0 != (value & mask); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsFlagSet(long value, long mask) { return 0 != (value & mask); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsFlagSet(ulong value, ulong mask) { return 0 != (value & mask); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsOSVistaOrNewer { get { return _osVersion >= new Version(6, 0); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool IsOSWindows7OrNewer { get { return _osVersion >= new Version(6, 1); } } /// /// Is this using WPF4? /// /// /// There are a few specific bugs in Window in 3.5SP1 and below that require workarounds /// when handling WM_NCCALCSIZE on the HWND. /// public static bool IsPresentationFrameworkVersionLessThan4 { get { return _presentationFrameworkVersion < new Version(4, 0); } } // Caller is responsible for destroying the HICON // Caller is responsible to ensure that GDI+ has been initialized. [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static IntPtr GenerateHICON(ImageSource image, Size dimensions) { if (image == null) { return IntPtr.Zero; } // If we're getting this from a ".ico" resource, then it comes through as a BitmapFrame. // We can use leverage this as a shortcut to get the right 16x16 representation // because DrawImage doesn't do that for us. var bf = image as BitmapFrame; if (bf != null) { bf = GetBestMatch(bf.Decoder.Frames, (int)dimensions.Width, (int)dimensions.Height); } else { // Constrain the dimensions based on the aspect ratio. var drawingDimensions = new Rect(0, 0, dimensions.Width, dimensions.Height); // There's no reason to assume that the requested image dimensions are square. double renderRatio = dimensions.Width / dimensions.Height; double aspectRatio = image.Width / image.Height; // If it's smaller than the requested size, then place it in the middle and pad the image. if (image.Width <= dimensions.Width && image.Height <= dimensions.Height) { drawingDimensions = new Rect((dimensions.Width - image.Width) / 2, (dimensions.Height - image.Height) / 2, image.Width, image.Height); } else if (renderRatio > aspectRatio) { double scaledRenderWidth = (image.Width / image.Height) * dimensions.Width; drawingDimensions = new Rect((dimensions.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, dimensions.Height); } else if (renderRatio < aspectRatio) { double scaledRenderHeight = (image.Height / image.Width) * dimensions.Height; drawingDimensions = new Rect(0, (dimensions.Height - scaledRenderHeight) / 2, dimensions.Width, scaledRenderHeight); } var dv = new DrawingVisual(); DrawingContext dc = dv.RenderOpen(); dc.DrawImage(image, drawingDimensions); dc.Close(); var bmp = new RenderTargetBitmap((int)dimensions.Width, (int)dimensions.Height, 96, 96, PixelFormats.Pbgra32); bmp.Render(dv); bf = BitmapFrame.Create(bmp); } // Using GDI+ to convert to an HICON. // I'd rather not duplicate their code. using (MemoryStream memstm = new MemoryStream()) { BitmapEncoder enc = new PngBitmapEncoder(); enc.Frames.Add(bf); enc.Save(memstm); using (var istm = new ManagedIStream(memstm)) { // We are not bubbling out GDI+ errors when creating the native image fails. IntPtr bitmap = IntPtr.Zero; try { Status gpStatus = NativeMethods.GdipCreateBitmapFromStream(istm, out bitmap); if (Status.Ok != gpStatus) { return IntPtr.Zero; } IntPtr hicon; gpStatus = NativeMethods.GdipCreateHICONFromBitmap(bitmap, out hicon); if (Status.Ok != gpStatus) { return IntPtr.Zero; } // Caller is responsible for freeing this. return hicon; } finally { Utility.SafeDisposeImage(ref bitmap); } } } } public static BitmapFrame GetBestMatch(IList frames, int width, int height) { return _GetBestMatch(frames, _GetBitDepth(), width, height); } private static int _MatchImage(BitmapFrame frame, int bitDepth, int width, int height, int bpp) { int score = 2 * _WeightedAbs(bpp, bitDepth, false) + _WeightedAbs(frame.PixelWidth, width, true) + _WeightedAbs(frame.PixelHeight, height, true); return score; } private static int _WeightedAbs(int valueHave, int valueWant, bool fPunish) { int diff = (valueHave - valueWant); if (diff < 0) { diff = (fPunish ? -2 : -1) * diff; } return diff; } /// From a list of BitmapFrames find the one that best matches the requested dimensions. /// The methods used here are copied from Win32 sources. We want to be consistent with /// system behaviors. private static BitmapFrame _GetBestMatch(IList frames, int bitDepth, int width, int height) { int bestScore = int.MaxValue; int bestBpp = 0; int bestIndex = 0; bool isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder; for (int i = 0; i < frames.Count && bestScore != 0; ++i) { int currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel; if (currentIconBitDepth == 0) { currentIconBitDepth = 8; } int score = _MatchImage(frames[i], bitDepth, width, height, currentIconBitDepth); if (score < bestScore) { bestIndex = i; bestBpp = currentIconBitDepth; bestScore = score; } else if (score == bestScore) { // Tie breaker: choose the higher color depth. If that fails, choose first one. if (bestBpp < currentIconBitDepth) { bestIndex = i; bestBpp = currentIconBitDepth; } } } return frames[bestIndex]; } // This can be cached. It's not going to change under reasonable circumstances. private static int s_bitDepth; // = 0; private static int _GetBitDepth() { if (s_bitDepth == 0) { using (SafeDC dc = SafeDC.GetDesktop()) { s_bitDepth = NativeMethods.GetDeviceCaps(dc, DeviceCap.BITSPIXEL) * NativeMethods.GetDeviceCaps(dc, DeviceCap.PLANES); } } return s_bitDepth; } /// /// Simple guard against the exceptions that File.Delete throws on null and empty strings. /// /// The path to delete. Unlike File.Delete, this can be null or empty. /// /// Note that File.Delete, and by extension SafeDeleteFile, does not throw an exception /// if the file does not exist. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDeleteFile(string path) { if (!string.IsNullOrEmpty(path)) { File.Delete(path); } } /// GDI's DeleteObject [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDeleteObject(ref IntPtr gdiObject) { IntPtr p = gdiObject; gdiObject = IntPtr.Zero; if (IntPtr.Zero != p) { NativeMethods.DeleteObject(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDestroyIcon(ref IntPtr hicon) { IntPtr p = hicon; hicon = IntPtr.Zero; if (IntPtr.Zero != p) { NativeMethods.DestroyIcon(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDestroyWindow(ref IntPtr hwnd) { IntPtr p = hwnd; hwnd = IntPtr.Zero; if (NativeMethods.IsWindow(p)) { NativeMethods.DestroyWindow(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDispose(ref T disposable) where T : IDisposable { // Dispose can safely be called on an object multiple times. IDisposable t = disposable; disposable = default(T); if (null != t) { t.Dispose(); } } /// GDI+'s DisposeImage /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void SafeDisposeImage(ref IntPtr gdipImage) { IntPtr p = gdipImage; gdipImage = IntPtr.Zero; if (IntPtr.Zero != p) { NativeMethods.GdipDisposeImage(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static void SafeCoTaskMemFree(ref IntPtr ptr) { IntPtr p = ptr; ptr = IntPtr.Zero; if (IntPtr.Zero != p) { Marshal.FreeCoTaskMem(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static void SafeFreeHGlobal(ref IntPtr hglobal) { IntPtr p = hglobal; hglobal = IntPtr.Zero; if (IntPtr.Zero != p) { Marshal.FreeHGlobal(p); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")] public static void SafeRelease(ref T comObject) where T : class { T t = comObject; comObject = default(T); if (null != t) { Assert.IsTrue(Marshal.IsComObject(t)); Marshal.ReleaseComObject(t); } } /// /// Utility to help classes catenate their properties for implementing ToString(). /// /// The StringBuilder to catenate the results into. /// The name of the property to be catenated. /// The value of the property to be catenated. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void GeneratePropertyString(StringBuilder source, string propertyName, string value) { Assert.IsNotNull(source); Assert.IsFalse(string.IsNullOrEmpty(propertyName)); if (0 != source.Length) { source.Append(' '); } source.Append(propertyName); source.Append(": "); if (string.IsNullOrEmpty(value)) { source.Append(""); } else { source.Append('\"'); source.Append(value); source.Append('\"'); } } /// /// Generates ToString functionality for a struct. This is an expensive way to do it, /// it exists for the sake of debugging while classes are in flux. /// Eventually this should just be removed and the classes should /// do this without reflection. /// /// /// /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [Obsolete] public static string GenerateToString(T @object) where T : struct { var sbRet = new StringBuilder(); foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (0 != sbRet.Length) { sbRet.Append(", "); } Assert.AreEqual(0, property.GetIndexParameters().Length); object value = property.GetValue(@object, null); string format = null == value ? "{0}: " : "{0}: \"{1}\""; sbRet.AppendFormat(format, property.Name, value); } return sbRet.ToString(); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void CopyStream(Stream destination, Stream source) { Assert.IsNotNull(source); Assert.IsNotNull(destination); destination.Position = 0; // If we're copying from, say, a web stream, don't fail because of this. if (source.CanSeek) { source.Position = 0; // Consider that this could throw because // the source stream doesn't know it's size... destination.SetLength(source.Length); } var buffer = new byte[4096]; int cbRead; do { cbRead = source.Read(buffer, 0, buffer.Length); if (0 != cbRead) { destination.Write(buffer, 0, cbRead); } } while (buffer.Length == cbRead); // Reset the Seek pointer before returning. destination.Position = 0; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static string HashStreamMD5(Stream stm) { stm.Position = 0; var hashBuilder = new StringBuilder(); using (MD5 md5 = MD5.Create()) { foreach (byte b in md5.ComputeHash(stm)) { hashBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } } return hashBuilder.ToString(); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static void EnsureDirectory(string path) { if (!Directory.Exists(Path.GetDirectoryName(path))) { Directory.CreateDirectory(Path.GetDirectoryName(path)); } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static bool MemCmp(byte[] left, byte[] right, int cb) { Assert.IsNotNull(left); Assert.IsNotNull(right); Assert.IsTrue(cb <= Math.Min(left.Length, right.Length)); // pin this buffer GCHandle handleLeft = GCHandle.Alloc(left, GCHandleType.Pinned); IntPtr ptrLeft = handleLeft.AddrOfPinnedObject(); // pin the other buffer GCHandle handleRight = GCHandle.Alloc(right, GCHandleType.Pinned); IntPtr ptrRight = handleRight.AddrOfPinnedObject(); bool fRet = _MemCmp(ptrLeft, ptrRight, cb); handleLeft.Free(); handleRight.Free(); return fRet; } private class _UrlDecoder { private readonly Encoding _encoding; private readonly char[] _charBuffer; private readonly byte[] _byteBuffer; private int _byteCount; private int _charCount; [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public _UrlDecoder(int size, Encoding encoding) { _encoding = encoding; _charBuffer = new char[size]; _byteBuffer = new byte[size]; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddByte(byte b) { _byteBuffer[_byteCount++] = b; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddChar(char ch) { _FlushBytes(); _charBuffer[_charCount++] = ch; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private void _FlushBytes() { if (_byteCount > 0) { _charCount += _encoding.GetChars(_byteBuffer, 0, _byteCount, _charBuffer, _charCount); _byteCount = 0; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public string GetString() { _FlushBytes(); if (_charCount > 0) { return new string(_charBuffer, 0, _charCount); } return ""; } } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static string UrlDecode(string url) { if (url == null) { return null; } var decoder = new _UrlDecoder(url.Length, Encoding.UTF8); int length = url.Length; for (int i = 0; i < length; ++i) { char ch = url[i]; if (ch == '+') { decoder.AddByte((byte)' '); continue; } if (ch == '%' && i < length - 2) { // decode %uXXXX into a Unicode character. if (url[i + 1] == 'u' && i < length - 5) { int a = _HexToInt(url[i + 2]); int b = _HexToInt(url[i + 3]); int c = _HexToInt(url[i + 4]); int d = _HexToInt(url[i + 5]); if (a >= 0 && b >= 0 && c >= 0 && d >= 0) { decoder.AddChar((char)((a << 12) | (b << 8) | (c << 4) | d)); i += 5; continue; } } else { // decode %XX into a Unicode character. int a = _HexToInt(url[i + 1]); int b = _HexToInt(url[i + 2]); if (a >= 0 && b >= 0) { decoder.AddByte((byte)((a << 4) | b)); i += 2; continue; } } } // Add any 7bit character as a byte. if ((ch & 0xFF80) == 0) { decoder.AddByte((byte)ch); } else { decoder.AddChar(ch); } } return decoder.GetString(); } /// /// Encodes a URL string. Duplicated functionality from System.Web.HttpUtility.UrlEncode. /// /// /// /// /// Duplicated from System.Web.HttpUtility because System.Web isn't part of the client profile. /// URL Encoding replaces ' ' with '+' and unsafe ASCII characters with '%XX'. /// Safe characters are defined in RFC2396 (http://www.ietf.org/rfc/rfc2396.txt). /// They are the 7-bit ASCII alphanumerics and the mark characters "-_.!~*'()". /// This implementation does not treat '~' as a safe character to be consistent with the System.Web version. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static string UrlEncode(string url) { if (url == null) { return null; } byte[] bytes = Encoding.UTF8.GetBytes(url); bool needsEncoding = false; int unsafeCharCount = 0; foreach (byte b in bytes) { if (b == ' ') { needsEncoding = true; } else if (!_UrlEncodeIsSafe(b)) { ++unsafeCharCount; needsEncoding = true; } } if (needsEncoding) { var buffer = new byte[bytes.Length + (unsafeCharCount * 2)]; int writeIndex = 0; foreach (byte b in bytes) { if (_UrlEncodeIsSafe(b)) { buffer[writeIndex++] = b; } else if (b == ' ') { buffer[writeIndex++] = (byte)'+'; } else { buffer[writeIndex++] = (byte)'%'; buffer[writeIndex++] = _IntToHex((b >> 4) & 0xF); buffer[writeIndex++] = _IntToHex(b & 0xF); } } bytes = buffer; Assert.AreEqual(buffer.Length, writeIndex); } return Encoding.ASCII.GetString(bytes); } // HttpUtility's UrlEncode is slightly different from the RFC. // RFC2396 describes unreserved characters as alphanumeric or // the list "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" // The System.Web version unnecessarily escapes '~', which should be okay... // Keeping that same pattern here just to be consistent. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private static bool _UrlEncodeIsSafe(byte b) { if (_IsAsciiAlphaNumeric(b)) { return true; } switch ((char)b) { case '-': case '_': case '.': case '!': //case '~': case '*': case '\'': case '(': case ')': return true; } return false; } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private static bool _IsAsciiAlphaNumeric(byte b) { return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9'); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private static byte _IntToHex(int n) { Assert.BoundedInteger(0, n, 16); if (n <= 9) { return (byte)(n + '0'); } return (byte)(n - 10 + 'A'); } [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private static int _HexToInt(char h) { if (h >= '0' && h <= '9') { return h - '0'; } if (h >= 'a' && h <= 'f') { return h - 'a' + 10; } if (h >= 'A' && h <= 'F') { return h - 'A' + 10; } Assert.Fail("Invalid hex character " + h); return -1; } public static void AddDependencyPropertyChangeListener(object component, DependencyProperty property, EventHandler listener) { if (component == null) { return; } Assert.IsNotNull(property); Assert.IsNotNull(listener); DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(property, component.GetType()); dpd.AddValueChanged(component, listener); } public static void RemoveDependencyPropertyChangeListener(object component, DependencyProperty property, EventHandler listener) { if (component == null) { return; } Assert.IsNotNull(property); Assert.IsNotNull(listener); DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(property, component.GetType()); dpd.RemoveValueChanged(component, listener); } #region Extension Methods public static bool IsThicknessNonNegative(Thickness thickness) { if (!IsDoubleFiniteAndNonNegative(thickness.Top)) { return false; } if (!IsDoubleFiniteAndNonNegative(thickness.Left)) { return false; } if (!IsDoubleFiniteAndNonNegative(thickness.Bottom)) { return false; } if (!IsDoubleFiniteAndNonNegative(thickness.Right)) { return false; } return true; } public static bool IsCornerRadiusValid(CornerRadius cornerRadius) { if (!IsDoubleFiniteAndNonNegative(cornerRadius.TopLeft)) { return false; } if (!IsDoubleFiniteAndNonNegative(cornerRadius.TopRight)) { return false; } if (!IsDoubleFiniteAndNonNegative(cornerRadius.BottomLeft)) { return false; } if (!IsDoubleFiniteAndNonNegative(cornerRadius.BottomRight)) { return false; } return true; } public static bool IsDoubleFiniteAndNonNegative(double d) { if (double.IsNaN(d) || double.IsInfinity(d) || d < 0) { return false; } return true; } #endregion } }