为啥我的自定义控件不总是接收 MouseEnter 事件?

Posted

技术标签:

【中文标题】为啥我的自定义控件不总是接收 MouseEnter 事件?【英文标题】:Why are my custom controls not always receiving MouseEnter events?为什么我的自定义控件不总是接收 MouseEnter 事件? 【发布时间】:2019-05-21 06:04:16 【问题描述】:

好的,我对 WPF 还很陌生,遇到了一个非常奇怪的问题。我的 XAML 的相关部分在 StackPanel 周围的 ScrollViewer 周围定义了一个边框,该边框使用 ItemsControl 填充,然后数据绑定到 CollectionViewSource,而 CollectionViewSource 又包装了标准的 ObservableCollection。 ItemsControl 定义了一个仅包含一个标记的 DataTemplate:我制作的一个自定义控件,称为 StackElement。我正在处理来自这个控件的三个事件——MouseEnter、MouseLeave 和 PreviewMouseLeftButtonUp。这些事件可以触发,但不可靠。

例如,在添加了一些新的 StackElement 之后,MouseEnter 事件通常不会在 first StackElement 上触发,直到我将鼠标悬停在其他几个 StackElement 上。一旦 MouseOver 成功触发一次,它就会继续在 StackElement 上正确触发。

但是,第一次将鼠标悬停在 StackElement 上并不总是失败。如果我从 beneath 接近 StackElements 并首先尝试最后一个,它总是会触发。当我这样做时,有时第一个会起作用,但第二个不会触发。曾经,他们俩都设法正确操作,但很少发生。

我没有对任何东西进行多线程处理,我的父控件都没有处理它们自己的事件,所有事件处理程序都仅包含用于调试目的的 WriteLine() 语句,并且 StackElement 代码隐藏也不处理任何事件。

我尝试将 ItemsControl 与 CollectionViewSource 解耦,以便将其直接绑定到 ObservableCollection,除了(如我所料)绕过我添加到 ViewSource 的排序功能外,什么也没做。除了使它们与 StackElement 中包含的其他控件相关联之外,我还尝试处理 StackElement 类本身中的事件。我尝试使用 DataTriggers,如果我记得它按预期工作的话,但我需要包含更高级的逻辑,例如多选和无法轻轻突出显示已选择的 StackElement。

对于上下文,我打算使用这些事件在用户将鼠标拖到 StackElements 上方时轻轻突出显示它们,并在按下鼠标时强烈突出显示它们 - 基本上,我需要看起来和感觉像 Windows 文件资源管理器的东西.据我所知,仅使用 DataTriggers 无法以优雅的方式完成。

这是我的事件处理程序(在 MainWindow.xaml 中):

private void StackElement_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)

    Console.WriteLine("OnPreviewMouseLeftButtonUp fired for a StackElement.");


private void StackElement_OnMouseEnter(object sender, MouseEventArgs e)

    Console.WriteLine("OnMouseEnter fired for a StackElement.");


private void StackElement_OnMouseLeave(object sender, MouseEventArgs e)

    Console.WriteLine("OnMouseLeave fired for a StackElement.");

这是我添加到绑定集合的方式(用于测试,这就是它连接到随机按钮的原因):

private void Btn_File_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)

    InitiativeStackElement t = new InitiativeStackElement(new Entity("TestName", 10, 11, 12, 13, null)); //InitiativeStackElement implements INotifyPropertyChanged so the databindings work
    _entityProvider.Elements.Add(t); //_entityProvider is just a reference to a XAML-defined resource class, which is loaded up in the constructor so I don't have to call TryGetResource() whenever I want to use it. it's currently used for testing purposes only

最后,这是我的 XAML 中包含 StackElements 的部分:

<Border Grid.Row="1"
        Margin="0,1,0,0" 
        Style="StaticResource StandardBorder">
    <ScrollViewer Name="Scv_InitiativeStack">
        <StackPanel Name="Stp_InitiativeStack">
            <ItemsControl Name="Its_InitiativeStack" ItemsSource="Binding Source=StaticResource SortedInitiativeStack">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <con:StackElement Element="Binding" PreviewMouseLeftButtonUp="StackElement_OnPreviewMouseLeftButtonUp"                                           MouseEnter="StackElement_OnMouseEnter" MouseLeave="StackElement_OnMouseLeave"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </ScrollViewer>
</Border>

StackElement 类只定义了一个 InitiativeStackElement 类型的 DependencyProperty。此对象的属性绑定到 StackElement 中的一些控件,这些控件始终正确显示。让我感到困惑的是事件的行为。

如前所述,我希望只要将鼠标拖到 StackElement 上,就会触发 MouseEnter 事件。但是,它只有在我满足不应影响其功能的看似随机的条件后才会触发,例如首先将鼠标悬停在另一个 StackElement 上。没有错误消息。

【问题讨论】:

“据我所知,仅使用 DataTriggers 无法以优雅的方式完成。” 为什么不呢? 因为我一直无法找到用于实现比“如果 A 和 B,设置属性”更高级的逻辑的 DataTrigger 示例。 正如您提到的,您只想突出显示...这可以在 XAML 中轻松完成 好吧,我不只是想在用户将鼠标悬停在某物上时突出显示。当用户单击我的 UserControl 时,我还想突出显示(以不同的颜色)。此外,当用户将鼠标悬停在已选择的元素上时,我不希望出现较浅的鼠标悬停突出显示颜色。我还打算添加多选功能,类似于 Windows 文件资源管理器中的 shift-click。我无法找到仅在 XAML 中完成所有这些操作的方法,因此我选择了事件。 我想说所有这些都是可能的标准功能。在我看来 ListView 是你真正需要的:docs.microsoft.com/en-us/dotnet/framework/wpf/controls/… 【参考方案1】:

好的,我能够使用 ListBox 获得我想要的功能:

<Window.Resources>
    <DataTemplate x:Key="InitiativeStackTemplate">
        <con:StackElement Element="Binding"/>
    </DataTemplate>
</Window.Resources>

<Border Margin="0,1,0,0" 
        Grid.Row="1"
        Style="StaticResource StandardBorder">
    <ScrollViewer Name="Scv_InitiativeStack">
        <ListBox Name="Lbx_InitiativeStack" 
                 SelectionMode="Extended"
                 ItemsSource="Binding Source=StaticResource SortedInitiativeStack" 
                 ItemTemplate="StaticResource InitiativeStackTemplate"
                 HorizontalContentAlignment="Stretch"/>
    </ScrollViewer>
</Border>

一切都按预期进行。

【讨论】:

以上是关于为啥我的自定义控件不总是接收 MouseEnter 事件?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的自定义按钮的值不能保存在我的数据库中?

为啥自定义表格单元格总是默认大小

为啥我的 UISearchDisplayController 不使用我的自定义 tableViewCell?

我的自定义控件在页面中为何不能显示?

C# 在 DataGridView 上显示我的自定义控件,即使不编辑

不知道为啥我的自定义 UICollectionViewCell 不起作用?