« Posts under .NET

Silverlight 5 Slider Binding Bug

As described here
http://forums.silverlight.net/t/119338.aspx/1

Silverlight 5 (and 4) has a bug with it’s slider control. You can not bind to the Maximum and Minimum properties of the control. To get around this I have a simple solution with the code below. Basically just inherit from Slider and add two custom dependency properties and register for the on-changed events. In the callback you can manually set the minimum and maximum values of the slider instead of bindings.

This is a pain but seems to be a easy and clean solutions.

C#
 
public class CustomSlider : Slider
    {
 
        public static readonly DependencyProperty MinimumBugFixProperty = DependencyProperty.Register("MinimumBugFix", typeof(double), typeof(CustomSlider), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnUpdateSliderRange)));
        public static readonly DependencyProperty MaximumBugFixProperty = DependencyProperty.Register("MaximumBugFix", typeof(double), typeof(CustomSlider), new FrameworkPropertyMetadata(100, new PropertyChangedCallback(OnUpdateSliderRange)));
 
 
        public CustomSlider()
        {
 
        }
 
        public double MinimumBugFix
        {
            get { return (double)GetValue(MinimumBugFixProperty); }
            set { SetValue(MinimumBugFixProperty, value); }
        }
 
        public double MaximumBugFix
        {
            get { return (double)GetValue(MaximumBugFixProperty); }
            set { SetValue(MaximumBugFixProperty, value); }
        }
 
        private static void OnUpdateSliderRange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CustomSlider slider = d as CustomSlider;
            if (slider != null)
            {
                if(slider.Maximum != slider.MaximumBugFix)
                    slider.Maximum = slider.MaximumBugFix;
                if(slider.Minimum != slider.MinimumBugFix)
                    slider.Minimum = slider.MinimumBugFix;
            }
        }
}

WPF to Silverlight porting…

My biggest WTF moment so far with silverlight.. there is no Visibility.Hidden enum value.

So much for reusing all those valueconverters.. oh the pain!

C#
 
namespace System.Windows
{
    // Summary:
    //     Specifies the display state of an element.
    public enum Visibility
    {
        // Summary:
        //     Display the element.
        Visible = 0,
        //
        // Summary:
        //     Do not display the element, and do not reserve space for it in layout.
        Collapsed = 1,
    }
}

Calculate Dominant Color of Bitmap

The code section below is an example of how to calculate the dominant color in a bitmap. The code uses euclidean distance calculations for each color channel in each pixel. Then it sorts the pixels to find the color which is the closest to all other colors.

To improve the output I take to top %2 of colors and average them. Also the Alpha channel is treated differently though it could be changed to use the same distance logic.

The results are much better than the average color calculation. Though it is more computationally expensive so it will take longer to complete.

C#
<code>
 
public static Color CalculateDominantColor(BitmapSource source)
        {
            if (source.Format.BitsPerPixel != 32 || source.Format != PixelFormats.Bgra32)
                throw new ApplicationException("expected 32bit image");
 
 
            Dictionary<Color, double> colorDist = new Dictionary<Color, double>();
 
            System.Windows.Size sz = new System.Windows.Size(source.PixelWidth, source.PixelHeight);
 
            //read bitmap 
            int pixelsSz = (int)sz.Width * (int)sz.Height * (source.Format.BitsPerPixel / 8);
            int stride = ((int)sz.Width * source.Format.BitsPerPixel + 7) / 8;
            int pixelBytes = (source.Format.BitsPerPixel / 8);
 
            byte[] pixels = new byte[pixelsSz];
            source.CopyPixels(pixels, stride, 0);
 
            const int alphaThershold = 10;
            UInt64 pixelCount = 0;
            UInt64 avgAlpha = 0;
 
            for (int y = 0; y < sz.Height; y++)
            {
                for (int x = 0; x < sz.Width; x++)
                {
                    int index = (int)((y * sz.Width) + x) * (pixelBytes);
                    byte r1, g1, b1, a1; r1 = g1 = b1 = a1 = 0;
                    a1 = pixels[index + 3];
                    r1 = pixels[index + 2];
                    g1 = pixels[index + 1];
                    b1 = pixels[index];
 
                    if (a1 <= alphaThershold)
                        continue; //ignore
 
                    pixelCount++;
                    avgAlpha += (UInt64)a1;
 
                    Color cl = Color.FromArgb(0, r1, g1, b1);
                    double dist = 0;
                    if (!colorDist.ContainsKey(cl))
                    {
                        colorDist.Add(cl, 0);
 
                        for (int y2 = 0; y2 < sz.Height; y2++)
                        {
                            for (int x2 = 0; x2 < sz.Width; x2++)
                            {
                                int index2 = (int)(y2 * sz.Width) + x2;
                                byte r2, g2, b2, a2; r2 = g2 = b2 = a2 = 0;
                                a2 = pixels[index2 + 3];
                                r2 = pixels[index2 + 2];
                                g2 = pixels[index2 + 1];
                                b2 = pixels[index2];
 
                                if (a2 <= alphaThershold)
                                    continue; //ignore
 
                                dist += Math.Sqrt(Math.Pow(r2 - r1, 2) + 
                                                  Math.Pow(g2 - g1, 2) + 
                                                  Math.Pow(b2 - b1, 2));
                            }
                        }
 
                        colorDist[cl] = dist;
                    }
                }
            }
 
            //clamp alpha
            avgAlpha = avgAlpha / pixelCount;
            if (avgAlpha >= (255 - alphaThershold))
                avgAlpha = 255;
 
            //take weighted average of top 2% of colors         
            var clrs = (from entry in colorDist
                        orderby entry.Value ascending
                        select new { Color = entry.Key, Dist = 1.0/Math.Max(1,entry.Value) }).ToList().Take( Math.Max(1, (int)(colorDist.Count * 0.02)) );
 
            double sumDist = clrs.Sum(x => x.Dist);
            Color result = Color.FromArgb((byte)avgAlpha,
                                          (byte)(clrs.Sum(x => x.Color.R * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.G * x.Dist) / sumDist),
                                          (byte)(clrs.Sum(x => x.Color.B * x.Dist) / sumDist));
 
            return result;
        }
 
