Double Click a ListBox item to open a browser
Asked Answered
T

5

43

I have a ListBox in my wpf window that binds to an ObervableCollection. I want to open the browser if someone clicks on an element of the ListBox (just like a link). Can someone tell me how to do this? I found something with listboxviews, does it only work this way or is there a way by just using the ListBox?

Yours

Sebastian

Tomtit answered 4/5, 2009 at 19:25 Comment(0)
O
86

You can add a style to ListBox.ItemContainerStyle, and add an EventSetter there:

<ListBox>
    ....
    <ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
            <EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
        </Style>
    </ListBox.ItemContainerStyle>
</ListBox>

ListBoxItem_MouseDoubleClick is a method in your code behind with the correct signature for MouseDoubleClick.

Outcast answered 4/5, 2009 at 19:33 Comment(6)
Thanks. This solves my problem too. One followup: This works great in the running application, but it hoses the visual studio designer (not a showstopper, but an irritation). I don't explicitly define a ListBoxItem style in the application resources, so the BasedOn will fail at design time. However, I have a theming library (WPFTheme) that defines this resource at runtime. If i do define a static resource, it obliterates the dynamic, themed one. Any thought on how I can make both play nice together?Disfranchise
After a bit more experimentation, I figured it out...I just referenced the theme's xaml in a mergedresource property: <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Theme.xaml"/> </ResourceDictionary.MergedDictionaries>Disfranchise
This is pretty cool but what if you have already defined ItemContainerStyle in a ResourceDictionary?Gluttonize
Just change to BasedOn="{StaticResource TheNameOfYourResource}" in the <Style> tag.Outcast
@Bob +1 for an easy solution for the lack of a double click.Spheno
I was wondering, do you guys know how may I bind this event to a Command on my ViewModel?Fernandez
J
10

I wanted to solve this without needing to handle the listBoxItem double click event in the code-behind, and I didn't want to have to override the listBoxItem style (or define the style to override in the first place). I wanted to just fire a command when the listBox was doubleclicked.

I created an attached property like so (the code is very specific, but you can generalise it as required):

public class ControlItemDoubleClick : DependencyObject
{
    public ControlItemDoubleClick()
    {
    }

    public static readonly DependencyProperty ItemsDoubleClickProperty = DependencyProperty.RegisterAttached("ItemsDoubleClick", typeof(bool), typeof(Binding));

    public static void SetItemsDoubleClick(ItemsControl element, bool value)
    {
        element.SetValue(ItemsDoubleClickProperty, value);

        if (value)
        {
            element.PreviewMouseDoubleClick += new MouseButtonEventHandler(element_PreviewMouseDoubleClick);
        }
    }

    static void element_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var control = sender as ItemsControl;

        foreach (InputBinding b in control.InputBindings)
        {
            if (!(b is MouseBinding))
            {
                continue;
            }

            if (b.Gesture != null
                && b.Gesture is MouseGesture
                && ((MouseGesture)b.Gesture).MouseAction == MouseAction.LeftDoubleClick
                && b.Command.CanExecute(null))
            {
                b.Command.Execute(b.CommandParameter);
                e.Handled = true;
            }
        }
    }

    public static bool GetItemsDoubleClick(ItemsControl element)
    {
        return (bool)element.GetValue(ItemsDoubleClickProperty);
    }
}

I then declare my ListBox with the attached property and my target command:

<ListBox ItemsSource="{Binding SomeItems}" myStuff:ControlItemDoubleClick.ItemsDoubleClick="true">
    <ListBox.InputBindings>
        <MouseBinding MouseAction="LeftDoubleClick" Command="MyCommand"/>
    </ListBox.InputBindings>
</ListBox>

Hope this helps.

Julissa answered 19/5, 2009 at 14:45 Comment(2)
but won't that fire if they double click on the scroll bar? Or any padding between ListBoxItems?Outcast
Yes, it will fire if you double click anywhere in the list box. Still for me it is a good solution, I like it that it does not take code behind. Changed the command parameters for b.Command.CanExecute(null) and Execute(null) from null to b.CommandParameter.Indiscretion
V
6

I have updated AndrewS solution in order to solve the issue with firing executing the command if double click anywhere in the list box:

