#673 – Mapping Mouse Position to Color

You can map a 2D mouse position to a color by using the mouse position to map to the Hue and Saturation and then converting the HSV value to an RGB value.  (We just use 1.0 as the Value component of HSV).  We can map the mouse to a color wheel and use the angle of a line from the mouse position to the center of the wheel as the hue and use the distance from the center of the wheel as the saturation.

So we want to map a mouse position against a color wheel like the following:

In WPF, we’ll just create a Canvas, with a Label that will be used to dump out the RGB values.

<Window Name="win1" x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Color from Mouse Position"
        SizeToContent="WidthAndHeight"
        MouseMove="win1_MouseMove_1">

    <Canvas x:Name="canv1" Width="400" Height="400">
        <Label Content="{Binding RGBInfo}" HorizontalAlignment="Center" />
    </Canvas>
</Window>

In the code-behind, we’ll handle the MouseMove event for the window and re-calculate the color of the window’s background brush, based on the mouse position.

    /// <summary>
    ///
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        SolidColorBrush backBrush = new SolidColorBrush();

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;
            win1.Background = backBrush;
        }

        private string rgbInfo;
        public string RGBInfo
        {
            get { return rgbInfo; }
            set
            {
                if (value != rgbInfo)
                {
                    rgbInfo = value;
                    RaisePropertyChanged("RGBInfo");
                }
            }
        }

        private void win1_MouseMove_1(object sender, MouseEventArgs e)
        {
            double radius = (canv1.ActualWidth / 2);

            Color c = ColorFromMousePosition(e.GetPosition(canv1), radius);
            backBrush.Color = c;
            RGBInfo = string.Format("R={0}, G={1}, B={2}", c.R, c.G, c.B);
        }

        Color ColorFromMousePosition(Point mousePos, double radius)
        {
            // Position relative to center of canvas
            double xRel = mousePos.X - radius;
            double yRel = mousePos.Y - radius;

            // Hue is angle in deg, 0-360
            double angleRadians = Math.Atan2(yRel, xRel);
            double hue = angleRadians * (180 / Math.PI);
            if (hue < 0)
                hue = 360 + hue;

            // Saturation is distance from center
            double saturation = Math.Min(Math.Sqrt(xRel * xRel + yRel * yRel) / radius, 1.0);

            byte r, g, b;
            ColorUtil.HsvToRgb(hue, saturation, 1.0, out r, out g, out b);
            return Color.FromRgb(r, g, b);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

Notice that we make use of an HSV to RGB conversion function, found at http://www.splinter.com.au/converting-hsv-to-rgb-colour-using-c/. Below is the function as we’re using it.

    public static class ColorUtil
    {
        /// <summary>
        /// Convert HSV to RGB
        /// h is from 0-360
        /// s,v values are 0-1
        /// r,g,b values are 0-255
        /// Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
        /// </summary>
        public static void HsvToRgb(double h, double S, double V, out byte r, out byte g, out byte b)
        {
            // ######################################################################
            // T. Nathan Mundhenk
            // mundhenk@usc.edu
            // C/C++ Macro HSV to RGB

            double H = h;
            while (H < 0) { H += 360; };
            while (H >= 360) { H -= 360; };
            double R, G, B;
            if (V <= 0)
            { R = G = B = 0; }
            else if (S <= 0)
            {
                R = G = B = V;
            }
            else
            {
                double hf = H / 60.0;
                int i = (int)Math.Floor(hf);
                double f = hf - i;
                double pv = V * (1 - S);
                double qv = V * (1 - S * f);
                double tv = V * (1 - S * (1 - f));
                switch (i)
                {

                    // Red is the dominant color

                    case 0:
                        R = V;
                        G = tv;
                        B = pv;
                        break;

                    // Green is the dominant color

                    case 1:
                        R = qv;
                        G = V;
                        B = pv;
                        break;
                    case 2:
                        R = pv;
                        G = V;
                        B = tv;
                        break;

                    // Blue is the dominant color

                    case 3:
                        R = pv;
                        G = qv;
                        B = V;
                        break;
                    case 4:
                        R = tv;
                        G = pv;
                        B = V;
                        break;

                    // Red is the dominant color

                    case 5:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // Just in case we overshoot on our math by a little, we put these here. Since its a switch it won't slow us down at all to put these here.

                    case 6:
                        R = V;
                        G = tv;
                        B = pv;
                        break;
                    case -1:
                        R = V;
                        G = pv;
                        B = qv;
                        break;

                    // The color is not defined, we should throw an error.

                    default:
                        //LFATAL("i Value error in Pixel conversion, Value is %d", i);
                        R = G = B = V; // Just pretend its black/white
                        break;
                }
            }
            r = Clamp((byte)(R * 255.0));
            g = Clamp((byte)(G * 255.0));
            b = Clamp((byte)(B * 255.0));
        }

        /// <summary>
        /// Clamp a value to 0-255
        /// </summary>
        private static byte Clamp(byte i)
        {
            if (i < 0) return 0;
            if (i > 255) return 255;
            return i;
        }
    }

The final result is a window whose background color changes as we move the mouse around.

#672 – Mouse Coordinates Are in Device Independent Units

As with other screen position values in WPF, the Mouse.GetPosition function returns coordinates that are in device independent units, rather than in pixels.

1 device independet unit = 1/96 in.  This means that on a 96 dpi device, the value will actually map to pixels, but on other devices, the value will not be the same as the # pixels.

 

 

#671 – Mouse.GetPosition Only Works When Mouse Is in Window

You typically only use the Mouse.GetPosition method when the mouse pointer is within the boundaries of your application’s main window.  When the mouse is located outside of the window, calling GetPosition with a null value will return 0 values for the mouses X and Y position, regardless of where the mouse is located on the screen.

In the example below, we are updating a label to dump out the value returned by Mouse.GetPosition when a timer fires, ever second.

 

#670 – Getting the Mouse Position Relative to a Specific Element

When you use the Mouse.GetPosition method or MouseButtonEventsArgs.GetPosition method to get the mouse position, you specify that you want the mouse position relative to a particular element.

If you pass a null value into the GetPosition method, you indicate that you want the mouse position relative to the containing window.    However, you can also pass a reference to a control that implements the IInputElement interface.  Since UIElement implements IInputElement, you can pass in a reference to any user interface element.

In the example below, on mouse movement, we display the coordinates of the mouse position relative to the main window, as well as relative to a Button.

        private void win1_MouseMove_1(object sender, MouseEventArgs e)
        {
            Point p = e.GetPosition(null);
            MousePosText = string.Format("GetPosition(null): X = {0}, Y = {1}", p.X, p.Y);

            p = e.GetPosition(btn1);
            MousePosText2 = string.Format("GetPosition(btn1): X = {0}, Y = {1}", p.X, p.Y);
        }



#669 – Retrieving the Mouse’s Current Position in an Event Handler

You can use the static Mouse.GetPosition method anywhere in your code to retrieve the current position of your mouse.

You can also access the mouse position through either the MouseButtonEventArgs or MouseEventArgs object passed in to an event handler for any of the mouse-related events.

In the example below, we use the MouseButtonEventArgs.GetPosition and MouseEventArgs.GetPosition methods in the handlers to get the mouse’s position and to update a related label.

        private void win1_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(null);
            MousePosTextLastClick = string.Format("Last click at X = {0}, Y = {1}", p.X, p.Y);
        }

        private void win1_MouseMove_1(object sender, MouseEventArgs e)
        {
            Point p = e.GetPosition(null);
            MousePosText = string.Format("X = {0}, Y = {1}", p.X, p.Y);
        }


