Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Image with two sources

Tags:

c#

image

wpf

I'd like to extend Image class by adding second source. I want to define second source in XAML (like original source) and change these images when mouse enters/leaves this image.

I tried myself with:

class MainMenuImageButton : Image
    {
        public static readonly DependencyProperty Source2Property;
        public ImageSource Source2 
        {
            get { return Source2; }
            set
            {
                this.MouseEnter+=new System.Windows.Input.MouseEventHandler(MainMenuImageButton_MouseEnter);
            }
        }
        public void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
        {
            this.Source = Source2;
        }
    }

But it doesn't work and I think I do it tottaly wrong. Can somebody help?

[UPDATE]

I wrote this:

class MainMenuImageButton : Image
{
    protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters)
    {
        var source = (BitmapSource)Source;
        var x = (int)(hitTestParameters.HitPoint.X / ActualWidth * source.PixelWidth);
        var y = (int)(hitTestParameters.HitPoint.Y / ActualHeight * source.PixelHeight);
        var pixels = new byte[4];
        source.CopyPixels(new Int32Rect(x, y, 1, 1), pixels, 4, 0);
        if (pixels[3] < 10) return null;
        return new PointHitTestResult(this, hitTestParameters.HitPoint);
    }
    public ImageSource Source1
    {
        get { return GetValue(ImageSourceProperty) as ImageSource; }
        set { base.SetValue(ImageSourceProperty, value); }
    }
    public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("Source1", typeof(ImageSource), typeof(MainMenuImageButton));
    public ImageSource Source2
    {
        get { return GetValue(ImageSource2Property) as ImageSource; }
        set { base.SetValue(ImageSource2Property, value); }
    }
    public static readonly DependencyProperty ImageSource2Property = DependencyProperty.Register("Source2", typeof(ImageSource), typeof(MainMenuImageButton));
    public MainMenuImageButton() : base() 
    {
        this.MouseEnter += new MouseEventHandler(MainMenuImageButton_MouseEnter);
        this.MouseLeave += new MouseEventHandler(MainMenuImageButton_MouseLeave);
    }

    void MainMenuImageButton_MouseLeave(object sender, MouseEventArgs e)
    {
        this.Source = this.Source1;
    }

    void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
    {
        this.Source = this.Source2;
    }
}

But sometimes it works and sometimes there is exception: "An unhandled exception of type 'System.ArgumentException' occurred in PresentationCore.dll

Additional information: The value is outside the expected range."


I'm not sure if I understood, but I tried this:

class MainMenuImageButton : Image
{
    public static readonly DependencyProperty Source2Property = DependencyProperty.Register("Source2", typeof(ImageSource), typeof(MainMenuImageButton), new PropertyMetadata(true));
    public ImageSource Source2 
    {
        get { return (ImageSource)GetValue(Source2Property); }
        set
        {
            BitmapImage logo = new BitmapImage(new Uri(value.ToString(), UriKind.Relative));
            SetValue(Source2Property, logo); 
            this.MouseEnter+=new System.Windows.Input.MouseEventHandler(MainMenuImageButton_MouseEnter);
        }
    }
    public void MainMenuImageButton_MouseEnter(object sender, MouseEventArgs e)
    {
        this.Source = Source2;
    }
}

And still nothing. Wham am I doing wrong?

like image 754
petros Avatar asked Jul 23 '26 11:07

petros


1 Answers

Refer to the Custom Dependency Properties article on MSDN. The event hookup belongs in your dependency property's PropertyChangedCallback.

I would also suggest using a trigger instead of event handling. However, this doesn't mean you will need to duplicate the XAML everywhere you want to use it. You could define a custom control with the image switching trigger in its default style (see "Defining Resources at the Theme Level" in the Control Authoring Overview). Where MouseOverImage is a Control with "Source" and "Source2" dependency properties, you could define this default style:

<Style TargetType="local:MouseOverImage">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:MouseOverImage">
                <Grid>
                    <Image Name="SourceImage" Source="{TemplateBinding Source}" />
                    <Image Name="Source2Image" Source="{TemplateBinding Source2}" Visibility="Hidden" />
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="SourceImage" Property="Visibility" Value="Hidden" />
                        <Setter TargetName="Source2Image" Property="Visibility" Value="Visible" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If you use event handlers, you would need to store the original value of Source, add a MouseLeave handler that reverts it, and also consider the case where a user reassigns Source or Source2 at any time. Using the trigger solution with two separate "Source" and "Source2" bindings, all of this is handled automatically.

EDIT

But sometimes it works and sometimes there is exception: "An unhandled exception of type 'System.ArgumentException' occurred in PresentationCore.dll

Additional information: The value is outside the expected range."

My guess is that HitTestCore is firing after the source changes but before it's applied to the layout, so there is a discrepancy between ActualWidth and source.PixelWidth. I am not sure of the rationale for including these in the calculation (shouldn't they always be the same?) Try just using the following:

var x = (int)hitTestParameters.HitPoint.X;
var y = (int)hitTestParameters.HitPoint.Y; 
like image 171
nmclean Avatar answered Jul 25 '26 23:07

nmclean