Search code examples
c#wpfmousedown

How can I use a C# function to access the WPF control that called it?


I'm working on a game level editor in WPF using C#.

I have a series of image controls for choosing textures, and I want each image to be clickable, with some visible feedback to show which one is selected.

Here's one of the image controls, along with a green highlight border that shows up when it's clicked:

<Image x:Name="tile_image1" Source="as asphalt_test.png" Stretch="Fill" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" Height="50" MouseDown="texture_click" Margin="0,93,69,0" RenderTransformOrigin="0.16,2.04"/>
<Border x:Name="tile_border" BorderBrush="Lime" BorderThickness="3" HorizontalAlignment="Right" Height="54" Margin="0,91,65,0" VerticalAlignment="Top" Width="54" Visibility="Hidden" />

My question involves the "texture_click" function.

I want to re-use the same function for each image control, which I can easily assign using the MouseDown attribute in XAML. However, what I don't know is how to tell from within the function which control called it, or how to access that control's property's, such as ".Source". I want to be able to grab the file name of the image, as well as move the coordinates of the green border behind the new selection.

Right now, I just have it hard-coded to the first image control. Clicks on the other images will call the function, but the function will only select the first image (not the one that was actually clicked).

// click on tile 1
private void texture_click (object sender, MouseButtonEventArgs e)
  {
  tile_border.Visibility = Visibility.Visible;

  current_tilefile = tile_image1.Source;
  string source_string = Convert.ToString (tile_image1.Source);
  int last_slash = source_string.LastIndexOf ('/');
  current_tile = source_string.Substring (last_slash + 1, 3);
  }

I tried using "sender", since I thought that might be the object that called the function, but that returned an error. I also tried calling the function with "texture_click (this)", but that was also no good. These were, admittedly, complete shots in the dark, so I wasn't surprised.

I'm still pretty new to this software, so any insight you guys can give would be great.


Solution

  • You just have to cast the sender parameter to the control type (Image in this case):

    private void texture_click (object sender, MouseButtonEventArgs e)
    {
        //tile_border.Visibility = Visibility.Visible;
        var image = sender as Image;
        if (image != null)
        {
            current_tilefile = image.Source;
            string source_string = image.Source.ToString();
            int last_slash = source_string.LastIndexOf ('/');
            current_tile = source_string.Substring (last_slash + 1, 3);
        }
    }
    

    Of course, this doesn't give you access to the associated border. One thing you can do is to just dump the border into the Image's Tag property:

    <Image x:Name="tile_image1" ... Tag="{Binding ElementName=tile_border}" />
    <Border x:Name="tile_border" ... />
    

    Then you can retrieve it, again by casting:

    private void texture_click (object sender, MouseButtonEventArgs e)
    {
        var image = sender as Image;
        if (image != null)
        {
            var border = image.Tag as Border;
            if (border != null)
            {
                border.Visibility = Visibility.Visible;
            }
            // ...
        }
    }
    

    Note that this (manipulating UI elements from code-behind) is not the ideal way to write a WPF application. Typically you would do something like this by using an existing control (like a ToggleButton), and re-writing its ControlTemplate so that its IsChecked visual state shows a border. But I realize that is a mouthful ...