

良くない実装なのはとりあえず、無視ください。

Gridに多く(7000個)のLabelをaddして表示するのに要する時間と、LabelをTextBoxにした場合に要する時間が、7秒と240秒と約35倍もの差があります。  
若干の差であれば納得できるのですが、TextBoxはなぜここまで遅くなるのでしょうか。

あと、1000個、2000個と増やして計測するとリニアに時間がのびるのではなく、個数が増えると急速に要する時間が増える挙動が納得できないところです。


CC BY-SA 3.0
2016-10-04T03:48:39.207
29335
2016-10-06T06:02:17.580
2016-10-04T05:03:03.233
2266
2266
question
c# wpf
Labelと比べてTextBoxの表示が遅いのはなぜ？


単純に`TextBox`の方が`Label`より複雑なコントロールだからだと思います。

`Label`は`Border`+`ContentPresenter`(+`TextBlock`)という非常にシンプルなビジュアルツリーを取ります。  
一方`TextBox`は内部に`ScrollBar`や`AdornerLayer`を含んでいます。文字の描画も`TextBlock`ではなく`DrawingVisual`を使用しています。

ソースコードでも[`TextBox`](http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/TextBox.cs)は[`Label`](http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Label.cs)の10倍程度の長さがあります。それぞれの基底を含めても6,7倍にはなるでしょう。  
さらに上のコードから`Label`は`MeasureOverride`を実装していないことが分かります。なのでレイアウト面でも`Label`の方が高速になると考えられます。

少なくとも上記の観点で`TextBox`の方がリッチなコントロールなのでパフォーマンスが悪いものと判断できます。


CC BY-SA 3.0
2016-10-04T04:34:52.267
29338
2016-10-04T04:34:52.267
5750
29335
answer


ちゃんとした説明になっていないかもですが、  
下記はラベルとテキストボックスの内部定義です。  
保有しているオブジェクトの容量が圧倒的に違うことが分かると思います。  
これらの初期により時間が掛かっているのではないでしょうか？

ラベルを継承して、テキストボックスとまったく同じ動作をさせるにはどのぐらいの処理が必要かを考えると分かりやすいかも知れません。

■テキストボックス

