Отключить колесико мыши по управлению элементами в wpf
У меня есть usercontrol, у которого есть scrollviewer, а затем куча дочерних элементов управления, таких как текстовые поля, переключатели, списки и т.д. внутри него. Я могу использовать колесо мыши для прокрутки родительского средства прокрутки, пока моя мышь не окажется внутри списка, а затем события колеса мыши начнут поступать в список. Есть ли способ, чтобы список отправлял эти события обратно в родительский элемент управления? Удаление списка изнутри стороны родительского элемента управления, как предполагает этот вопрос ( колесо мыши не работает, когда над дочерними элементами управления ScrollViewer) не является решением.
я пытался
void ListBox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
}
но это тоже не сработало.
Спасибо
8 ответов
Ответ, на который вы ссылались, заключается именно в том, что вызывает вашу проблему: ListBox (который состоит из прочего ScrollViewer) внутри вашего ScrollViewer перехватывает событие MouseWheel и обрабатывает его, предотвращая его пузыри, и, таким образом, ScrollViewer не имеет представления о событии когда-либо происходило.
Используйте следующий чрезвычайно простой ControlTemplate для вашего ListBox для демонстрации (обратите внимание, что в нем нет ScrollViewer, и поэтому событие MouseWheel не будет перехвачено) ScrollViewer все еще будет прокручиваться с помощью мыши над ListBox.
<UserControl.Resources>
<ControlTemplate x:Key="NoScroll">
<ItemsPresenter></ItemsPresenter>
</ControlTemplate>
</UserControl.Resources>
<ScrollViewer>
<SomeContainerControl>
<.... what ever other controls are inside your ScrollViewer>
<ListBox Template="{StaticResource NoScroll}"></ListBox>
<SomeContainerControl>
</ScrollViewer>
У вас есть возможность захвата мыши, когда она входит в ScrollViewer, хотя она продолжает получать все события мыши до тех пор, пока мышь не будет отпущена, однако эта опция потребует, чтобы вы добавляли любые дальнейшие события мыши к элементам управления, содержащимся в ScrollViewer, если вы хочу ответа... будет достаточно следующих обработчиков событий MouseEnter MouseLeave.
private void ScrollViewerMouseEnter(object sender, MouseEventArgs e)
{
((ScrollViewer)sender).CaptureMouse();
}
private void ScrollViewerMouseLeave(object sender, MouseEventArgs e)
{
((ScrollViewer)sender).ReleaseMouseCapture();
}
Однако ни один из предложенных мной обходных путей не является предпочтительным, и я бы посоветовал переосмыслить то, что вы на самом деле пытаетесь сделать. Если вы объясните, что вы пытаетесь достичь в своем вопросе, я уверен, что вы получите еще несколько предложений...
Это может быть достигнуто через прикрепленное поведение.
http://josheinstein.com/blog/index.php/2010/08/wpf-nested-scrollviewer-listbox-scrolling/
Изменить: Вот связанное решение:
"Поэтому вместо этого я придумал следующий IgnoreMouseWheelBehavior. Технически это не игнорирование MouseWheel, а" перенаправление "события назад и обратно из ListBox. Проверьте его".
/// <summary>
/// Captures and eats MouseWheel events so that a nested ListBox does not
/// prevent an outer scrollable control from scrolling.
/// </summary>
public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement>
{
protected override void OnAttached( )
{
base.OnAttached( );
AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel ;
}
protected override void OnDetaching( )
{
AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
base.OnDetaching( );
}
void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
var e2 = new MouseWheelEventArgs(e.MouseDevice,e.Timestamp,e.Delta);
e2.RoutedEvent = UIElement.MouseWheelEvent;
AssociatedObject.RaiseEvent(e2);
}
}
А вот как вы будете использовать его в XAML.
<ScrollViewer Name="IScroll">
<ListBox Name="IDont">
<i:Interaction.Behaviors>
<local:IgnoreMouseWheelBehavior />
</i:Interaction.Behaviors>
</ListBox>
</ScrollViewer>
Где пространство имен i от Blend:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Я следовал подходу Амандуха для решения той же проблемы, что и у меня, с несколькими сетями данных в средстве просмотра прокрутки, но в WPF:
public sealed class IgnoreMouseWheelBehavior
{
public static bool GetIgnoreMouseWheel(DataGrid gridItem)
{
return (bool)gridItem.GetValue(IgnoreMouseWheelProperty);
}
public static void SetIgnoreMouseWheel(DataGrid gridItem, bool value)
{
gridItem.SetValue(IgnoreMouseWheelProperty, value);
}
public static readonly DependencyProperty IgnoreMouseWheelProperty =
DependencyProperty.RegisterAttached("IgnoreMouseWheel", typeof(bool),
typeof(IgnoreMouseWheelBehavior), new UIPropertyMetadata(false, OnIgnoreMouseWheelChanged));
static void OnIgnoreMouseWheelChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
var item = depObj as DataGrid;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
item.PreviewMouseWheel += OnPreviewMouseWheel;
else
item.PreviewMouseWheel -= OnPreviewMouseWheel;
}
static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
e.Handled = true;
var e2 = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{RoutedEvent = UIElement.MouseWheelEvent};
var gv = sender as DataGrid;
if (gv != null) gv.RaiseEvent(e2);
}
}
Как сказал Саймон, это ScrollViewer в стандартном шаблоне ListBox, который ловит событие. Чтобы обойти это, вы можете предоставить свой собственный шаблон.
<ControlTemplate x:Key="NoWheelScrollListBoxTemplate" TargetType="ListBox">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="1,1,1,1" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" Name="Bd" SnapsToDevicePixels="True">
<!-- This is the new control -->
<l:NoWheelScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</l:NoWheelScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter TargetName="Bd" Property="Panel.Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
</Trigger>
<Trigger Property="ItemsControl.IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
И реализация для NoWheelScrollViewer довольно проста.
public class NoWheelScrollViewer : ScrollViewer
{
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
// Do nothing
}
}
Затем, когда вы хотите, чтобы список не обрабатывал колесо мыши.
<ListBox Template="{StaticResource NoWheelScrollListBoxTemplate}">
Простое решение, которое сработало для меня, - переопределить шаблон внутреннего элемента управления, чтобы удалить средство просмотра прокрутки (в зависимости от того, что требуется), как это
Например, у меня есть такая структура
ListView (a)
ListView (б)
- ListView (c)
Я хотел выделить прокрутку колесика мыши от (b) до (a), однако хотел оставить прокрутку колеса мыши доступной (c). Я просто переопределил шаблон (б), как это. Это позволило мне всплеснуть содержимое (b), кроме (c) - (a). Кроме того, я все еще могу прокручивать содержимое (с). Если я хочу удалить даже для (с), то я должен повторить тот же шаг.
<ListView.Template>
<ControlTemplate>
<ItemsPresenter />
</ControlTemplate>
</ListView.Template>
Я пытался адаптировать ответ Саймона Фокса для DataGrid. Я обнаружил, что шаблон скрыл мои заголовки, и я так и не получил событие mouseLeave, выполнив его в C#. В конечном итоге это то, что сработало для меня:
private void DataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
((DataGrid)sender).CaptureMouse();
}
private void DataGrid_MouseWheel(object sender, MouseWheelEventArgs e)
{
((DataGrid)sender).ReleaseMouseCapture();
}
Вы должны прослушать PreviewMouseWheel из ScrollViewer (это работает), но не из списка.
Модифицированное решение Саймона Фокса, если оригинал не работает:
public sealed class IgnoreMouseWheelBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewMouseWheel += AssociatedObject_PreviewMouseWheel;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewMouseWheel -= AssociatedObject_PreviewMouseWheel;
base.OnDetaching();
}
static void AssociatedObject_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!(sender is DependencyObject))
{
return;
}
DependencyObject parent = VisualTreeHelper.GetParent((DependencyObject) sender);
if (!(parent is UIElement))
{
return;
}
((UIElement) parent).RaiseEvent(
new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta) { RoutedEvent = UIElement.MouseWheelEvent });
e.Handled = true;
}
}