public class ControlDoubleClick : DependencyObject
{
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(ControlDoubleClick), new PropertyMetadata(OnChangedCommand));

    public static ICommand GetCommand(Control target)
    {
        return (ICommand)target.GetValue(CommandProperty);
    }

    public static void SetCommand(Control target, ICommand value)
    {
        target.SetValue(CommandProperty, value);
    }

    private static void OnChangedCommand(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Control control = d as Control;
        control.PreviewMouseDoubleClick += new MouseButtonEventHandler(Element_PreviewMouseDoubleClick);
    }

    private static void Element_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        Control control = sender as Control;
        ICommand command = GetCommand(control);

        if (command.CanExecute(null))
        {
            command.Execute(null);
            e.Handled = true;
        }
    }
}

And in the XAML the declaration for the ListBox is:

<ListBox ItemsSource="{Binding MyItemsSource, Mode=OneWay}">                    
      <ListBox.ItemContainerStyle>
                    <Style>                            
                        <Setter Property="behaviours:ControlDoubleClick.Command" Value="{Binding DataContext.MyCommand,
                                    RelativeSource={RelativeSource FindAncestor, 
                                    AncestorType={x:Type UserControl}}}"/>
                     </Style>  
     </ListBox.ItemContainerStyle>
</ListBox>
Viscountcy answered 10/10, 2014 at 14:7 Comment(2)
best answer on here!Exercise
This was the solution I chose independently after a trying a few options, but I changed the binding slightly: I gave the UserControl a Name, removed the RelativeSource search, and added an ElementName in its place with the name I gave the control. The outcome is the same, but it might save some space/runtime for people who have a lot of references to the control.Stanford
S
5

I Used Expression SDK 4.0

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

<i:Interaction.Triggers>
  <i:EventTrigger EventName="MouseDoubleClick" SourceName="CaravanasListBox">
     <i:InvokeCommandAction Command="{Binding AccionesToolbarCommand}" CommandParameter="{x:Static local:OpcionesBarra.MostrarDetalle}" />
   </i:EventTrigger>
</i:Interaction.Triggers>

Jaimir G.

Spillman answered 5/11, 2012 at 15:4 Comment(1)
If this is attached to the listbox, then it will fire if you double click any part of the listbox, including the scroll bar which is amazing annoying with long lists. And the listboxitem doesn't seem to have a MouseDoubleClick event.Fullerton
M
0

Here's a behavior that gets that done on both ListBox and ListView. This is based of answers from Andrew S. and Vadim Tofan, great job guys!

public class ItemDoubleClickBehavior : Behavior<ListBox>
{
    #region Properties
    MouseButtonEventHandler Handler;
    #endregion

    #region Methods

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.PreviewMouseDoubleClick += Handler = (s, e) =>
        {
            e.Handled = true;
            if (!(e.OriginalSource is DependencyObject source)) return;

            ListBoxItem sourceItem = source is ListBoxItem ? (ListBoxItem)source : 
                source.FindParent<ListBoxItem>();

            if (sourceItem == null) return;

            foreach (var binding in AssociatedObject.InputBindings.OfType<MouseBinding>())
            {
                if (binding.MouseAction != MouseAction.LeftDoubleClick) continue;

                ICommand command = binding.Command;
                object parameter = binding.CommandParameter;

                if (command.CanExecute(parameter))
                    command.Execute(parameter);
            }
        };
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewMouseDoubleClick -= Handler;
    }

    #endregion
}

Here's the extension class used to find the parent.

public static class UIHelper
{
    public static T FindParent<T>(this DependencyObject child, bool debug = false) where T : DependencyObject
    {
        DependencyObject parentObject = VisualTreeHelper.GetParent(child);

        //we've reached the end of the tree
        if (parentObject == null) return null;

        //check if the parent matches the type we're looking for
        if (parentObject is T parent)
            return parent;
        else
            return FindParent<T>(parentObject);
    }
}

Usage:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:coreBehaviors="{{Your Behavior Namespace}}"


<ListView AllowDrop="True" ItemsSource="{Binding Data}">
    <i:Interaction.Behaviors>
       <coreBehaviors:ItemDoubleClickBehavior/>
    </i:Interaction.Behaviors>

    <ListBox.InputBindings>
       <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding YourCommand}"/>
    </ListBox.InputBindings>
</ListView>
Mercedes answered 18/5, 2019 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.