Wpf ScrollViewer Quantità di scorrimento
-
08-07-2019 - |
Domanda
È possibile modificare la quantità di scorrimento di WPF ScrollViewer? Mi chiedo semplicemente se è possibile cambiare il visualizzatore di scorrimento in modo che quando si utilizza la rotellina del mouse o le frecce del visualizzatore di scorrimento, è possibile modificare la quantità di scorrimento incrementale.
Soluzione
La risposta breve è: non c'è modo di farlo senza scrivere un codice di scorrimento personalizzato, ma non lasciare che ti spaventi non è poi così difficile.
ScrollViewer funziona scorrendo usando unità fisiche (cioè pixel) o impegnandosi con un'implementazione di IScrollInfo per usare unità logiche. Questo è controllato dall'impostazione la proprietà CanContentScroll dove un valore di false significa "scorrere il contenuto usando le unità fisiche" e un valore di mezzi veri " scorrere il contenuto logicamente " ;.
Quindi, come fa ScrollViewer a scorrere il contenuto logicamente? Comunicando con un'implementazione IScrollInfo. Quindi è così che potresti assumere esattamente quanto scorre il contenuto del tuo pannello quando qualcuno esegue un'azione logica. Dai un'occhiata alla documentazione di IScrollInfo per ottenere un elenco di tutte le unità logiche di misura che possono essere richieste per scorrere, ma poiché hai citato la rotellina del mouse, sarai principalmente interessato ai metodi MouseWheelUp / Giù / Sinistra / Destra.
Altri suggerimenti
Ecco una WPF ScrollViewer
semplice, completa e funzionante che ha una proprietà SpeedFactor
vincolante i dati per regolare la sensibilità della rotellina del mouse. L'impostazione di SpeedFactor
su 1.0 significa un comportamento identico al ScrollViewer
di WPF. Il valore predefinito per la proprietà di dipendenza è 2.5 , che consente lo scorrimento delle ruote molto veloce.
Ovviamente, puoi anche creare funzionalità utili aggiuntive vincolando la proprietà SpeedFactor
stessa, ovvero per consentire all'utente di controllare facilmente il moltiplicatore.
public class WheelSpeedScrollViewer : ScrollViewer
{
public static readonly DependencyProperty SpeedFactorProperty =
DependencyProperty.Register(nameof(SpeedFactor),
typeof(Double),
typeof(WheelSpeedScrollViewer),
new PropertyMetadata(2.5));
public Double SpeedFactor
{
get { return (Double)GetValue(SpeedFactorProperty); }
set { SetValue(SpeedFactorProperty, value); }
}
protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
{
if (!e.Handled &&
ScrollInfo is ScrollContentPresenter scp &&
ComputedVerticalScrollBarVisibility == Visibility.Visible)
{
scp.SetVerticalOffset(VerticalOffset - e.Delta * SpeedFactor);
e.Handled = true;
}
}
};
Demo completa XAML di "scorrimento rapido della rotellina del mouse" di circa 3200 elementi di dati:
<UserControl x:Class="RemoveDuplicateTextLines.FastScrollDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<local:WheelSpeedScrollViewer VerticalScrollBarVisibility="Auto">
<ListBox ItemsSource="{Binding Source={x:Type sys:Object},Path=Assembly.DefinedTypes}" />
</local:WheelSpeedScrollViewer>
</UserControl>
Rotellina veloce del mouse:
È possibile implementare un comportamento sullo scrollviewer. Nel mio caso CanContentScroll
non ha funzionato. La soluzione seguente funziona per lo scorrimento con la rotellina del mouse e per trascinare la barra di scorrimento.
public class StepSizeBehavior : Behavior<ScrollViewer>
{
public int StepSize { get; set; }
#region Attach & Detach
protected override void OnAttached()
{
CheckHeightModulesStepSize();
AssociatedObject.ScrollChanged += AssociatedObject_ScrollChanged;
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.ScrollChanged -= AssociatedObject_ScrollChanged;
base.OnDetaching();
}
#endregion
[Conditional("DEBUG")]
private void CheckHeightModulesStepSize()
{
var height = AssociatedObject.Height;
var remainder = height%StepSize;
if (remainder > 0)
{
throw new ArgumentException(<ScrollViewer MaxHeight="248"
VerticalScrollBarVisibility="Auto">
<i:Interaction.Behaviors>
<behaviors:StepSizeBehavior StepSize="62" />
</i:Interaction.Behaviors>
quot;{nameof(StepSize)} should be set to a value by which the height van be divised without a remainder.");
}
}
private void AssociatedObject_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
const double stepSize = 62;
var scrollViewer = (ScrollViewer)sender;
var steps = Math.Round(scrollViewer.VerticalOffset / stepSize, 0);
var scrollPosition = steps * stepSize;
if (scrollPosition >= scrollViewer.ScrollableHeight)
{
scrollViewer.ScrollToBottom();
return;
}
scrollViewer.ScrollToVerticalOffset(scrollPosition);
}
}
Lo useresti in questo modo:
<*>L'ho fatto per garantire numeri interi su scrollbar1.ValueChanged:
scrollbar1.Value = Math.Round(scrollbar1.Value, 0, MidpointRounding.AwayFromZero)