</code>

WPF easy bitmap resize

Here is an example of a really easy way to resize a bitmap using WPF imaging classes.

C#
<code>
 
 public static BitmapSource ResizeBitmap(BitmapSource source, int nWidth, int nHeight)
 {
   TransformedBitmap tbBitmap = new TransformedBitmap(source,
                                             new ScaleTransform(nWidth / source.PixelWidth,
                                                                nHeight / source.PixelHeight,
                                                                0, 0));
   return tbBitmap;
  }
 
 
</code>

WPF C#, CalculateAverageColor for bitmap

Here is a simple method to calculate the average color in a bitmap (WPF C#). The result is not always the best, it can tend to be muddy or more like a grey-scale effect. An improved method is to find the dominant color, though more computationally expensive – I will post an example soon.

C#
<code>
 
public static Color CalculateAverageColor(BitmapSource source)
        {
            if (source.Format.BitsPerPixel != 32)
                throw new ApplicationException("expected 32bit image");
 
            Color cl;
            System.Windows.Size sz = new System.Windows.Size(source.PixelWidth, source.PixelHeight);
 
            //read bitmap 
            int pixelsSz = (int)sz.Width * (int)sz.Height * (source.Format.BitsPerPixel / 8);
            int stride = ((int)sz.Width * source.Format.BitsPerPixel + 7) / 8;
 
            byte[] pixels = new byte[pixelsSz];
            source.CopyPixels(pixels, stride, 0);
 
            //find the average color for the image
            const int alphaThershold = 10;
            UInt64 r, g, b, a; r = g = b = a = 0;
            UInt64 pixelCount = 0;
 
            for (int y = 0; y < sz.Height; y++)
            {
                for (int x = 0; x < sz.Width; x++)
                {
                    int index = (int)((y * sz.Width) + x) * 4;
                    if (pixels[index + 3] <= alphaThershold) //ignore transparent
                        continue;
 
                    pixelCount++;
                    a += pixels[index + 3];
                    r += pixels[index + 2];
                    g += pixels[index + 1];
                    b += pixels[index];
                }
            }
 
            //average color result
            cl = Color.FromArgb((int)(a / pixelCount),
                                (int)(r / pixelCount),
                                (int)(g / pixelCount),
                                (int)(b / pixelCount));
 
            return cl;           
        }
 
</code>

WPF WebBrowser script errors

The WPF WebBrowser control doesn’t expose all the features of the IWebBrowser2 interface. This can be a real pain, because the control doesn’t even expose the underlying activex interface to get around its short comings. Recently I needed to disable script errors from being reported. This can easily be done with the WinForms version of the control. I can across some simple reflection based code to get around the issue. This technique could be used to access other features of the IWebBrowser2 interface.

C#
<code>
public partial class Text : UserControl {
        public Text()
        {                               
            InitializeComponent();            
            browser.Navigated += new NavigatedEventHandler(browser_Navigated);
        }
 
        void browser_Navigated(object sender, NavigationEventArgs e)
        {
            HideScriptErrors(browser, true);
        }
 
 
        public void HideScriptErrors(WebBrowser wb, bool Hide)
        {
            FieldInfo fiComWebBrowser = typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
            if (fiComWebBrowser == null) 
                return;
 
            object objComWebBrowser = fiComWebBrowser.GetValue(wb);
 
            if (objComWebBrowser == null) 
                return;
 
            objComWebBrowser.GetType().InvokeMember( "Silent", BindingFlags.SetProperty, null, objComWebBrowser, new object[] { Hide });
 
        }
}
 
</code>