TableLayoutPanelsの同期化

私はいくつかのTableLayoutPanelsを持っています。それぞれのラベルは情報ラベル付きのものとデータラベル付きのものの2つの列に名前/値情報のカテゴリを表示します。

それぞれの場合、最初の列をオートサイズに設定し、すべてのラベルを右揃えにしました。これはうまく動作します。しかし、それは各TableLayoutPanel(明らかに)に対して個別に動作し、次のようになります:

TableLayoutPanel 1:

+--------+--------+
| Name 1 | Data 1 |
+--------+--------+
| Name 2 | Data 2 |
+--------+--------+
| Name 3 | Data 3 |
+--------+--------+

TableLayoutPanel 2:

+------------------+--------+
|      Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
|      Long Name 3 | Data 3 |
+------------------+--------+

最初の列のすべてを自動サイズ変更するときに、名前ラベルのすべてを検討する方法を探しています。

TableLayoutPanel 1:

+------------------+--------+
|           Name 1 | Data 1 |
+------------------+--------+
|           Name 2 | Data 2 |
+------------------+--------+
|           Name 3 | Data 3 |
+------------------+--------+

TableLayoutPanel 2:

+------------------+--------+
|      Long Name 1 | Data 1 |
+------------------+--------+
| Very Long Name 2 | Data 2 |
+------------------+--------+
|      Long Name 3 | Data 3 |
+------------------+--------+

各テーブルは異なるカテゴリの情報を表し、折りたたみ可能なパネルのコレクションを持つカスタムコントロール内にあるため(各カテゴリを個別に表示または非表示にすることができるため)、すべてのデータを1つのテーブルに入れることはできません。

私はコンテナコントロールのOnLayout()をオーバーライドすることでこれを達成しようとしていました。すべてのTableLayoutPanelsの最初の列を自動サイズに設定し、すべての幅を取得し、最大値を見つけて、最初の列をすべて固定サイズに設定しました最大幅のこれは機能しますが、すべての列が自動サイズ変更にジャンプしてから固定サイズに戻るたびにレイアウトが発生するたびに恐ろしいように見えます。

私は、各テーブルのControlAddedとControlRemovedをフックし、各子コントロールのSizeChanged、子コントロールのサイズが変更されたときを知って、何とか手動で列幅を設定する必要があると仮定していますが、正確な幅を確実に得る方法がわからない。

最初のメソッドのバリエーションを試しました - 最初の列のすべてのコントロールでGetPreferredSize()を使用して、最大の幅を見つけようとしましたが、すべての最初の列を固定サイズに設定しましたが、小さい。私はいくつかの余分な間隔を適用する必要がありますか?

TableLayoutPanelに実際に視覚的に適用せずに自動サイズ計算を実行するように求める方法は誰にも分かりますか?または、テーブルに横たわって、特定の幅のコントロールがあるというふりをする、ちょうどそれが考慮に入れられますか?実際のコントロールを追加することはできません。なぜなら、より多くのセルを割り当てたいからです。私はILSpyでソースを見てみましたが、うーん、それほど美味しくはありません。ほとんどの作業はTableLayoutクラスで行われているようですが、これはもちろん内部的なものであり、私はそれが何をしているかを追うことができませんでした。

すべてのアイデアありがとう...

