tags:

views:

78

answers:

1

When hovering over an item in a list, how can I set a property on another element to the DataContext of the list item?

I'm trying to make an area where I can display a preview of the item currently underneath the mouse cursor. I'm able to do this using code-behind, but I'd like to find an alternative way which could use EventSetters/Binding/Triggers/AttachedProperties or any other means.

The aim is to apply the solution in a more loosely coupled scenario where the ListView control could be in separate resource file and the PreviewControl might be shared by a number of ListViews to show previews of different types.

The following piece of code works, but requires code-behind:

<Window x:Class="Previewer.PreviewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="PreviewWindow" Height="300" Width="300">
<Window.Resources>
    <x:Array x:Key="Data" Type="sys:String">
        <sys:String>First</sys:String>
        <sys:String>Second</sys:String>
    </x:Array>

    <CollectionViewSource x:Key="DataSource" Source="{StaticResource Data}"/>
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <ContentControl Grid.Row="0" x:Name="PreviewControl"/>

    <ListView Grid.Row="1" ItemsSource="{Binding Source={StaticResource DataSource}}">
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <EventSetter Event="MouseEnter" Handler="ListViewItem_MouseEnter"/>
                <EventSetter Event="MouseLeave" Handler="ListViewItem_MouseLeave"/>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>
</Grid>

The code-behind which at the moment does the trick:

public partial class PreviewWindow : Window
{
    public PreviewWindow()
    {
        InitializeComponent();
    }

    private void ListViewItem_MouseEnter(object sender, MouseEventArgs e)
    {
        var listViewItem = (ListViewItem)sender;
        PreviewControl.Content= listViewItem.DataContext;
    }

    private void ListViewItem_MouseLeave(object sender, MouseEventArgs e)
    {
        var listViewItem = (ListViewItem)sender;
        PreviewControl.Content= null;
    }
}
+1  A: 

Solution 1 (not that generic but simple and works):

encapsulate the logic you already implemented into a custom control, which has a new dependency property (typeof(object)), which represents the HoveredItemContext. In the constructor of your custom list view, you can create the ContainerStyle and attach the events. Set the HoveredItemContext in this EventHandlers and you can bind to this property from the outside:

 <ContentControl Grid.Row="0" x:Name="PreviewControl" 
     Content="{Binding ElementName=MyListView, Path=HoveredItemContext}"/>
 <local:MyListView Grid.Row="1" x:Name="MyListView" 
     ItemsSource="{Binding Source={StaticResource DataSource}}" />

And here the custom control (works):

public class MyListView : ListView
{
    public static readonly DependencyProperty HoveredItemContextProperty = DependencyProperty.Register(
        "HoveredItemContext",
        typeof(object),
        typeof(MyListView),
        new PropertyMetadata(null));

    public object HoveredItemContext
    {
        get { return GetValue(HoveredItemContextProperty); }
        set { SetValue(HoveredItemContextProperty, value); }
    }

    public MyListView()
    {
        this.ItemContainerStyle = new Style()
        {
            TargetType = typeof(ListViewItem),
        };

        this.ItemContainerStyle.Setters.Add(new EventSetter(ListViewItem.MouseEnterEvent,
            (MouseEventHandler)((s, e) =>
            {
                this.HoveredItemContext = (s as ListViewItem).DataContext;
            })));

        this.ItemContainerStyle.Setters.Add(new EventSetter(ListViewItem.MouseLeaveEvent,
            (MouseEventHandler)((s, e) =>
            {
                this.HoveredItemContext = null;
            })));
    }
}

Solution 2 (more generic):

I am still working on it on a similar problem but its not yet finished ;) If i bring it to an end, i will post this here.

JanW
PS: Added code for MyListView (tested and works)
JanW
Thanks, I like the idea and think I can work with it. Exposing a dependancy property feels better than trying to push the datacontext onto the preview control from the event handler like the way I did in my example. One could probably also use an attached property without resorting to creating a custom control.
Christo