Reputation: 1050
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
ImageUrls
of FileItem
with ImageUrl
of MBImage
.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
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