Mirza Bilal
Mirza Bilal

Reputation: 1050

Binding Images Url Array to Custom Image Control Property

I need to change images on mouseover after x amount of time, if there are multiple images are present in the list.

To achieve this i have developed a custom image control, and my control has two properties basically MainImageSource which is a BitmapImage and ImageUrl which is a IList.

Now what am i doing here is onmouseenter starts a timer and change MainImageSource to the next image in the list (if there is any) after timer hass elapsed and reseting image onmouseleave event.

Everything's working fine, like when i set the imageurls it setups the images as expected.

Now i need to use this control in ListBox which is data bind to ObservableCollection of type FileItem (custom class). Now in listbox i need to setup that array so what i did was, add a property in FileItem called Imageurls of type string[]. In xaml i used the following to data bind.

<local:MBImage ImageUrl="{Binding ImageUrls}"  Source="{Binding MainImageSource}" />

But ImageUrl of MBImage was never set.

So now there are two things

  1. How can i bind ImageUrls of FileItem with ImageUrl of MBImage.
  2. How can i get rid of Source="{Binding MainImageSource}". As it is used internally within MBImage. And if i dont use MainImageSource Property it does not change the image even if i updated MBImage Source.

Add to everything is there a better way to achieve my main objective which was to show multiple images on hover. I am willing to rewrite everything if i find a better approach than this and which uses more of WPF's strengths.

Edited

This is the error

'ImageUrls' property not found on 'object' ''MBImage' (Name='')'

Which shows its looking for ImageUrls in MBImage rather than listbox itemsource of ObservableCollection type.

MBImage.cs

class MBImage : Image, INotifyPropertyChanged
{
    private List<BitmapImage> bmpImages = new List<BitmapImage>();
    private Timer imgChangeTimer = new Timer(1000);
    private int curImage = 0;

    BitmapImage _MainImageSource = null;
    public event PropertyChangedEventHandler PropertyChanged;

    public static readonly DependencyProperty ImageUrlProperty = DependencyProperty.Register("ImageUrl", typeof(IList<string>), typeof(MBImage));

    public IList<string> ImageUrl
    {
        get { return (IList<string>)this.GetValue(ImageUrlProperty); }
        set
        {                
            this.SetValue(ImageUrlProperty, value);
            DownloadImages();
        }
    }

    public MBImage()
    {
        imgChangeTimer.Elapsed += new ElapsedEventHandler(imgChangeElapsed);
        DataContext = this;
    }

    public BitmapImage MainImageSource
    {
        get
        {
            return _MainImageSource;
        }
        set
        {
            _MainImageSource = value;
            OnPropertyChanged("MainImageSource");
        }
    }

    private void DownloadImages()
    {
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += (s, e) =>
        {
            List<ImageDownloadArgs> argList = new List<ImageDownloadArgs>();

            int index = (int)e.Argument;
            int count = 1;
            if (index == -1)
            {
                count = ImageUrl.Count();
                index = 1;
            }

            for (int i = index; i < count; i++)
            {
                ImageDownloadArgs args = new ImageDownloadArgs();
                args.Index = i;


                Uri uri = new Uri(ImageUrl[i], UriKind.Absolute);

                using (WebClient webClient = new WebClient())
                {
                    webClient.Proxy = null;  
                    webClient.CachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
                    try
                    {
                        byte[] imageBytes = null;

                        imageBytes = webClient.DownloadData(uri);

                        if (imageBytes == null)
                        {
                            e.Result = null;
                            return;
                        }
                        MemoryStream imageStream = new MemoryStream(imageBytes);
                        args.Img = new BitmapImage();

                        args.Img.BeginInit();
                        args.Img.StreamSource = imageStream;
                        args.Img.CacheOption = BitmapCacheOption.OnLoad;
                        args.Img.EndInit();

                        args.Img.Freeze();
                        imageStream.Close();

                        argList.Add(args);

                    }
                    catch (WebException)
                    {                
                        //argList.Add(args);
                    }
                }
            }
            e.Result = argList;
        };

        worker.RunWorkerCompleted += (s, e) =>
        {
            List<ImageDownloadArgs> argList = e.Result as List<ImageDownloadArgs>;
            bool shouldDownloadRest = false;
            foreach (ImageDownloadArgs args in argList)
            {
                if (args != null)
                {
                    if (args.Index == 0)
                    {
                        MainImageSource = args.Img;
                        bmpImages.Add(args.Img);
                        shouldDownloadRest = true;
                    }
                    else
                    {
                        bmpImages.Add(args.Img);
                    }
                }
            }

            worker.Dispose();
            if (shouldDownloadRest)
                worker.RunWorkerAsync(-1);

        };

        worker.RunWorkerAsync(0);

    }

    private void imgChangeElapsed(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine("Time out");
        MainImageSource = bmpImages[++curImage % bmpImages.Count()];

    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
    {
        imgChangeTimer.Start();
    }

    protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
    {
        imgChangeTimer.Stop();
        MainImageSource = bmpImages[0];
        curImage = 0;

    }
}

Upvotes: 0

Views: 588

Answers (1)

Nitin Purohit
Nitin Purohit

Reputation: 18580

The problem is that you are Setting the DataContext of MBImage to itself in its constructor DataContext = this;, which you should not be doing. That is the reason it is searching in itself for properties when you do ImageUrl="{Binding ImageUrls}"

Remove DataContext = this; this line as it is not required.

Also I see that you are doing some work in the property setter, this will not be called from the Xaml bindings as wpf internally calls GetValue and SetValue functions.. if you want to do somethign on property value set, register the ValueChanged callback in the dependency property metadata while defining it and do that work in the valuechanged handler

public static readonly DependencyProperty ImageUrlProperty = DependencyProperty.Register("ImageUrl", typeof(IList<string>), typeof(MBImage), new FrameworkPropertyMetadata(null, ImageUrlsChanged));

Upvotes: 1

Related Questions