-3

I have an WPF image control in my view. When image is loaded I want to fire an event that is defined and executed in my model view. How can I do this? I do not know how to do this using a command.

<Image Grid.Row="0" 
       Source="{Binding Path=ImageSrc,  NotifyOnTargetUpdated=True, Converter={StaticResource imgToSrcConverter}}" 
       Visibility="{Binding ImgVisibility}" 
       RenderTransformOrigin="0,0"
       SnapsToDevicePixels="True"
       OverridesDefaultStyle="False"
       TargetUpdated="targetUpdated" 
       Cursor="Hand"
       RenderOptions.BitmapScalingMode="LowQuality"
       RenderOptions.EdgeMode="Aliased">
<Image.Effect>
        <DropShadowEffect Opacity="0.8" BlurRadius="8">                
        </DropShadowEffect>
    </Image.Effect>
</Image>

Converter:

public class ImgToSrcConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
    {
        Image image = value as Image;
        if (image != null)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, image.RawFormat);
            ms.Seek(0, SeekOrigin.Begin);
            BitmapImage bi = new BitmapImage();
            bi.BeginInit();
            bi.StreamSource = ms;
            bi.EndInit();
            return bi;
        }
        return null;
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

View model:

        public Image ImageSrc
        {
            get
            {
                MemoryStream ms = new MemoryStream(GetImageAsByteArray());
                Image img = Image.FromStream(ms);

                // Here I call method to hide splash screen "Loading"

                return img;
            }
        }
Willy
  • 9,848
  • 22
  • 141
  • 284
  • 1
    Did you bind its Source property to a property in your view model? Then you could simply perform an action when the view model property getter is called. – Clemens Oct 05 '17 at 13:53
  • 1
    What is a "model view" ? It pays to be accurate with these terms. – H H Oct 05 '17 at 14:00
  • @Clemens - setting the property and finish loading are not the same thing. – H H Oct 05 '17 at 14:01
  • @HenkHolterman That depends on how the image is actually loaded, e.g. from a web resource or not. – Clemens Oct 05 '17 at 14:02
  • @Clemens Yes I bind Source property to an Image property in the view model. I have tried it in the getter. Basically what i am trying to do is to hide a loading splash screen when image is completely loaded. But what happens is that I call a method just within getter and splash screen is loaded few seconds before image gets loaded. Image gets loaded a few seconds later because the WPF image control, in the Source I set a converter to get an lower image resolution. – Willy Oct 05 '17 at 14:03
  • Why don't you just show us the relevant parts of your code? – Clemens Oct 05 '17 at 14:04
  • @Clemens code provided. – Willy Oct 05 '17 at 14:18
  • @Clemens So what I need is just close the splash screen just when the image has finished to load completely.For this reason I wanted to fire loaded event on image which would call to method in view model. I guess loaded event in image means image has finished loading. – Willy Oct 05 '17 at 14:38
  • @Clemens GetImageAsByteArray basically return an array of bytes byte[] – Willy Oct 06 '17 at 13:51
  • Sure it does, did you try the code from my answer? – Clemens Oct 06 '17 at 13:53

2 Answers2

0

You can load the image in background and call "NotifyPropertyChanged" when the image has been loaded:

private static Image SplashScreenImage = GetSplashScreenImage();
private Image ActualImage = null;
private bool IsLoading = false;
public Image ImageSrc
{
    get
    {
        if (ActualImage != null)
            return ActualImage;

        if (!IsLoading)
        {
            IsLoading = true;
            // start loading image in background
            Task.Run(() =>
            {
                MemoryStream ms = new MemoryStream(GetImageAsByteArray());
                ActualImage = Image.FromStream(ms);
            }).ContinueWith(t => PropertyChanged("ImageSrc"), TaskScheduler.FromCurrentSynchronizationContext());
        }
        return SplashScreenImage;
    }
}
JanDotNet
  • 3,746
  • 21
  • 30
  • I am using NET 3.5 SP1, no Task available. – Willy Oct 06 '17 at 13:51
  • This is not exactly what I want.Also I do not understand why you are returning the splash screen image to the image source. What I am pretending to do is: While I am doing a long task, I show a splash screen showing "Loading...". This long task consists on convert an image from high to low resolution. This is what does GetImageAsByteArray. Then once I have converted the image to a low resolution, I want to close the splash screen.The problem I have is that once OnPropertyChanged("ImageSrc") is called and after the splash screen is closed, the image spents several seconds to be visible..... – Willy Oct 17 '17 at 22:56
  • ...to the user and I want the splash screen to be closed just after iimage has been loaded, rendered and make visible to the user. I do not want to close splash screen before image is visible. So is there an event on wpf image control or some way to just do something (in my case close splash screen) just after image is visible to the user? – Willy Oct 17 '17 at 22:59
-1

If you want to invoke a command when the Loaded event for the Image element fires, you could use an interaction trigger:

<Image Grid.Row="0" 
           Source="{Binding Path=ImageSrc,  NotifyOnTargetUpdated=True, Converter={StaticResource imgToSrcConverter}}" 
           Visibility="{Binding ImgVisibility}" 
           RenderTransformOrigin="0,0"
           SnapsToDevicePixels="True"
           OverridesDefaultStyle="False"
           TargetUpdated="targetUpdated" 
           Cursor="Hand"
           RenderOptions.BitmapScalingMode="LowQuality"
           RenderOptions.EdgeMode="Aliased"
           xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding YourCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Image.Effect>
        <DropShadowEffect Opacity="0.8" BlurRadius="8">
        </DropShadowEffect>
    </Image.Effect>
</Image>

Please refer to the following blog post for more information about how to handle events by executing view model commands: https://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/.

Another option may be to simply execute the command from the setter of your source property:

public Image ImageSrc
{
    get
    {
        MemoryStream ms = new MemoryStream(GetImageAsByteArray());
        Image img = Image.FromStream(ms);

        YourCommandProperty.Execute(null);

        return img;
    }
}
mm8
  • 163,881
  • 10
  • 57
  • 88