I am using Entity Framework Code First
I have a Movie like so:
public class Movie
{
public byte[] Thumbnail { get; set; }
I was having issues with the answer when applied to a ListBox whose ItemsSource is dynamic. In that case, when the source is modified, the ScrollViewer is not necessarily, the trigger is not fired and the images are not loaded.
My main issue concerns the lazy loading lots of highres images in a UniformGrid (which is not virtualized).
To overcome this, I applied a Behavior on ListBoxItem. I find it is a good solution too because you do not have to subclass the ScrollViewer and change the ListBox's Template but only ListBoxItem's.
Add a behavior to your project :
namespace behaviors
{
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
public class ListBoxItemIsVisibleBehavior : Behavior
{
public static readonly DependencyProperty IsInViewportProperty = DependencyProperty.RegisterAttached("IsInViewport", typeof(bool), typeof(ListBoxItemIsVisibleBehavior));
public static bool GetIsInViewport(UIElement element)
{
return (bool)element.GetValue(IsInViewportProperty);
}
public static void SetIsInViewport(UIElement element, bool value)
{
element.SetValue(IsInViewportProperty, value);
}
protected override void OnAttached()
{
base.OnAttached();
try
{
this.AssociatedObject.LayoutUpdated += this.AssociatedObject_LayoutUpdated;
}
catch { }
}
protected override void OnDetaching()
{
try
{
this.AssociatedObject.LayoutUpdated -= this.AssociatedObject_LayoutUpdated;
}
catch { }
base.OnDetaching();
}
private void AssociatedObject_LayoutUpdated(object sender, System.EventArgs e)
{
if (this.AssociatedObject.IsVisible == false)
{
SetIsInViewport(this.AssociatedObject, false);
return;
}
var container = WpfExtensions.FindParent(this.AssociatedObject);
if (container == null)
{
return;
}
var visible = this.IsVisibleToUser(this.AssociatedObject, container) == true;
SetIsInViewport(this.AssociatedObject, visible);
}
private bool IsVisibleToUser(FrameworkElement element, FrameworkElement container)
{
if (element.IsVisible == false)
{
return false;
}
GeneralTransform transform = element.TransformToAncestor(container);
Rect bounds = transform.TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
Rect viewport = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
return viewport.IntersectsWith(bounds);
}
}
}
Then you would have to use this answer in order to add a behavior to your ListBoxItem style : How to add a Blend Behavior in a Style Setter
This leads to add a helper in your project :
public class Behaviors : List
{
}
public static class SupplementaryInteraction
{
public static Behaviors GetBehaviors(DependencyObject obj)
{
return (Behaviors)obj.GetValue(BehaviorsProperty);
}
public static void SetBehaviors(DependencyObject obj, Behaviors value)
{
obj.SetValue(BehaviorsProperty, value);
}
public static readonly DependencyProperty BehaviorsProperty =
DependencyProperty.RegisterAttached("Behaviors", typeof(Behaviors), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyBehaviorsChanged));
private static void OnPropertyBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviors = Interaction.GetBehaviors(d);
foreach (var behavior in e.NewValue as Behaviors) behaviors.Add(behavior);
}
}
Then add the behavior in a resource somewhere in your control :
And a reference of this resource and a trigger to the style of your ListBoxItem :
And reference the style in your ListBox :
In the case of a UniformGrid :