Search code examples
c#wpfopacitymaskismouseover

Use image transparency to filter mouse events


I've been digging through examples, tutorials, and forums all day and I cannot seem to wrap my head around what seems like a simple concept.

Essentially, I am creating a color wheel color selection tool. The tool is a ring in shape, so I do not want the mouse to perform functions unless it is hovering over the tool shape itself.

The color wheel is a simple image. I've tried finding ways to utilize the opacity mapping, drawing ellipses to detect the mouse (which works, but then I can't click the physical wheel under it).

I'm just running into blanks here.

What I want to achieve (inevitably) is this: Mouse moves into color wheel's domain, changes cursor to dropper. When user clicks on a pixel at xPos/yPos we want to grab the RGB value of the pixel at that location. Looks easy on paper, right?

Anyone wanna try to assist? Maybe some spit-balling? Thank you so much for any help already, and thank you for taking the time to at least look at my question!

Here is the image being used for the color wheel for now:

Color Wheel PNG

UPDATE: I've got an overlay working, and I'm passing the click event successfully. Seems like I might be going the right way. Just need to figure out how to grab the pixel data next.

XAML:

<Window x:Name="frmMain" x:Class="MouseImageTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:MouseImageTest"
    mc:Ignorable="d"
    Title="Color Picker Example" Height="423.41" Width="572.61">
<Window.Resources>
    <Style x:Key="EllipseStyle1" TargetType="{x:Type Ellipse}"/>
</Window.Resources>
<Grid>
    <Label x:Name="lblOpacity" Content="Update:" HorizontalAlignment="Left" VerticalAlignment="Top" Width="59" HorizontalContentAlignment="Right"/>
    <Label x:Name="lblNumbers" Content="" HorizontalAlignment="Left" Margin="64,0,0,0" VerticalAlignment="Top" Width="149"/>
    <Grid x:Name="grdBleh">
        <Image x:Name="image" HorizontalAlignment="Left" Height="331" Margin="118,29,0,0" VerticalAlignment="Top" Width="323" Source="physicswheel.png" StretchDirection="DownOnly" MouseDown="image_MouseDown">
            <Image.OpacityMask>
                <ImageBrush ImageSource="physicswheel.png" Stretch="Uniform" Opacity="0.99"/>
            </Image.OpacityMask>
        </Image>
        <Ellipse x:Name="swatchOuterBounds" HorizontalAlignment="Left" Height="291" Margin="127,38,0,0" VerticalAlignment="Top" Width="290" Stroke="#FDFF0000" StrokeThickness="50" Style="{DynamicResource EllipseStyle1}" Opacity="0" MouseEnter="ellipse_MouseEnter" MouseLeave="ellipse_MouseLeave" PreviewMouseDown="ellipse_MouseDown"/>
        <Border x:Name="brdrRed1" BorderBrush="Black" BorderThickness="2,2,1,2" Width="29" Height="25" Margin="446,106,89,256" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="4,0,0,4" Background="#FF959595">
            <Label x:Name="lblRed" Content="R:" Margin="0,0,5,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
        <Border x:Name="brdrRed2" BorderBrush="Black" BorderThickness="1,2,2,2" Width="29" Height="25" Margin="475,106,61,256" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="0,4,4,0" Background="#FF959595">
            <Label x:Name="lblRed2" Content="" Margin="5,0,0,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
        <Border x:Name="brdrGreen1" BorderBrush="Black" BorderThickness="2,2,1,2" Width="29" Height="25" Margin="446,135,89,229" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="4,0,0,4" Background="#FF959595">
            <Label x:Name="lblGreen" Content="G:" Margin="0,0,5,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
        <Border x:Name="brdrGreen2" BorderBrush="Black" BorderThickness="1,2,2,2" Width="29" Height="25" Margin="475,135,61,229" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="0,4,4,0" Background="#FF959595">
            <Label x:Name="lblGreen2" Content="" Margin="5,0,0,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
        <Border x:Name="brdrBlue1" BorderBrush="Black" BorderThickness="2,2,1,2" Width="29" Height="25" Margin="446,162,89,200" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="4,0,0,4" Background="#FF959595">
            <Label x:Name="lblBlue" Content="B:" Margin="0,0,5,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
        <Border x:Name="brdrBlue2" BorderBrush="Black" BorderThickness="1,2,2,2" Width="29" Height="25" Margin="475,162,61,200" VerticalAlignment="Center" HorizontalAlignment="Center" CornerRadius="0,4,4,0" Background="#FF959595">
            <Label x:Name="lblBlue2" Content="" Margin="5,0,0,0" Background="{x:Null}" Foreground="Black" BorderThickness="0" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Right"/>
        </Border>
    </Grid>

