WPF 检测滚动父控件
想象一下您打开 WPF Popup
的情况(例如通过 ButtonClick)。 您在 Popup
中直接有一个 ListBox
,其中包含一些项目,因此您必须能够滚动。 想象一下,这是您的自定义控件
,它位于ScrollViewer
中。
现在,如果您将鼠标从 Popup
表面移出并滚动,会发生什么? 您可以上下滚动,但 Popup
打开!这就是问题所在。
问题是,如何从控件内部检测 VisualTree 中其他未知的父控件已开始滚动? 并连续设置IsDropDownOpen = false
?
Imagine the situation you open a WPF Popup
(e.g. through ButtonClick).
You have a ListBox
directly in the Popup
with some items, so you have to be able to scroll.
Imagine that this is your Custom Control
and it's located in the ScrollViewer
.
Now if you move with your mouse outside from the Popup
surface and scroll, what happens?
You scroll up and down but with the Popup
opened ! And that's the problem.
The question is, how to detect from inside the Control, that some other unknown Parent Control in the VisualTree has started to scroll ?
and consecutively set IsDropDownOpen = false
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我们可以编写一个触发器来与
ScrollViewer
中包含的元素一起使用。这是一个完整的示例应用程序:我们有一个打开
Popup
的按钮,并且任何父ScrollViewer
中的任何滚动都会导致ScrollTrigger
操作触发并然后我们就可以关闭弹出窗口了。请注意,触发器附加到Button
而不是Popup
。我们可以使用视觉树中任何附近的元素。另请注意,我们使用另一个触发器来打开弹出窗口,但如何打开它对于原始问题并不重要。这是
ScrollTrigger
:ScrollTrigger
非常简单,它只是附加到所有父ScrollChanged
事件并触发任何包含的操作。在示例中,我们使用ChangePropertyAction
关闭Popup
。如果您不熟悉行为,请安装 Expression Blend 4 SDK 并添加这些命名空间:
并将
System.Windows.Interactivity
和Microsoft.Expression.Interactions
添加到您的项目中。We can write a trigger for use with elements contained within a
ScrollViewer
. Here is a complete sample application:We have a button that opens a
Popup
and any scrolling in any parentScrollViewer
causes theScrollTrigger
actions to fire and then we can close the popup. Note that the trigger is attached to theButton
and not thePopup
. We can use any nearby element that is in the visual tree. Also note that we use another trigger to open thePopup
but how it opens is not important to the original question.Here is the
ScrollTrigger
:The
ScrollTrigger
is very simple, it just attaches to all parentScrollChanged
events and fires any contained actions. In the sample we use theChangePropertyAction
to close thePopup
.If you are not familiar with behaviors, install the Expression Blend 4 SDK and add these namespaces:
and add
System.Windows.Interactivity
andMicrosoft.Expression.Interactions
to your project.我不太清楚您的控件是如何的,但是您不能根据 Focus 事件来打开/关闭控件吗?如果失去焦点,要关闭弹出窗口吗?
也许我理解错了,可以贴一下代码片段吗?
丹尼尔
I don't quite picture how your controls are, but can't you base your opening/closing of a control on the Focus event? And if it loses focus, to close the popup?
Maybe I understand wrong, can you post a code snippet?
Daniel
警告:这是一条很长的评论,它基本上只是解释我对 @Rick Sladkey 响应的更改。这是一个很好的起点,但我确实注意到我对所看到的一些事情做出了一些改变。
在进行自定义控件时,我想要与此类似的东西(我想关闭滚动上的弹出窗口),并发现答案与 Rick Sladkey 的答案非常相似,只需进行一些细微的更改即可帮助改进某些项目。
我所做的更改主要涉及3项。第一个是我看到
ScrollViewer_ScrollChanged
甚至在我没有主动滚动时触发(其他事情显然将其触发)。接下来是,当我卸载控件时,ScrollViewer_ScrollChanged
并未与ScrollViewer
分离,因此如果我添加 3,然后删除 1 并滚动,它仍然会触发3 次而不是 2 次。最后,我希望能够添加允许控件的使用者也动态设置 IsOpen 属性的功能。这样,我的
ScrollTrigger
类的修改版本看起来像这样:这里的第一个更改是我在
ScrollViewer_ScrollChanged
中添加了逻辑,以查看偏移值是否实际更改。我在触发器上添加了一个依赖属性,以便您可以根据需要绕过该逻辑。第二个更改是我向关联对象添加了 unloaded 事件,这样如果控件被删除,它就会删除 ScrollViewers 的相关操作,从而减少 ScrollViewer_ScrollChanged< 的次数动态添加和删除控件时进行了 /code> 调用。考虑到这些变化以及我希望能够允许我的控件的使用者决定弹出窗口的显示方式,我的 .xaml 看起来像这样:
现在我需要绑定一些东西,因为我正在创建一个自定义控件中,我在代码隐藏中创建了一些依赖属性和一些其他项目。如果您将此方法与 MVVM 一起使用,您将需要编写“
INotifyProperty
”并确保您的绑定是它们(可能不需要绑定的 ElementName 部分,具体取决于您的操作方式)。有很多方法可以做到这一点,如果你不知道,只需谷歌“mvvm数据绑定INotifyPropertyChanged”,你就会很容易找到它。附带说明一下,我也在使用 Prism,因此我使用的是
DelegateCommand
,但您可以使用您想要的任何ICommand
实现。有了这个,我的代码隐藏看起来像这样:这样,我就可以有一个弹出窗口,它将在任何实际发生更改的滚动触发器上关闭,没有任何不需要的调用,并且还允许用户可以修改是否打开。
如果您已经做到了这一点,谢谢您。我感谢您的奉献精神,希望这对您有所帮助。
Warning: This is a long comment, and it is basically just explaining my changes to @Rick Sladkey's response. It was a great starting point, but i did notice a few changes that I made with some things that I saw happening.
While doing my custom controls, I wanted something similar to this (I wanted to close a popup on a scroll), and found the answer to be something very similar to that of Rick Sladkey's answer with just a few minor changes to help improve some items.
The changes I made were mainly in regards to 3 items. The first being that i was seeing that the
ScrollViewer_ScrollChanged
even was firing when i wasn't activly scrolling (other things set it off apparently). Next was that when I was unloading my controls,ScrollViewer_ScrollChanged
wasn't detached from theScrollViewer
s, so if I added 3 and then removed 1 and scrolled, it would still fire 3 times instead of 2. Finally, I wanted to be able to add the functionality of allowing the consumer of my control to set the IsOpen property dynamically as well.With that, my modified version of the
ScrollTrigger
class looks something like:The first change here is that I added logic in
ScrollViewer_ScrollChanged
to see if the offset values actually changed or not. I added a dependency property on the trigger to allow you to bypass that logic if you wish to. The second change that I added an unloaded event to the associated object, so that if the control was removed, it would remove the related actions to theScrollViewers
, reducing the amount of times theScrollViewer_ScrollChanged
call was made when adding and removing my controls dynamically.With those changes in mind and the fact that I want to be able to allow consumers of my control to dictate how the popup is displayed, my .xaml looked something like:
Now I needed something to bind to, and because i'm creating a custom control, I created some dependency properties and some other items in the code-behind. If you are using this approach with MVVM, you will want to write '
INotifyProperty
's and make sure your bindings are them (might not need the ElementName parts of the binding depending on how you do it). There are many ways to do that, and if you don't know, just google "mvvm data binding INotifyPropertyChanged" and you will easily find it out.As a side note, I am also using Prism, so I am using
DelegateCommand
s, but you can use whatever implementation ofICommand
you want. With that, my code-behind looked something like:And with that, I am able to have a popup that will close on any scroll trigger that actually has a change, doens't have any un-needed calls, and will also allow a user to modify whether is open or not.
If you have made it this far, thank you. I appreciate your dedication, and hopefully this helps out a little bit.