[code]

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Controls.Primitives;
    using System.Windows.Documents;
    using System.Windows.Markup;
    
    namespace System.Windows.Controls
    {
        // 概要:
        //     書式なしテキストの表示や編集に使用できるコントロールを表します。
        [ContentProperty("Text")]
        [Localizability(LocalizationCategory.Text)]
        public class TextBox : TextBoxBase, IAddChild, ITextBoxViewHost
        {
            // 概要:
            //     System.Windows.Controls.TextBox.CharacterCasing 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.CharacterCasing 依存関係プロパティの識別子。
            public static readonly DependencyProperty CharacterCasingProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.MaxLength 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.MaxLength 依存関係プロパティの識別子。
            public static readonly DependencyProperty MaxLengthProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.MaxLines 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.MaxLines 依存関係プロパティの識別子。
            public static readonly DependencyProperty MaxLinesProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.MinLines 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.MinLines 依存関係プロパティの識別子。
            public static readonly DependencyProperty MinLinesProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.TextAlignment 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.TextAlignment 依存関係プロパティの識別子。
            public static readonly DependencyProperty TextAlignmentProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.TextDecorations 依存関係プロパティを識別します。
            public static readonly DependencyProperty TextDecorationsProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.Text 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.Text 依存関係プロパティの識別子。
            public static readonly DependencyProperty TextProperty;
            //
            // 概要:
            //     System.Windows.Controls.TextBox.TextWrapping 依存関係プロパティを識別します。
            public static readonly DependencyProperty TextWrappingProperty;
    
            // 概要:
            //     System.Windows.Controls.TextBox クラスの新しいインスタンスを初期化します。
            public TextBox();
    
            // 概要:
            //     キャレットの挿入位置インデックスを取得または設定します。
            //
            // 戻り値:
            //     キャレットの 0 から始まる挿入位置インデックス。
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public int CaretIndex { get; set; }
            //
            // 概要:
            //     テキスト ボックスに手動で文字を入力した場合に大文字で入力されるか小文字で入力されるかを取得または設定します。
            //
            // 戻り値:
            //     手動で入力した文字が大文字で入力されるか小文字で入力されるかを指定する System.Windows.Controls.CharacterCasing
            //     値のいずれか。 既定値は、System.Windows.Controls.CharacterCasing.Normal です。
            public CharacterCasing CharacterCasing { get; set; }
            //
            // 概要:
            //     テキスト ボックスの行の合計数を取得します。
            //
            // 戻り値:
            //     テキスト ボックス内の行の合計数。レイアウト情報を利用できない場合は -1。
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public int LineCount { get; }
            //
            // 概要:
            //     System.Windows.Controls.TextBox の論理上の子要素に対する列挙子を取得します。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox の論理上の子要素に対する列挙子。
            protected internal override IEnumerator LogicalChildren { get; }
            //
            // 概要:
            //     テキスト ボックスに手動で入力できる最大文字数を取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックスに手動で入力できる最大文字数。 既定値 0 は、制限がないことを示します。
            [DefaultValue(0)]
            [Localizability(LocalizationCategory.None, Modifiability = Modifiability.Unmodifiable)]
            public int MaxLength { get; set; }
            //
            // 概要:
            //     表示行の最大数を取得または設定します。
            //
            // 戻り値:
            //     表示行の最大数。 既定値は、System.Int32.MaxValue です。
            //
            // 例外:
            //   System.Exception:
            //     System.Windows.Controls.TextBox.MaxLines が System.Windows.Controls.TextBox.MinLines
            //     より小さい。
            [DefaultValue(2147483647)]
            public int MaxLines { get; set; }
            //
            // 概要:
            //     表示行の最小数を取得または設定します。
            //
            // 戻り値:
            //     表示行の最小数。 既定値は 1 です。
            //
            // 例外:
            //   System.Exception:
            //     System.Windows.Controls.TextBox.MinLines が System.Windows.Controls.TextBox.MaxLines
            //     より大きくなっています。
            [DefaultValue(1)]
            public int MinLines { get; set; }
            //
            // 概要:
            //     テキスト ボックス内の現在の選択範囲のコンテンツを取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックス内で現在選択されているテキスト。
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public string SelectedText { get; set; }
            //
            // 概要:
            //     テキスト ボックス内の現在の選択範囲の文字数を示す値を取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックス内の現在の選択範囲の文字数。 既定値は 0 です。
            //
            // 例外:
            //   System.ArgumentOutOfRangeException:
            //     System.Windows.Controls.TextBox.SelectionLength に負の数が設定されています。
            [DefaultValue(0)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public int SelectionLength { get; set; }
            //
            // 概要:
            //     現在の選択範囲の先頭の文字インデックスを取得または設定します。
            //
            // 戻り値:
            //     現在の選択範囲の先頭の文字インデックス。
            //
            // 例外:
            //   System.ArgumentOutOfRangeException:
            //     System.Windows.Controls.TextBox.SelectionStart が負の数に設定されています。
            [DefaultValue(0)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
            public int SelectionStart { get; set; }
            //
            // 概要:
            //     テキスト ボックスのテキスト コンテンツを取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックスのテキスト コンテンツが含まれる文字列。 既定値は、空の文字列 ("") です。
            [DefaultValue("")]
            [Localizability(LocalizationCategory.Text)]
            public string Text { get; set; }
            //
            // 概要:
            //     テキスト ボックスのコンテンツの水平方向の配置を取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックスのコンテンツの水平方向の配置を指定する System.Windows.TextAlignment 値の 1 つ。 既定値は、System.Windows.TextAlignment.Left
            //     です。
            public TextAlignment TextAlignment { get; set; }
            //
            // 概要:
            //     テキスト ボックスに適用する文字装飾を取得します。
            //
            // 戻り値:
            //     テキスト ボックスに適用する文字装飾が格納される System.Windows.TextDecorationCollection コレクション。
            //     既定値は null です (文字装飾は適用されません)。
            public TextDecorationCollection TextDecorations { get; set; }
            //
            // 概要:
            //     テキスト ボックスのテキスト折り返し方法を取得または設定します。
            //
            // 戻り値:
            //     テキスト ボックスのテキスト折り返し方法を示す System.Windows.TextWrapping 値のいずれか。 既定値は、System.Windows.TextWrapping.NoWrap
            //     です。
            public TextWrapping TextWrapping { get; set; }
            //
            // 概要:
            //     テキスト ボックスのテキスト コンテンツで現在有効な文字体裁のバリエーションを取得します。
            //
            // 戻り値:
            //     現在有効な文字体裁のバリエーションを指定する System.Windows.Documents.Typography オブジェクト。 既定の文字体裁値の一覧については、System.Windows.Documents.Typography
            //     のトピックを参照してください。
            public Typography Typography { get; }
    
            // 概要:
            //     テキスト ボックスからすべての内容を削除します。
            public void Clear();
            //
            // 概要:
            //     指定された行内の最初の文字に対応する、0 から始まる文字インデックスを返します。
            //
            // パラメーター:
            //   lineIndex:
            //     最初の文字インデックスを取得する行の 0 から始まるインデックス。
            //
            // 戻り値:
            //     指定された行内の最初の文字に対応する、0 から始まるインデックス。
            public int GetCharacterIndexFromLineIndex(int lineIndex);
            //
            // 概要:
            //     指定されたポイントに最も近い場所にある文字の、0 から始まるインデックスを返します。
            //
            // パラメーター:
            //   point:
            //     インデックスを返す対象である、System.Windows.Controls.TextBox 座標空間内のポイント。
            //
            //   snapToText:
            //     指定されたポイントに文字がなかったときに最も近いインデックスを返す場合は true。指定されたポイントに文字がなかったときに –1 を返す場合は
            //     false。
            //
            // 戻り値:
            //     指定されたポイントに最も近い場所にある文字のインデックス。有効なインデックスが見つからない場合は -1。
            public int GetCharacterIndexFromPoint(Point point, bool snapToText);
            //
            // 概要:
            //     現在テキスト ボックスに表示されている最初の行に対応する行インデックスを返します。
            //
            // 戻り値:
            //     テキスト ボックスに表示されている最初の行に対応する、0 から始まるインデックス。
            public int GetFirstVisibleLineIndex();
            //
            // 概要:
            //     現在テキスト ボックスに表示されている最後の行に対応する行インデックスを返します。
            //
            // 戻り値:
            //     テキスト ボックスに表示されている最後の行に対応する、0 から始まるインデックス。
            public int GetLastVisibleLineIndex();
            //
            // 概要:
            //     指定された文字インデックスが含まれている行に対応する、0 から始まる行インデックスを返します。
            //
            // パラメーター:
            //   charIndex:
            //     関連付けられている行インデックスを取得するための 0 から始まる文字インデックス。
            //
            // 戻り値:
            //     指定された文字インデックスが含まれている行に対応する、0 から始まるインデックス。
            public int GetLineIndexFromCharacterIndex(int charIndex);
            //
            // 概要:
            //     指定された行の文字数を返します。
            //
            // パラメーター:
            //   lineIndex:
            //     文字数を取得するための 0 から始まる行インデックス。
            //
            // 戻り値:
            //     指定された行の文字数。
            public int GetLineLength(int lineIndex);
            //
            // 概要:
            //     指定された行に現在表示されているテキストを返します。
            //
            // パラメーター:
            //   lineIndex:
            //     現在表示されているテキストを取得するための 0 から始まる行インデックス。
            //
            // 戻り値:
            //     指定された行に現在表示されているテキストのコピーを格納する文字列。
            public string GetLineText(int lineIndex);
            //
            // 概要:
            //     テキスト ボックスのコンテンツ内にある次のスペル ミスの先頭の文字インデックスを返します。
            //
            // パラメーター:
            //   charIndex:
            //     次のスペル ミスの検索を開始する位置を示す、0 から始まる文字インデックス。
            //
            //   direction:
            //     指定された charIndex から次のスペル ミスを検索するときの方向を指定する System.Windows.Documents.LogicalDirection
            //     値のいずれか。
            //
            // 戻り値:
            //     テキスト ボックスのコンテンツ内にある次のスペル ミスの先頭に対応する文字インデックス。次のスペル ミスが存在しない場合は -1。
            public int GetNextSpellingErrorCharacterIndex(int charIndex, LogicalDirection direction);
            //
            // 概要:
            //     指定されたインデックス位置にある文字のリーディング エッジの四角形を返します。
            //
            // パラメーター:
            //   charIndex:
            //     四角形を取得する文字の 0 から始まる文字インデックス。
            //
            // 戻り値:
            //     指定された文字インデックス位置にある文字のリーディング エッジの四角形。外接する四角形を決定できない場合は System.Windows.Rect.Empty。
            public Rect GetRectFromCharacterIndex(int charIndex);
            //
            // 概要:
            //     指定されたインデックス位置にある文字のリーディング エッジまたはトレーリング エッジの四角形を返します。
            //
            // パラメーター:
            //   charIndex:
            //     四角形を取得する文字の 0 から始まる文字インデックス。
            //
            //   trailingEdge:
            //     文字のトレーリング エッジを取得する場合は true。文字のリーディング エッジを取得する場合は false。
            //
            // 戻り値:
            //     指定された文字インデックス位置にある文字のエッジの四角形。外接する四角形を決定できない場合は System.Windows.Rect.Empty。
            //
            // 例外:
            //   System.ArgumentOutOfRangeException:
            //     charIndex が負か、またはコンテンツの長さより大きい値です。
            public Rect GetRectFromCharacterIndex(int charIndex, bool trailingEdge);
            //
            // 概要:
            //     指定された文字インデックス位置にあるスペル ミスに関連付けられた System.Windows.Controls.SpellingError オブジェクトを返します。
            //
            // パラメーター:
            //   charIndex:
            //     スペル ミスをチェックするコンテンツ内の位置の 0 から始まる文字インデックス。
            //
            // 戻り値:
            //     charIndex で示された文字の位置で見つかったスペル ミスの詳細を格納する System.Windows.Controls.SpellingError
            //     オブジェクト。指定された文字にスペル ミスが存在しない場合は null。
            public SpellingError GetSpellingError(int charIndex);
            //
            // 概要:
            //     指定された文字を含むスペル ミスがあった場合、その長さを返します。
            //
            // パラメーター:
            //   charIndex:
            //     スペル ミスをチェックするコンテンツ内の位置の 0 から始まる文字インデックス。
            //
            // 戻り値:
            //     charIndex で指定された文字を含むスペル ミスの長さ。指定した文字を含むスペル ミスがない場合は 0。
            public int GetSpellingErrorLength(int charIndex);
            //
            // 概要:
            //     指定された文字を含むスペル ミスがあった場合、先頭の文字インデックスを返します。
            //
            // パラメーター:
            //   charIndex:
            //     スペル ミスをチェックするコンテンツ内の位置の 0 から始まる文字インデックス。
            //
            // 戻り値:
            //     charIndex で指定された文字を含むスペル ミスがあった場合は、先頭の文字インデックス。指定された文字を含むスペル ミスがない場合は –1。
            public int GetSpellingErrorStart(int charIndex);
            //
            // 概要:
            //     テキスト ボックスのサイズをコンテンツに合わせます。
            //
            // パラメーター:
            //   constraint:
            //     テキスト ボックスのサイズに関する制約を指定する System.Windows.Size 構造体。
            //
            // 戻り値:
            //     テキスト ボックスの新しいサイズを示す System.Windows.Size 構造体。
            protected override Size MeasureOverride(Size constraint);
            //
            // 概要:
            //     テキスト ボックスの System.Windows.Automation.Peers.AutomationPeer オブジェクトを作成し、返します。
            //
            // 戻り値:
            //     テキスト ボックスの System.Windows.Automation.Peers.AutomationPeer オブジェクト。
            protected override AutomationPeer OnCreateAutomationPeer();
            //
            // 概要:
            //     要素に存在する 1 つ以上の依存関係プロパティの有効な値が変更された場合に呼び出されます。
            //
            // パラメーター:
            //   e:
            //     関連付けられたイベントの引数。
            protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e);
            //
            // 概要:
            //     指定された行インデックス位置の行が表示されるまでスクロールします。
            //
            // パラメーター:
            //   lineIndex:
            //     表示されるまでスクロールする行の 0 から始まる行インデックス。
            public void ScrollToLine(int lineIndex);
            //
            // 概要:
            //     テキスト ボックスでテキストの範囲を選択します。
            //
            // パラメーター:
            //   start:
            //     選択されている先頭の文字の 0 から始まる文字インデックス。
            //
            //   length:
            //     選択範囲の長さ (文字数)。
            //
            // 例外:
            //   System.ArgumentOutOfRangeException:
            //     start または length が負の値です。
            public void Select(int start, int length);
            //
            // 概要:
            //     System.Windows.Controls.TextBox オブジェクトのシリアル化中に System.Windows.Controls.TextBox.Text
            //     プロパティの有効値をシリアル化する必要があるかどうかを示す値を返します。
            //
            // パラメーター:
            //   manager:
            //     このオブジェクトのシリアル化サービス マネージャー オブジェクト。
            //
            // 戻り値:
            //     System.Windows.Controls.TextBox.Text プロパティをシリアル化する必要がある場合は true。それ以外の場合は
            //     false。
            //
            // 例外:
            //   System.NullReferenceException:
            //     manager は null なので、
            [EditorBrowsable(EditorBrowsableState.Never)]
            public bool ShouldSerializeText(XamlDesignerSerializationManager manager);
        }
    }
    
[/code]

■ラベル

[code]

    #region アセンブリ PresentationFramework.dll, v4.0.0.0
    // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\PresentationFramework.dll
    #endregion
    
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Automation.Peers;
    using System.Windows.Markup;
    namespace System.Windows.Controls
    {
        // 概要:
        //     コントロールのテキスト ラベルを表し、アクセス キーのサポートを提供します。
        [Localizability(LocalizationCategory.Label)]
        public class Label : ContentControl
        {
            // 概要:
            //     System.Windows.Controls.Label.Target 依存関係プロパティを識別します。
            //
            // 戻り値:
            //     System.Windows.Controls.Label.Target 依存関係プロパティの識別子。
            public static readonly DependencyProperty TargetProperty;
    
            // 概要:
            //     System.Windows.Controls.Label クラスの新しいインスタンスを初期化します。
            public Label();
    
            // 概要:
            //     ユーザーがラベルのアクセス キーを押したときにフォーカスを受け取る要素を取得または設定します。
            //
            // 戻り値:
            //     ユーザーがアクセス キーを押したときにフォーカスを受け取る System.Windows.UIElement。 既定値は、null です。
            [TypeConverter(typeof(NameReferenceConverter))]
            public UIElement Target { get; set; }
    
            // 概要:
            //     WPF インフラストラクチャの一部として、このコントロールに適切な System.Windows.Automation.Peers.LabelAutomationPeer
            //     を実装します。
            //
            // 戻り値:
            //     型固有の System.Windows.Automation.Peers.AutomationPeer 実装。
            protected override AutomationPeer OnCreateAutomationPeer();
        }
    }
    
[/code]


CC BY-SA 3.0
2016-10-04T04:38:26.073
29339
2016-10-04T04:38:26.073
19001
29335
answer


[`Grid`](https://msdn.microsoft.com/ja-
jp/library/system.windows.controls.grid\(v=vs.110\).aspx)に7000個追加してみましたが、イベント完了までの時間も実際の描画完了までの時間も倍程度の差しかありませんでした。

[code]

    private void button_Click(object sender, RoutedEventArgs e) {
        var stopwatch = Stopwatch.StartNew();
        for (var y = 0; y < 700; y++)
            for (var x = 0; x < 10; x++) {
                grid.Children.Add(new Label { Content = $"{x}x{y}", Margin = new Thickness(10 + 50 * x, 35 + 35 * y, 0, 0) });
                //grid.Children.Add(new TextBox { Text = $"{x}x{y}", Margin = new Thickness(10 + 50 * x, 35 + 35 * y, 0, 0) });
            }
        stopwatch.Stop();
        Debug.WriteLine($"time: {stopwatch.ElapsedMilliseconds / 1000.0}");
    }
    
[/code]


CC BY-SA 3.0
2016-10-04T08:14:07.543
29346
2016-10-04T08:14:07.543
4236
29335
answer


<https://social.msdn.microsoft.com/Forums/ja-
JP/4cd4681d-6cd8-45bb-805d-b5c552955ecf/windows-10-anniversary-update-
pcoutofmemory?forum=wpfja>  
によると  
Windows 10 Anniversary Update + 日本語 MS-IME な環境では TextBox を１つ作ると  
スレッドが１つ増える、とのことです。そりゃ遅くなるわ。  
人によって再現したりしなかったりするのは環境の違いだと思われます。

日本語 MS-IME のバグなのか WPF のせいかまではわかっていないとの事。

オイラも TextBox を多数作っても UX の向上には寄与しないと思うので再考慮を推奨。


CC BY-SA 3.0
2016-10-06T06:02:17.580
29386
2016-10-06T06:02:17.580
8589
29335
answer