WPF Scroll & focus changing problem
I am having a problem with scrolling for my WPF application.
Here is the deal. My UI is the following:
The role of my application is to act as a central hub for many applications and launch them. An admin can launch a dump recorded by another user.
Therefore, I have a ListView
, showing the application list, which is scrollable if needed.
I defined a GroupStyle
in order to show expanders and emulate a Windows Explorer view.
Everything works fine, I just have a problem: when scrolling with the mouse wheel, the component in clear blue ("Launch mode") seems to be catching the focus and stop scrolling.
This means especially that if my mouse is anywhere out of this control, the scrolling is okay. But whenever the mouse enters this control, I can't scroll anymore.
I tried to modify the property Focusable
and set it to False
everywhere I could but nothing changed. I'd guess that it is finally not a focus problem.
Anybody has an idea on how to avoid the scrolling to be caught by the element?
Here is some (simplified, removed some useless properties so as to make it as clear as possible) XAML for the expander's content:
<StackPanel Orientation="Vertical" VerticalAlignment="Top" >
<ToggleButton>
<!-- ToggleButton Content... -->
</ToggleButton>
<!-- This is the custom component in which you can see "Launch mode" -->
<my:UcReleaseChooser >
<!-- Properties there. I tried to set Focusable to False, no impact... -->
</my:UcReleaseChooser>
</StackPanel>
And the code for UcReleaseChooser
:
<StackPanel HorizontalAlignment="Stretch"
Focusable="False" ScrollViewer.CanContentScroll="False">
<ListBox ItemsSource="{Binding ListChosenReleases}" BorderBrush="LightGray" Background="AliceBlue"
HorizontalAlignment="Stretch" Focusable="False" ScrollViewer.CanContentScroll="False">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"
Focusable="False" ScrollViewer.CanContentScroll="False"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True" HorizontalAlignment开发者_高级运维="Stretch"
Focusable="False" ScrollViewer.CanContentScroll="False">
<TextBlock DockPanel.Dock="Top"
HorizontalAlignment="Left" Text="{Binding Key}"
FontStyle="Italic"/>
<ListBox DockPanel.Dock="Bottom"
HorizontalAlignment="Right" ItemsSource="{Binding Value}"
BorderBrush="{x:Null}" Background="AliceBlue"
Focusable="False" ScrollViewer.CanContentScroll="False">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Focusable="False"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<-- Blah blah about style -->
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Key}" Margin="3"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}}}"
Focusable="False" ScrollViewer.CanContentScroll="False"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
As you can see, the UcReleaseChooser
contains a list of RadioButton
lists. I tried to set Focusable
& CanContentScroll
to False
everywhere it seemed appropriate, but the control keeps preventing the main UI to scroll...
I guess I should change another property... Any idea?
Thanks!
The problem is the ListBox
, or more specifically, the ScrollViewer
within the ListBox
's template. This is getting your scroll events and consuming them before the outer ScrollViewer
in the ListView
even sees them.
I would advise replacing the ListBox
with an ItemsControl
if possible. However, that implies there will be no SelectedItem
property. If you need that, I would suggest setting ScrollViewer.HorizontalScrollBarVisibility
(or VerticalScrollBarVisibility
) to Disabled
. Failing that, I can only suggest re-templating ListBox
to not contain a ScrollViewer
at all.
I had an issue with a listbox stealing focus inside a scrollviewer (I have multiple listboxes inside the scrollviewer). So I created an attached property which denies the listbox the ability to scroll. So therefore the scrollviewer which is housing the listbox can scroll
Your control is a listbox, so this should work as is, but there is no reason the Extension should be limited to a Listbox; it just is to match my exact purpose.
public static class ListboxExtensions
{
public static DependencyProperty IgnoreScrollProperty = DependencyProperty.RegisterAttached("IgnoreScroll", typeof(bool), typeof(ListboxExtensions), new UIPropertyMetadata(false, IgnoreScrollChanged));
public static bool GetIgnoreScroll(DependencyObject dependencyObject)
{
return (bool)dependencyObject.GetValue(IgnoreScrollProperty);
}
public static void SetIgnoreScroll(DependencyObject dependencyObject, bool value)
{
dependencyObject.SetValue(IgnoreScrollProperty, value);
}
private static void IgnoreScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var newValue = (bool)e.NewValue;
var oldValue = (bool)e.OldValue;
var frameworkElement = d as FrameworkElement;
if (frameworkElement == null) return;
if (!newValue || oldValue || frameworkElement.IsFocused) return;
var lb = frameworkElement as ListBox;
if (lb == null) return;
lb.PreviewMouseWheel += LbOnPreviewMouseWheel;
}
private static void LbOnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!(sender is ListBox) || e.Handled) return;
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta)
{
RoutedEvent = UIElement.MouseWheelEvent,
Source = sender
};
var parent = ((Control)sender).Parent as UIElement;
if (parent != null) parent.RaiseEvent(eventArg);
}
}
And then in your XAML, you just put this on the listbox:
<ListBox extensions:ListboxExtensions.IgnoreScroll="True">
Of course, remembering to include the namespace to your extensions in the top of the XAML:
xmlns:extensions="clr-namespace:UI.Extensions"
精彩评论