3
追加された
ビュー: 1
私はあなたのテキストがフォントとテキストに基づいてどれくらい長くなるかを判断できることを知っています。すべてのテキストを最長で検索し、すべてのレイアウトパネルの最初の列の幅を手動で設定できますか?
追加された 著者 Daryl,
彼らの方法でアスキーテーブルの良い仕事...
追加された 著者 Daryl,
@ダリル:「私はあなたが知っていることは知っていますか」</私>は「あなたが知っている方法」または「方法を知っている」という意味ですか?あなたのアイデアは実行可能ですが、多分あなたはいくつかのコードでそれを例示する必要があります。
追加された 著者 Gert Arnold,
あまりにもあなたがWPFを使用することはできません悪い。これはWPFでは簡単です。
追加された 著者 Ritch Melton,
@Daryl:基本的には、ラベルにGetPreferredSize()を使用していたのですが、最も幅の広い要素には小さすぎる幅を与えているようでしたので、私はどこから来たかわからないけれど、いくつかのパッディングや何かのピクセルを無視しています。私はちょうど私が得るか何かの幅にちょうど+8を加えることができると思うが、方法があれば 'それを正しく行う'ことはいいだろう。また、私はコントロールが必ずしもラベルではない一般的な解決策を見出そうとしていました...おそらく少しでも有望であるかもしれませんが、テキストを測定することは私の直面する問題を解決するでしょう。
追加された 著者 Ashley,

3 答え

3
追加された
私はGetPreferredSize()を使っていました。なぜなら、テキストの有無にかかわらず、どのコントロールでも動作できるからです。しかし、提案をありがとう。
追加された 著者 Ashley,

この方法では、サイジングでちらつきやジャンプが起きることはありません。

public partial class Form1 : Form
{
    private readonly Timer _timer = new Timer();

    public Form1()
    {
        InitializeComponent();

        _timer.Interval = 500;
        _timer.Tick += (o, ea) => UpdateWithRandomSizes();
        _timer.Start();
    }

    private void UpdateWithRandomSizes()
    {
        var rand = new Random();
        label1.Text = new string('A', rand.Next(10));
        label2.Text = new string('B', rand.Next(10));
        label3.Text = new string('C', rand.Next(10));
        label4.Text = new string('D', rand.Next(10));

        tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.AutoSize;
        tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.AutoSize;
        var width1 = tableLayoutPanel1.GetColumnWidths()[0];
        var width2 = tableLayoutPanel2.GetColumnWidths()[0];

        var max = Math.Max(width1, width2);

        tableLayoutPanel1.ColumnStyles[0].Width = max;
        tableLayoutPanel1.ColumnStyles[0].SizeType = SizeType.Absolute;
        tableLayoutPanel2.ColumnStyles[0].Width = max;
        tableLayoutPanel2.ColumnStyles[0].SizeType = SizeType.Absolute;
    }
}
0
追加された
@アシュリー - ああ、それは吸う。 WinFormsにWPF機能と同等の機能が備わっているかどうか疑問に思ったので興味がありました。
追加された 著者 Ritch Melton,
うん、あなたはwinforms(flowlayout、databinding、発表者の分離)でかなりビットを行うことができますが、簡単にキック・ロットコントロールを堆肥化するために、WPF rocks
追加された 著者 Ritch Melton,
うーん、それは本質的に私が最初にやっていたものですが、それは私のためにうまくいっていませんでした。私はもう一度試してみましたが(実際にはコードをコピーして貼り付けました)、実際には2行だけで問題ありませんが、それぞれ15行しかない2つのテーブルを使用すると、レイアウトプロセス全体が1秒かかります。 1つのテーブルに長い行と1つのテーブルの短い行を付けると、「ジャンプする」ことがより明らかになります。私のコンピュータは正確に遅くはありません(クアッドコアPhenom II)...それはちょうど非効率なTableLayoutPanelですか?
追加された 著者 Ashley,
ラベルを変更する前にテーブル上のSuspendLayout()/ ResumeLayout()はジャンプ動作を排除するように見えますが、まだ1秒程度かかりますが、ビットごとに視覚的に再描画されます。多分ダブルバッファリングが描画を処理することができますが、速度は処理できません。
追加された 著者 Ashley,
私は本当に、本当にWPFに移動したいです。私の現在のプロジェクトでは、すでに80以上のカスタムコントロールを作成していますが、その75%以上がWPFで実装するには不必要または些細なものでした。残念ながら私は十分な機会をとるのに十分なほどアーキテクチャに精通していません。
追加された 著者 Ashley,

