Scroll Viewer를 자동으로 스크롤하는 방법 - 사용자가 스크롤 위치를 변경하지 않은 경우에만
다음 동작을 생성하려고 합니다.ScrollViewer
그것은 포장되어 있다ContentControl
:
언제?ContentControl
키가 커지다,ScrollViewer
자동으로 마지막까지 스크롤됩니다.이것은 사용으로 쉽게 얻을 수 있다.ScrollViewer.ScrollToEnd()
.
그러나 사용자가 스크롤 바를 사용하면 자동 스크롤이 더 이상 발생하지 않습니다.이것은 예를 들어 VS 출력창에서 발생하는 것과 비슷합니다.
문제는 사용자 스크롤로 인해 스크롤이 발생한 시기와 콘텐츠 크기가 변경되었기 때문에 발생한 시기를 파악하는 것입니다.나는 그 게임을 하려고 했는데ScrollChangedEventArgs
의ScrollChangedEvent
, 하지만 작동하지 않았습니다.
가능한 모든 마우스 및 키보드 이벤트를 처리하지 않는 것이 좋습니다.
Scroll Changed Event Args 를 사용할 수 있습니다.ExtentHeightChanged가 콘텐츠 변경 또는 사용자 조작에 의한 것인지 확인하기 위해 변경...내용이 변경되지 않은 경우 ScrollBar 위치는 자동 스크롤 모드를 설정 또는 해제합니다.내용이 변경되면 자동 스크롤을 적용할 수 있습니다.
코드 배면:
private Boolean AutoScroll = true;
private void ScrollViewer_ScrollChanged(Object sender, ScrollChangedEventArgs e)
{
// User scroll event : set or unset auto-scroll mode
if (e.ExtentHeightChange == 0)
{ // Content unchanged : user scroll event
if (ScrollViewer.VerticalOffset == ScrollViewer.ScrollableHeight)
{ // Scroll bar is in bottom
// Set auto-scroll mode
AutoScroll = true;
}
else
{ // Scroll bar isn't in bottom
// Unset auto-scroll mode
AutoScroll = false;
}
}
// Content scroll event : auto-scroll eventually
if (AutoScroll && e.ExtentHeightChange != 0)
{ // Content changed and auto-scroll mode set
// Autoscroll
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight);
}
}
여기 몇 가지 출처에서 각색한 내용이 있습니다.
public class ScrollViewerExtensions
{
public static readonly DependencyProperty AlwaysScrollToEndProperty = DependencyProperty.RegisterAttached("AlwaysScrollToEnd", typeof(bool), typeof(ScrollViewerExtensions), new PropertyMetadata(false, AlwaysScrollToEndChanged));
private static bool _autoScroll;
private static void AlwaysScrollToEndChanged(object sender, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scroll = sender as ScrollViewer;
if (scroll != null)
{
bool alwaysScrollToEnd = (e.NewValue != null) && (bool)e.NewValue;
if (alwaysScrollToEnd)
{
scroll.ScrollToEnd();
scroll.ScrollChanged += ScrollChanged;
}
else { scroll.ScrollChanged -= ScrollChanged; }
}
else { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); }
}
public static bool GetAlwaysScrollToEnd(ScrollViewer scroll)
{
if (scroll == null) { throw new ArgumentNullException("scroll"); }
return (bool)scroll.GetValue(AlwaysScrollToEndProperty);
}
public static void SetAlwaysScrollToEnd(ScrollViewer scroll, bool alwaysScrollToEnd)
{
if (scroll == null) { throw new ArgumentNullException("scroll"); }
scroll.SetValue(AlwaysScrollToEndProperty, alwaysScrollToEnd);
}
private static void ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scroll = sender as ScrollViewer;
if (scroll == null) { throw new InvalidOperationException("The attached AlwaysScrollToEnd property can only be applied to ScrollViewer instances."); }
// User scroll event : set or unset autoscroll mode
if (e.ExtentHeightChange == 0) { _autoScroll = scroll.VerticalOffset == scroll.ScrollableHeight; }
// Content scroll event : autoscroll eventually
if (_autoScroll && e.ExtentHeightChange != 0) { scroll.ScrollToVerticalOffset(scroll.ExtentHeight); }
}
}
다음과 같이 XAML에서 사용합니다.
<ScrollViewer Height="230" HorizontalScrollBarVisibility="Auto" extensionProperties:ScrollViewerExtension.AlwaysScrollToEnd="True">
<TextBlock x:Name="Trace"/>
</ScrollViewer>
이 코드는 이전에 아래로 스크롤된 경우 콘텐츠가 커질 때 자동으로 끝까지 스크롤됩니다.
XAML:
<Window x:Class="AutoScrollTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<ScrollViewer Name="_scrollViewer">
<Border BorderBrush="Red" BorderThickness="5" Name="_contentCtrl" Height="200" VerticalAlignment="Top">
</Border>
</ScrollViewer>
</Window>
코드 배면:
using System;
using System.Windows;
using System.Windows.Threading;
namespace AutoScrollTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 2);
timer.Tick += ((sender, e) =>
{
_contentCtrl.Height += 10;
if (_scrollViewer.VerticalOffset == _scrollViewer.ScrollableHeight)
{
_scrollViewer.ScrollToEnd();
}
});
timer.Start();
}
}
}
여기 좋은 결과를 얻은 방법이 있습니다.두 개의 종속성 속성을 기반으로 합니다.다른 응답과 같이 코드 배후에 있는 것과 타이머를 회피합니다.
public static class ScrollViewerEx
{
public static readonly DependencyProperty AutoScrollProperty =
DependencyProperty.RegisterAttached("AutoScrollToEnd",
typeof(bool), typeof(ScrollViewerEx),
new PropertyMetadata(false, HookupAutoScrollToEnd));
public static readonly DependencyProperty AutoScrollHandlerProperty =
DependencyProperty.RegisterAttached("AutoScrollToEndHandler",
typeof(ScrollViewerAutoScrollToEndHandler), typeof(ScrollViewerEx));
private static void HookupAutoScrollToEnd(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var scrollViewer = d as ScrollViewer;
if (scrollViewer == null) return;
SetAutoScrollToEnd(scrollViewer, (bool)e.NewValue);
}
public static bool GetAutoScrollToEnd(ScrollViewer instance)
{
return (bool)instance.GetValue(AutoScrollProperty);
}
public static void SetAutoScrollToEnd(ScrollViewer instance, bool value)
{
var oldHandler = (ScrollViewerAutoScrollToEndHandler)instance.GetValue(AutoScrollHandlerProperty);
if (oldHandler != null)
{
oldHandler.Dispose();
instance.SetValue(AutoScrollHandlerProperty, null);
}
instance.SetValue(AutoScrollProperty, value);
if (value)
instance.SetValue(AutoScrollHandlerProperty, new ScrollViewerAutoScrollToEndHandler(instance));
}
이것은, 다음과 같이 정의된 핸들러를 사용합니다.
public class ScrollViewerAutoScrollToEndHandler : DependencyObject, IDisposable
{
readonly ScrollViewer m_scrollViewer;
bool m_doScroll = false;
public ScrollViewerAutoScrollToEndHandler(ScrollViewer scrollViewer)
{
if (scrollViewer == null) { throw new ArgumentNullException("scrollViewer"); }
m_scrollViewer = scrollViewer;
m_scrollViewer.ScrollToEnd();
m_scrollViewer.ScrollChanged += ScrollChanged;
}
private void ScrollChanged(object sender, ScrollChangedEventArgs e)
{
// User scroll event : set or unset autoscroll mode
if (e.ExtentHeightChange == 0)
{ m_doScroll = m_scrollViewer.VerticalOffset == m_scrollViewer.ScrollableHeight; }
// Content scroll event : autoscroll eventually
if (m_doScroll && e.ExtentHeightChange != 0)
{ m_scrollViewer.ScrollToVerticalOffset(m_scrollViewer.ExtentHeight); }
}
public void Dispose()
{
m_scrollViewer.ScrollChanged -= ScrollChanged;
}
다음으로 XAML에서 이것을 다음과 같이 사용합니다.
<ScrollViewer VerticalScrollBarVisibility="Auto"
local:ScrollViewerEx.AutoScrollToEnd="True">
<TextBlock x:Name="Test test test"/>
</ScrollViewer>
와 함께local
해당 XAML 파일의 맨 위에 네임스페이스 Import가 됩니다.이것에 의해,static bool
다른 답변에서 볼 수 있습니다.
TextBox의 "TextChanged" 이벤트와 ScrollToEnd() 메서드를 사용하면 어떨까요?
private void consolebox_TextChanged(object sender, TextChangedEventArgs e)
{
this.consolebox.ScrollToEnd();
}
bool autoScroll = false;
if (e.ExtentHeightChange != 0)
{
if (infoScroll.VerticalOffset == infoScroll.ScrollableHeight - e.ExtentHeightChange)
{
autoScroll = true;
}
else
{
autoScroll = false;
}
}
if (autoScroll)
{
infoScroll.ScrollToVerticalOffset(infoScroll.ExtentHeight);
}
월스트리트의 프로그래머
Windows 의 빌드 17763 이후에서는, 다음과 같이 설정할 수 있습니다.ScrollViewer
그리고 이것이 마지막입니다.
단, 아직 해결되지 않은 버그가 있습니다.https://github.com/Microsoft/microsoft-ui-xaml/issues/562
Windows 10에서는 .ScrollToVerticalOffset은 사용되지 않습니다.ChangeView를 이렇게 써요.
TextBlock messageBar;
ScrollViewer messageScroller;
private void displayMessage(string message)
{
messageBar.Text += message + "\n";
double pos = this.messageScroller.ExtentHeight;
messageScroller.ChangeView(null, pos, null);
}
이전 답변은 부동소수점 비교에서 작동하도록 다시 작성되었습니다.이 솔루션은 간단하지만 콘텐츠가 아래로 스크롤되는 즉시 사용자가 스크롤할 수 없도록 합니다.
private bool _should_auto_scroll = true;
private void ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e) {
if (Math.Abs(e.ExtentHeightChange) < float.MinValue) {
_should_auto_scroll = Math.Abs(ScrollViewer.VerticalOffset - ScrollViewer.ScrollableHeight) < float.MinValue;
}
if (_should_auto_scroll && Math.Abs(e.ExtentHeightChange) > float.MinValue) {
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight);
}
}
두 번째 답변에 따르면 다음과 같이 되지 않는 이유는 무엇입니까?
private void ScrollViewer_ScrollChanged(Object sender, ScrollChangedEventArgs e)
{
if (e.ExtentHeightChange != 0)
{
ScrollViewer.ScrollToVerticalOffset(ScrollViewer.ExtentHeight);
}
}
어플리케이션으로 테스트해 봤는데, 동작하고 있어요.
언급URL : https://stackoverflow.com/questions/2984803/how-to-automatically-scroll-scrollviewer-only-if-the-user-did-not-change-scrol
'programing' 카테고리의 다른 글
어댑터로부터의 콜액티비티 메서드 (0) | 2023.04.19 |
---|---|
힌트 텍스트를 WPF 텍스트 상자에 추가하려면 어떻게 해야 합니까? (0) | 2023.04.19 |
PowerShell에서 파일 버전 가져오기 (0) | 2023.04.14 |
데이터베이스 인덱스는 어떻게 작동합니까? (0) | 2023.04.14 |
#temptable과 #TempTable의 차이점 (0) | 2023.04.14 |