#668 – Retrieving the Mouse’s Current Position

You can retrieve the current position of the mouse at any time using the Mouse.GetPosition method.  Mouse is a static class that provides information about the mouse through a variety of static methods.

The GetPosition method returns a Point object, which contains the and Y position of the mouse, relative to a specified user interface element.

In the example below, we call the GetPosition method and then set a property to report the mouse’s position.  (We’ll bind a label’s Content to this property).  Passing null to GetPosition means that we want the position relative to the top-level window.

        private void win1_MouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            ReportMousePosition();
        }

        // Can be called at any time (not just from event handler)
        public void ReportMousePosition()
        {
            Point p = Mouse.GetPosition(null);

            MousePosText = string.Format("X = {0}, Y = {1}", p.X, p.Y);
        }



#667 – IsMouseOver vs. IsMouseDirectlyOver

The IsMouseOver property for a user interface element indicates whether the mouse is currently located over the element or any of its children.  For example, for a Button contained in a StackPanel, when the user moves the mouse over the ButtonIsMouseOver will be true for both the Button and the StackPanel.

The IsMouseDirectlyOver, on the other hand, indicates whether the mouse is over a control and not over any of its children.

In the example below, when the mouse is over the button, the StackPanel’s IsMouseOver property is true, but its IsMouseDirectlyOver property is false.

 

Notice that IsMouseDirectlyOver is also false for the Button.  This is because the mouse is actually pointing to some child element within the Button element.

If we move the mouse back off the Button, but still within the StackPanel, its IsMouseDirectlyOver property becomes true.