WPF
myVisualStudioStudy

Visual Studio | WPF > Behavior > 複数行のTextBox > 最終行に移動する(自動スクロール)実装

動作環境
Windows 8.1 Pro (64bit)
Microsoft Visual Studio 2017 Community
Sublime Text 2

実装内容

ロギングなどで複数行のTextBoxの表示をする場合、1行追加のたびに最終行が表示されることが望ましい場合がある。

C++ Builder | TMemo > 最終行に移動する実装

WPFではどう実装するか調べた。
https://stackoverflow.com/questions/7010462/how-to-make-wpf-textbox-with-a-scrollbar-automatically-scroll-to-the-bottom-when/7010608

answered Aug 10 '11 at 12:14
bitbonk

You could write an attached property or even better a behavior that listens to the TextChanged event and scrolls to the bottom in the callback.

Behavior を使ってみる。

参考

添付ビヘイビアを使い TextBox で数値以外を入力できなくする。 by hilaponさん
にBehaviorの使用例があり、これを元に自動スクロールにしてみる。

code

MainWindow.xaml.cs
<Window x:Class="_171207_t1930_behavior.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_171207_t1930_behavior"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <Button Name="B_add" Content="Add" Height="30" Click="B_add_Click"/>
            <TextBox Name="T_memo" Height="250" Width="500"
                     VerticalScrollBarVisibility="Visible"
                     local:TextBoxBehaviors.AutoScrollToEnd="True"/>
        </StackPanel>
    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace _171207_t1930_behavior
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void B_add_Click(object sender, RoutedEventArgs e)
        {
            T_memo.Text += "HELLO" + System.Environment.NewLine;
        }
    }

}
TextBoxBehavior.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 以下を追加した
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace _171207_t1930_behavior
{
    /// <summary>
    /// TextBox 添付ビヘイビア
    /// </summary>
    public class TextBoxBehaviors
    {
        /// <summary>
        /// 複数行のテキストを扱う
        /// テキスト追加時に最終行が表示されるようにする
        /// </summary>
        public static readonly DependencyProperty AutoScrollToEndProperty =
                    DependencyProperty.RegisterAttached(
                        "AutoScrollToEnd", typeof(bool),
                        typeof(TextBoxBehaviors),
                        new UIPropertyMetadata(false, IsTextChanged)
                    );

        [AttachedPropertyBrowsableForType(typeof(TextBox))]
        public static bool GetAutoScrollToEnd(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoScrollToEndProperty);
        }

        public static void SetAutoScrollToEnd(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoScrollToEndProperty, value);
        }

        private static void IsTextChanged
            (DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var textBox = sender as TextBox;
            if (textBox == null) return;

            // イベントを登録・削除 
            textBox.TextChanged -= OnTextChanged;

            Console.Write("-");

            var newValue = (bool)e.NewValue;
            if (newValue)
            {
                textBox.TextChanged += OnTextChanged;
            }
        }

        private static void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            var textBox = sender as TextBox;
            if (textBox == null) return;

            if (string.IsNullOrEmpty(textBox.Text))
            {
                return;
            }

            textBox.ScrollToEnd();
            Console.Write("*");
        }
    }
}

実行例

自動スクロールになった。

qiita.png

デバッグウィンドウに表示される項目は下記である。

  • -: 起動時に表示される
    • イベント登録処理
  • *: TextChangedごとに表示される

補足

Behaviorを使うことで、MVVMにした場合にも動作するようになると考えている。

関連