我的WPF ScrollingTextBox
定义如下。
此文本框是只读的,只要触发ViewModel中的事件,其内容就会更新。
<Grid>
<StackPanel>
<local:ScrollingTextBox x:Name="textBox1"
Width="480"
Height="100"
Margin="12,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsReadOnly="True"
Background="Black"
Foreground="White"
Text="{Binding Path=UpdatedText, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
</StackPanel>
</Grid>
我必须定义以下类,以便在将新文本添加到文本框时启用自动向下滚动。此外,我需要覆盖OnPreviewMouseLeftButtonDown
和OnPreviewMouseLeftButtonUp
方法,以便在用户单击滚动条时禁用自动向下滚动:这些方法中包含的指令和FindVisualParent
方法来自this page。
public class ScrollingTextBox : TextBox
{
private volatile bool _AutomaticScrollingEnabled = true;
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (_AutomaticScrollingEnabled)
{
Focus();
CaretIndex = Text.Length;
ScrollToEnd();
}
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonDown(e);
object original = e.OriginalSource;
if (!original.GetType().Equals(typeof(ScrollViewer)))
{
if (FindVisualParent<ScrollBar>(original as DependencyObject) != null)
{
_AutomaticScrollingEnabled = false;
Trace.WriteLine("scroll bar is clicked down");
}
}
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonUp(e);
object original = e.OriginalSource;
if (!original.GetType().Equals(typeof(ScrollViewer)))
{
if (FindVisualParent<ScrollBar>(original as DependencyObject) != null)
{
_AutomaticScrollingEnabled = true;
Trace.WriteLine("scroll bar is clicked up");
}
}
}
private parentItem FindVisualParent<parentItem>(DependencyObject obj) where parentItem : DependencyObject
{
DependencyObject parent = VisualTreeHelper.GetParent(obj);
while (parent != null && !parent.GetType().Equals(typeof(parentItem)))
{
parent = VisualTreeHelper.GetParent(parent);
}
return parent as parentItem;
}
}
typeof(ScrollViewer)
?FindVisualParent
方法如何运作?答案 0 :(得分:1)
为什么我需要验证事件原始来源是否不等于typeof(ScrollViewer)?
我认为重点是确定用户点击的ScrollViewer
中的确切位置。因此,如果ScrollViewer本身正在发送事件,那么这是无用的,并且您希望等到事件隧道传输到您感兴趣的特定子项之一。(注意PreviewMouseLeftButtonUp
和{{1}隧道事件,这意味着事件从根向下通过可视树,直到它们被处理。)
FindVisualParent方法如何工作?
它只是爬上可视树,直到找到指定类型的元素(&#34; parentItem&#34;)或它到达顶部。
PreviewMouseLeftButtonDown