2

I have an WPF image and I have subscribed to some events:

<Image Grid.Row="0" 
       Source="{Binding Path=ImageSelected,  NotifyOnTargetUpdated=True, Converter={StaticResource imageToSourceConverter}}" 
       Visibility="{Binding imageVisibility}" 
       RenderTransformOrigin="0,0" 
       SnapsToDevicePixels="True" 
       MouseLeftButtonDown="myImage_MouseLeftButtonDown" 
       MouseLeftButtonUp="myImage_MouseLeftButtonUp" 
       MouseMove="myImage_MouseMove" 
       OverridesDefaultStyle="False"
       TargetUpdated="myImage_TargetUpdated"
       Cursor="Hand"
       RenderOptions.BitmapScalingMode="LowQuality" 
       RenderOptions.EdgeMode="Aliased" 
       Loaded="myImage_Loaded">

I have noticed that all events are fired except the Loaded event and I do not understand why. I do not know if it is conflicting with some other events. Which is the sequence of events fired in an image?

Any ideas why it is happening?

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
Willy
  • 9,848
  • 22
  • 141
  • 284
  • Is that consistent? So for example if you just have `` - still does not fire? – Evk Oct 17 '17 at 13:56
  • @Evk I have noticed that when app is launched for first time, it is fired, but once app launched, the rest of times is not being fired. I have not tried to only put loaded event. – Willy Oct 17 '17 at 14:00
  • 1
    And why should it fire multiple times? Is it getting unloaded? – Evk Oct 17 '17 at 14:01
  • @Evk because I am updating the image in it using the source property so I want to do some things after image on the image control has been completely rendered/loaded, visible and ready for the user to interact. – Willy Oct 17 '17 at 14:03
  • 2
    `Loaded` event is not about that. It fires only once when (basically) control appears on the screen. – Evk Oct 17 '17 at 14:05
  • @Evk ahhh so I did not understand well.... and what event is the correct to do what i want? layoutUpdated? sourceupdated? – Willy Oct 17 '17 at 14:06
  • I think you should test them all and see yourself, I'm not sure which will fit your needs. I guess that LayoutUpdated should do (but check if it fires when you change image to another one with the same size, because in that case I guess layout is not updated). – Evk Oct 17 '17 at 14:08
  • [`Image.Source`](https://msdn.microsoft.com/en-us/library/system.windows.controls.image.source(v=vs.110).aspx) is dependency property, you can [monitor changes](https://stackoverflow.com/q/4764916/1997232) as with any `propdp`. – Sinatr Oct 17 '17 at 14:12
  • 1
    The Binding.SourceUpdate event should be rasied whenever the source property (ImageSelected) is set to a new value provided that you set the NotifyOnSourceUpdated property of the binding to true: `Source="{Binding Path=ImageSelected, NotifyOnTargetUpdated=True, Converter={StaticResource imageToSourceConverter}, NotifyOnSourceUpdated=true}"`. – mm8 Oct 17 '17 at 14:13

1 Answers1

1

What you are experiencing is the intended behavior for that event.

The Loaded event:

Occurs when the element is laid out, rendered, and ready for interaction.

We are talking about a control event. When the control, not the image you load into it, is laid out, rendered and ready for interaction this event will be fired, once.

This is not the right event, if you are looking for one that "tells" you when the image itself is loaded.

DownloadCompleted

If that's what you need, and the images you display are not locally available, but are downloaded over HTTP, you can use the DownloadCompleted event. It is provided by the BitmapSource class. It would require you to bind your Image control to a BitmapSource, instead of providing and Uri, as I suspect is the case right now.

Custom code

The only alternative I know of is to do this manually, which usually gives you also more flexibility. A sample could be the following (untested code):

private void UpdateImageFromBuffer(byte[] yourBuffer)
{
    ThreadPool.QueueUserWorkItem(delegate {
        try {

            SelectedImageLoaded = false; // Set the notification Property and notify that image is being loaded.

            using (MemoryStream memoryStream = new MemoryStream(yourBuffer)) // Remember to provide the yourBuffer variable.
            {
                var imageSource = new BitmapImage();
                imageSource.BeginInit();
                imageSource.StreamSource = memoryStream;
                imageSource.EndInit();
                ImageSelected = imageSource; // Assign ImageSource to your ImageSelected Property.
            }

        } catch (Exception ex) {
            /* You might want to catch this */
        } finally {
            SelectedImageLoaded = true; // Notify that image has been loaded
        }
    });
}

First of all, move the loading of the image to another thread, it's unlikely you want to do this on the UI thread anyway. Depending on what you need to do with that "image-loaded-notification" you need to adapt the code above.

Let's say you want to update the UI depending on what is happening, say display a progress bar or loading animation. In such a case the code above sets the SelectedImageLoaded Property to the current state of the image. All you need to do is to properly bind your UI control to that Property to get the UI to update (Note: class has to implement INotifyPropertyChanged).

Hope that helps.

r41n
  • 908
  • 7
  • 18
  • Well, in my case, in ImageSelected property what I do is create a MemoryStream from a byte array byte[] and finally I return Image.FromStream(MyMemoryStream). This image is in high resolution so from the time I update ImageSelected property and perform OnPropertyChanged until the image is loaded, rendered and visible to the user it spent some seconds so I was trying to find an event that tells me when image is completely visible to the user but I do not know how to do it. Any ideas? I guess that image spent some seconds to be visible due to its high resolution. – Willy Oct 17 '17 at 15:41
  • 1
    @user1624552 Do not use `Image.FromStream` and then convert a WinForms Image to a WPF BitmapSource. Instead, create a BitmapImage or BitmapFrame directly from the MemoryStream. – Clemens Oct 17 '17 at 16:34
  • @user1624552, it really comes down to how your application operates and what you want to achieve. Since this is WPF, I'd add/update a separate Property to reflect the "Loaded" state of the Image (allows binding, to do UI stuff). Move the loading of the image to a new function that uses a worker thread (ThreadPool). Inside that function set the Loaded property to false, load the image and set ImageSelected, finally set Loaded to true. At that point, all you have to do is call that Update function to load the image and give you status notifications through the Loaded property. – r41n Oct 18 '17 at 06:18
  • I updated my answer, hopefully it'll point you in the right direction. – r41n Oct 18 '17 at 06:45