</Grid>

C#:

public partial class MainWindow : Window
{
     public MainWindow()
     {
         InitializeComponent();
     }

     private void ellipse_MouseEnter(object sender, MouseEventArgs e)
     {
         Cursor = Cursors.Hand;
     }

     private void ellipse_MouseLeave(object sender, MouseEventArgs e)
     {
         Cursor = Cursors.Arrow;
     }

     private void image_MouseDown(object sender, MouseButtonEventArgs e)
     {
         if (swatchOuterBounds.IsMouseOver)
         {
             lblNumbers.Content = "Clicked the color wheel!";

             // Insert mouseclick evaluation here and grab Pixel Data. 
             // Write to R/G/B labels.
         }
     }

     private void ellipse_MouseDown(object sender, MouseButtonEventArgs e)
     {
         image_MouseDown(sender, e);
     }
 } 

Solution

  • So, I had to kind of wing it. I controlled the click on the transparent ellipse and gathered the pixel data from the image control at Mouse coords. So, it all works. (Sorry, I don't like XAML, so here's what I came up with:

    Image swatcher = new Image();
            swatcher.Source = new BitmapImage(new Uri("/Icons/physicswheel.png", UriKind.Relative));
            swatcher.Stretch = Stretch.Uniform;
            swatcher.Height = 100;
            swatcher.Width = 100;
            swatcher.Margin = new Thickness(5, 5, 5, 5);
            swatcher.SetValue(Grid.RowProperty, 0);
            swatcher.SetValue(Grid.ColumnProperty, 0);
            swatcher.HorizontalAlignment = HorizontalAlignment.Center;
            columns.Children.Add(swatcher);
    
            Ellipse bumper = new Ellipse();
            bumper.Height = 95;
            bumper.Width = 95;
            bumper.SetValue(Grid.RowProperty, 0);
            bumper.SetValue(Grid.ColumnProperty, 0);
            bumper.Stroke = new SolidColorBrush(Color.FromArgb(0, 12, 12, 255));
            bumper.StrokeThickness = 17;
            bumper.MouseEnter += delegate (object source, MouseEventArgs e)
            {
                    //Change Mouse Cursor to custom dropper.
                    Uri curDropper = new Uri("/Cursors/eyedropper.cur", UriKind.Relative);
                bumper.Cursor = new Cursor(App.GetResourceStream(curDropper).Stream);
            };
            bumper.MouseDown += delegate (object source, MouseButtonEventArgs e)
            {
                    //Get Mouse position and then grab pixel data.
                    int xPos = Convert.ToInt32(e.GetPosition(swatcher).X);
                int yPos = Convert.ToInt32(e.GetPosition(swatcher).Y);
    
                CroppedBitmap dropper = new CroppedBitmap(swatcher.Source as BitmapSource, new Int32Rect(xPos, yPos, 1, 1));
    
                byte[] pixel = new byte[4];
                dropper.CopyPixels(pixel, 4, 0);
    
                    //Change Swatch Preview and RGB fields.
    
                    redUpDown.Text = pixel[2].ToString();
                greenUpDown.Text = pixel[1].ToString();
                blueUpDown.Text = pixel[0].ToString();
    
            };
            columns.Children.Add(bumper);
    

    I really hope this helps anyone on the same path. :)