tags:

views:

465

answers:

2

I can trigger property settings on my ListBoxItem template based on properties of underlying data object using DataTrigger with something like this

<DataTrigger Binding="{Binding Path=IsMouseOver}" Value="true">
  <Setter TargetName="ItemText" Property="TextBlock.TextDecorations" Value="Underline">
  </Setter>
</DataTrigger>

But what if I want to do the opposite? I mean set a property value on the underlying data object based on property value of my ListBoxItem. Something like:

<Trigger Property="IsMouseOver" Value="True">
  <Setter Property="MyClass.IsHilited" Value="True"></Setter>
</Trigger>

Is there a mechanism for something like this or what is the recommended approach to deal with situations like this?

Thanks.

+1  A: 

WPF triggers are intended for causing visual changes. Setter objects within triggers cause property changes on the control.

If you want to respond to an event (like an EventTrigger), you could always simply subscribe to the event in code and then set the data property in the handler.

You could use MouseEnter and MouseLeave in this way. For example:

listBox.MouseEnter += listBox_MouseEnter;
listBox.MouseLeave += listBox_MouseLeave;

void listBox_MouseEnter(object sender, MouseEventArgs e)
{
    listBox.MyClass.IsHilited = true;
}

void listBox_MouseLeave(object sender, MouseEventArgs e)
{
    listBox.MyClass.IsHilited = false;
}

Some properties on a control you could bind the property of the data object to, like so:

Binding myBind = new Binding("IsHilited");
myBind.Source = listBox.DataContext;
listBox.SetBinding(listBox.IsEnabled, myBind);

You can't use IsMouseOver in a binding, however.

If you create a custom control you can have even greater flexibility to build a binding like this into the control. You could create a custom depency property and sync it with the data property in the DependencyPropertyChanged handler. You could then set this dependency property with a WPF trigger.

Here's an example:

public static readonly DependencyProperty IsHilitedProperty =
    DependencyProperty.Register("IsHilited", typeof(bool), typeof(CustomListBox),
    new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsHilitedChanged)));

public double IsHilited
{
    get
    {
        return (bool)GetValue(IsHilitedProperty);
    }
    set
    {
        SetValue(IsHilitedProperty, value);
    }
}


private static void OnIsHilitedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    CustomListBox box = obj as CustomListBox;

    if (box != null)
        box.MyClass.IsHilited = box.IsHilited;

   // Or:
   // Class myClass = box.DataContext as Class;
   // myClass.IsHilited = box.isHilited;
}

<Trigger Property="IsMouseOver" Value="True">
    <Setter Property="IsHilited" Value="True"/>
</Trigger>
Josh G
Thanks. My problem is that I can't understand how do I wire the event handlers to the MouseEnter/MouseLeave events of the child items of the ListBox or determine which item is under the cursor (if any) in the top-level MouseEnter/MouseLeave events of the ListBox?
Alan Mendelevich
Responding to events on ListBoxItems is a different problem. I would suggest creating an item template for the items in the ListBox. You can then create a trigger for IsMouseOver in the ItemTemplate.
Josh G
+2  A: 

I think that you could use an EventSetter to do in XAML what Josh G suggested in code. Maybe create one for the MouseEnter and MouseLeave events, and style the control appropriately for each?

Update: You can set up the events like this:

<ListBox>
    <ListBox.Resources>
        <Style TargetType="{x:Type ListBoxItem}">
            <EventSetter Event="MouseEnter" Handler="OnListBoxItemMouseEnter" />
            <EventSetter Event="MouseLeave" Handler="OnListBoxItemMouseLeave" />
        </Style>
    </ListBox.Resources>
    <ListBoxItem>Item 1</ListBoxItem>
    <ListBoxItem>Item 2</ListBoxItem>
    <ListBoxItem>Item 3</ListBoxItem>
    <ListBoxItem>Item 4</ListBoxItem>
    <ListBoxItem>Item 5</ListBoxItem>
</ListBox>

The Style registers for the MouseEnter and MouseLeave events for all ListBoxItems defined in that ListBox.

And then in your code behind file:

private void OnListBoxItemMouseEnter(object sender, RoutedEventArgs e)
{
    ListBoxItem lvi = sender as ListBoxItem;
    if(null != lvi)
    {
        lvi.Foreground = new SolidColorBrush(Colors.Red);
    }
}

private void OnListBoxItemMouseLeave(object sender, RoutedEventArgs e)
{
    ListBoxItem lvi = sender as ListBoxItem;
    if(null != lvi)
    {
        lvi.Foreground = new SolidColorBrush(Colors.Black);
    }
}

These handlers set the color of the text of the ListBoxItem to red when the mouse is over the item, and back to black when the mouse leaves it.

Andy
Yes, I tried to do that but I can't wrap my head around the problem where to place these setters (or how to wire events to the right place for that matter). I need the MouseEnter/Leave events for individual items (not the listbox) or I need a way to determine the hovered item from listbox's handler
Alan Mendelevich
Yes, it works. Thanks!
Alan Mendelevich
Andy, do you know how to do the same in code? I mean setting listBox.AddHandler(ListBoxItem.MouseEnterEvent...) results in event firing on the whole legend. Is there a way to attach event on all items at once or am I stuck with iterating through items and setting them one by one?
Alan Mendelevich
The only other option I can think of would be to register a ClassHandler (call EventManager.RegsiterClassHandler(), passing typeof(ListBoxItem) and the event), but then that would fire for all ListBoxItems in your app, not just those in a specific ListBox.
Andy
Found it. Nevermind.
Alan Mendelevich
Here's a way to do this programmaticallyhttp://www.mohundro.com/blog/2008/11/19/ProgrammaticallyAddingEventSettersInWPF.aspx
Alan Mendelevich
That would be a much better approach. Good find. :)
Andy