WPF – Hovering over one Element shows content in another Element

wpf

I would like to achieve an affect whereby I can hover over a Button and have a TextBlock update its content (via binding). To complicate matters, The Button is one of many buttons defined in an ItemsControl/DataTemplate. The TextBlock is outside the scope of the ItemsControl.

Some simplified markup of the problem is as follows:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <ItemsControl Grid.Row="0">
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <Button Content="{Binding Title}"
                Command="{Binding Command}" />    
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
  <TextBlock x:Name="TitleTextBox" Grid.Row="1" />
</Grid>

Say in this example, I may want to bind the "Title" property of the data item to the TextBlock's "Text" property.

I assume I want to be tapping into the IsMouseOver of the button, but I can't seem to get it hooked up properly.

Best Answer

I don't believe you're going to be able to accomplish this without some code... however, you don't need to use codebehind. A "behavior" would work just fine for this (either an old school attached behavior, or a more modern Blend Behavior). Here's what the modified markup might look like using an attached behavior:

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>
  <ItemsControl x:Name="Buttons" Grid.Row="0" ext:Hover.TrackChildItems="true">
    <ItemsControl.ItemTemplate>
      <DataTemplate>
        <Button Content="{Binding Title}"
                Command="{Binding Command}" />
      </DataTemplate>
    </ItemsControl.ItemTemplate>
  </ItemsControl>
  <TextBlock x:Name="TitleTextBox"
             Grid.Row="1"
             Text="{Binding ElementName=Buttons, Path=ext:Hover.Item.Content}" />
</Grid>

Here the attached behavor "Hover.TrackChildItems" is used to watch for the appropriate mouse events, and sets the readonly "Hover.Item" attached property to the control being hovered over. Writing the behavior should be simple enough, and is left to you.

Edit: To set up the event handlers for the items, the first thing to do is simple and obvious: iterate over the items in the Items property and add the handlers.

Mouse.AddMouseEnter(item, OnMouseEnter);

This works fine for static content, but dynamic (added and removed at runtime) content will be missed by this. So, next you must track changes to the Items property.

((INotifyCollectionChanged)Items).CollectionChanged += OnItemsChanged;

Add or remove the mouse event handlers as appropriate when items are added and removed in the CollectionChanged handler.

There's also another solution. Create a new HoverTrackingItemsControl for this, derived from ItemsControl. In this control override the GetContainerForItemOverride method to return a new HoverTrackingItem, which handles MouseEnter/MouseLeave to notify the parent HoverTrackingItemsControl. This solution is probably easier to implement, though it does require a specialized control, while the Behavior solution is more generic and can be used with any ItemsControl type (ItemsControl, ListBox, ComboBox, etc.).

Related Topic