FlaUI / FlaUI

UI automation library for .Net
MIT License
2.23k stars 365 forks source link

Cannot Get WPF Window objects background color #563

Open obradatan opened 1 year ago

obradatan commented 1 year ago

I am trying to automate the theme apply to a WPF window. As a first step, I would need to check the background color of the window. Is there any property that could help me get that value? And also, I would need to get the buttons background colors from this window, as well. For now, it seems that it is only possible to check the color of the text input in the textboxes, because TextBoxes have the Text Pattern available. Any help is appreciated! Thank you!

MaksOta commented 1 year ago

I had that problem too and the only solution i could find was to Capture the image of the Element and analyze the pixels

var image = Capture.Element(cell);
var pixel = image.Bitmap.GetPixel(2, 2); // just some offset because there are usually weird things going on around the border

then you can get the pixels individual RGB or the name of the color etc.

obradatan commented 1 year ago

Hello, yep, I reached that solution as well, but for each test run, the pixel has a different RGB ...

bwedding commented 1 year ago

I wrote a utility to get the color of a control as a string of the nearest known color. It may be helpful for you:

    //Just a simple utility class to store our
    //color values and the distance from the color of interest
    public class ColorDistance
    {
        private int _colorKey;
        public int colorKey
        {
            get { return _colorKey; }
        }
        private int _distance;
        public int distance
        {
            get { return _distance; }
        }

        private ColorDistance(int colorKeyRgb, int rgb2)
        {
            //store for use at end of query
            this._colorKey = colorKeyRgb;

            //we just pull the individual color components out
            byte r1 = (byte)((colorKeyRgb >> 16) & 0xff);
            byte g1 = (byte)((colorKeyRgb >> 8) & 0xff);
            byte b1 = (byte)((colorKeyRgb) & 0xff);

            byte r2 = (byte)((rgb2 >> 16) & 0xff);
            byte g2 = (byte)((rgb2 >> 8) & 0xff);
            byte b2 = (byte)((rgb2) & 0xff);

            //provide a simple distance measure between colors
            _distance = Math.Abs(r1 - r2) + Math.Abs(g1 - g2) + Math.Abs(b1 - b2);
        }
    }
    public static class ColorMapper
    {

        public static string GetNearestKnownColorName(Color color)
        {
            var minDistance = int.MaxValue;
            var nearestColor = Color.Black; // default fallback color

            foreach (KnownColor knownColor in Enum.GetValues(typeof(KnownColor)))
            {
                var knownColorValue = Color.FromKnownColor(knownColor);
                var distance = (int)Math.Pow(color.R - knownColorValue.R, 2) +
                                   (int)Math.Pow(color.G - knownColorValue.G, 2) +
                                   (int)Math.Pow(color.B - knownColorValue.B, 2);
                if (distance >= minDistance) 
                    continue;
                minDistance = distance;
                nearestColor = knownColorValue;
            }
            return nearestColor.Name;
        }

        //create the dictionary with the elements you are interested in
        private static Dictionary<int, String> colorMap = new Dictionary<int, String>();
        static ColorMapper()
        {
            foreach (KnownColor kc in Enum.GetValues(typeof(KnownColor)))
            {
                Color c = Color.FromKnownColor(kc);
                try
                {
                    colorMap.Add(c.ToArgb() & 0x00FFFFFF, c.Name);
                }
                //duplicate colors cause an exception
                catch { }
            }
        }
    }

Usage:

public string GetBackgroundColor(FlaUI.Core.AutomationElements.AutomationElement control)
        {
            string name = "Unknown";

            if (control == null)
            {
                return name;
            }

            // Capture the control's screenshot
            var screenshot = control.Capture();
            var image = Capture.Element(control);
            // Get the color of the center pixel of the bitmap
            int centerX = image.OriginalBounds.Width / 2;
            int centerY = image.OriginalBounds.Height / 3;
            Color color = image.Bitmap.GetPixel(centerX, centerY);
            string colorName = ColorMapper.GetNearestKnownColorName(color);

            // Return the color
            return colorName;
        }
Roemer commented 1 year ago

Unfortunately, colors are not part of UIA (which has it's origin in accessibility where colors usually don't matter). and therefore they are not exposed to UIA. The only colors that are sometimes available are in Text controls: https://learn.microsoft.com/en-us/windows/win32/winauto/uiauto-textattributes#text-color-attributes Other ways are as already described by using images and pixel colors and the 3rd one would be to create your own UIA Control/Attribute to expose the color (which is a bit more complex thou).