GetPreferredSize()によって返された幅は有用であり、それはちょうど「遅すぎました」。私は正しいサイズを試していて、TableLayoutPanelsのOnLayout()メソッドから呼び出されたコード内でそれを返し、次のレイアウトまで列幅を設定しても効果はありません。

私はテーブルを一緒に結合するために使用できるIExtenderProviderを実装する別個のコンポーネントを使用するソリューションを持っていましたが、上記の問題のために、常にコントロールの変更の後ろに遅れていました。すべての子コントロールにSizeChangedをフックしても、TableLayoutPanelはイベントを最初に取得し、レイアウトを開始します。

だから私は他の方法を見ることができませんでしたが、レイアウトプロセス自体をオーバーライドする。私は、必要な計算を行い、列のサイズを変更し、実際のレイアウト作業を古いレイアウトエンジンに委譲したLayoutEngineを作成しようとしましたが、残念ながらControl.LayoutEngineは読み取り専用であり、TableLayoutPanelはバッキングフィールド別のオブジェクトを直接返します。私は、リフレクションを使ってプライベートバッキングフィールドを割り当てても、それを回避することさえできませんでした。

最後に、OnLayout()をオーバーライドするために、コントロールをサブクラス化する必要がありました。結果は次のとおりです。

public class SynchronizedTableLayoutPanel : TableLayoutPanel
    {
    /// 
/// Specifies a key used to group s together. ///
 
    public String SynchronizationKey
        {
        get { return _SynchronizationKey; }
        set
            {
            if (!String.IsNullOrEmpty(_SynchronizationKey))
                RemoveSyncTarget(this);

            _SynchronizationKey = value;

            if (!String.IsNullOrEmpty(value))
                AddSyncTarget(this);
            }
        } private String _SynchronizationKey;

    #region OnLayout(), Recalculate()

    protected override void OnLayout(LayoutEventArgs levent)
        {
        if (ColumnCount > 0 && !String.IsNullOrEmpty(SynchronizationKey))
            {
            Recalculate();
            ColumnStyles[0] = new ColumnStyle(SizeType.Absolute, GetMaxWidth(SynchronizationKey));
            }

        base.OnLayout(levent);
        }

    public void Recalculate()
        {
        var LargestWidth = Enumerable.Range(0, RowCount)
            .Select(i => GetControlFromPosition(0, i))
            .Where(c => c != null)
            .Select(c => (Int32?)((c.AutoSize ? c.GetPreferredSize(new Size(Width, 0)).Width : c.Width)+ c.Margin.Horizontal))
            .Max();

        SetMaxWidth(this, LargestWidth.GetValueOrDefault(0));
        }

    #endregion

    #region (Static) Data, cctor, AddSyncTarget(), RemoveSyncTarget(), SetMaxWidth(), GetMaxWidth()

    private static readonly Dictionary Data;

    static SynchronizedTableLayoutPanel()
        {
        Data = new Dictionary();
        }

    private static void AddSyncTarget(SynchronizedTableLayoutPanel table)
        {
        Data.Add(table, 0);
        }

    private static void RemoveSyncTarget(SynchronizedTableLayoutPanel table)
        {
        Data.Remove(table);
        }

    private static void SetMaxWidth(SynchronizedTableLayoutPanel table, Int32 width)
        {
        Data[table] = width;

        foreach (var pair in Data.ToArray())
            if (pair.Key.SynchronizationKey == table.SynchronizationKey && pair.Value != width)
                pair.Key.PerformLayout();
        }

    private static Int32 GetMaxWidth(String key)
        {
        var MaxWidth = Data
            .Where(p => p.Key.SynchronizationKey == key)
            .Max(p => (Int32?) p.Value);

        return MaxWidth.GetValueOrDefault(0);
        }

    #endregion
    }

このバージョンでは、最初の列についてのみ扱いますが、他の列や行を同期させることもできます。

0
追加された