双击 ListBox 中的项以打开浏览器

43
我在我的WPF窗口中有一个绑定到ObservableCollection的ListBox。我想要在用户点击ListBox元素时打开浏览器(就像链接一样)。请问有人可以告诉我如何实现吗?我发现了一些关于listboxviews的东西,它只能用这种方式工作吗?还是只使用ListBox也有其他方法?
您的
Sebastian
5个回答

86

你可以为 ListBox.ItemContainerStyle 添加样式,并在其中添加一个 EventSetter

<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 是您代码后台中正确签名的方法,用于处理 MouseDoubleClick 事件。


1
谢谢,这也解决了我的问题。跟进一个问题:在运行应用程序时,它很好用,但它会破坏Visual Studio设计工具(不是致命的问题,但有点烦人)。我没有在应用程序资源中明确定义ListBoxItem样式,因此BasedOn会在设计时间出错。不过,我有一个主题库(WPFTheme),可以在运行时定义这个资源。如果我定义了一个静态资源,它就会覆盖动态的、带主题的资源。您有什么想法可以让两者友好共存吗? - el2iot2
1
经过更多的尝试,我弄清楚了...我只是在合并资源属性中引用了主题的xaml:<ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Theme.xaml"/> </ResourceDictionary.MergedDictionaries> - el2iot2
这很酷,但如果您已经在ResourceDictionary中定义了ItemContainerStyle,该怎么办? - Oliver
1
只需在<Style>标签中更改为BasedOn = "{StaticResource YourResource的名称}",即可完成修改。 - Bob King
2
@Bob 对于缺少双击的简单解决方案加1。 - Metro Smurf
我在想,你们知道我怎样将这个事件绑定到我的ViewModel上的命令吗? - Mert Susur

10
我希望在不需要在代码后台处理listBoxItem双击事件的情况下解决这个问题,我也不想覆盖listBoxItem样式(或者首先定义要覆盖的样式)。 我只想在双击listBox时触发一个命令。 我创建了一个类似的附加属性(代码非常具体,但您可以根据需要进行概括):
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);
    }
}

然后我使用附加属性和目标命令声明了我的ListBox:

<ListBox ItemsSource="{Binding SomeItems}" myStuff:ControlItemDoubleClick.ItemsDoubleClick="true">
    <ListBox.InputBindings>
        <MouseBinding MouseAction="LeftDoubleClick" Command="MyCommand"/>
    </ListBox.InputBindings>
</ListBox>
希望这能帮到你。

但如果他们双击滚动条,那不会触发吗?或者在ListBoxItems之间有任何填充吗? - Bob King
是的,如果你在列表框的任何地方双击,它将触发。 对我来说,这仍然是一个很好的解决方案,我喜欢它不需要代码后台。 将b.Command.CanExecute(null)和Execute(null)的命令参数从null更改为b.CommandParameter。 - HeWillem

6

我已经更新了AndrewS的解决方案,以解决在列表框中双击任何地方时触发执行命令的问题:

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;
        }
    }
}

在 XAML 中,ListBox 的声明如下:

<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>

1
这是这里最佳的答案! - JokerMartini
1
这是我在尝试了几个选项后独立选择的解决方案,但我稍微改变了绑定方式:我给UserControl一个Name,移除了RelativeSource搜索,并用我给控件的名称添加了一个ElementName。结果是相同的,但对于那些有很多对该控件的引用的人来说,可能会节省一些空间/运行时间。 - Dan

5

我使用了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.


如果将此附加到列表框,则会在双击列表框的任何部分(包括长列表中的滚动条)时触发,这非常令人讨厌。而且,列表框项似乎没有鼠标双击事件。 - Sinaesthetic

0

这是一个可以在 ListBoxListView 上实现的行为。这个方法基于 Andrew S. 和 Vadim Tofan 的答案,他们做得很好!

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
}

这是用于查找父级的扩展类。
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);
    }
}

使用方法:

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>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接