Synchronising TableLayoutPanels

本文关键字:TableLayoutPanels Synchronising | 更新日期: 2023-09-27 18:14:23

我有几个TableLayoutPanels,每一个都在两列中显示一个类别的名称/值信息——一列带有信息标签,另一列带有数据标签。

在每一个中,我都将第一列设置为自动调整大小并将所有标签右对齐,这很好。然而,它对每个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 |
+------------------+--------+

我不能把所有的数据放在一个表中,因为每个表代表一个不同的信息类别,并且在一个自定义控件中有一组可折叠面板(因此您可以单独显示或隐藏每个类别)。

我一直试图通过重写容器控件OnLayout()来实现这一点,设置所有的TableLayoutPanels的第一列自动大小,获得所有的宽度,找到最大值,然后设置所有的第一列的最大宽度的固定大小。这是有效的,但是每次布局发生时,所有列都跳到自动大小,然后又回到固定大小,看起来很糟糕。

我假设我将不得不为每个表挂钩ControlAdded和ControlRemoved,然后为每个子控件sizechange,知道什么时候任何子控件的大小改变,然后手动设置列宽度以某种方式,但我不确定如何可靠地获得正确的宽度。

我尝试了第一种方法的一种变体-在第一列中的所有控件上使用GetPreferredSize(),试图找到最大的宽度,然后将所有第一列设置为固定的大小,但它似乎返回的宽度略小。我应该再加一些间距吗?

有没有人知道任何方法要求TableLayoutPanel执行自动大小计算,而不实际应用他们的视觉?或者,向桌子撒谎,"假装"有一个特定宽度的控制,只是为了让它考虑到它?我不能添加实际控件,因为它会想要为它们分配更多的单元格。我试着用ILSpy查看源代码,但是,它不是很漂亮。似乎大部分工作是由TableLayout类完成的,当然,这是内部的,我无法遵循它在做什么。

Synchronising TableLayoutPanels

你可以使用图形。在不实际绘制它的情况下确定以像素为单位的长度。它有一些轻微的缺陷,所以你可能会考虑添加或删除一些填充。经过一两次测试,你就可以非常接近了。这是我所知道的最合适的一种方式,而且它不涉及将文本放在标签中。

此外,试图找到一种方法,让TableLayoutPanel计算大小,而不显示它的视觉只是听起来像你试图hack它做一些事情,它不是设计

事实证明GetPreferredSize() 返回的宽度是有用的,只是"太晚了";我正在计算出正确的大小,并在从TableLayoutPanels的OnLayout()方法调用的代码中返回它,并设置列宽度没有影响,直到下一个布局。

我有一个解决方案,使用一个单独的组件实现IExtenderProvider,可以用来将表连接在一起,但由于上面的问题,它总是落后于控件的变化。即使在所有子控件上挂钩sizechange, TableLayoutPanel也会首先获得事件,然后开始布局。

所以除了重写布局过程本身,我看不到任何其他方法。我尝试创建一个layoutenengine来执行必要的计算,调整列的大小,然后将实际的布局工作委托给旧的布局引擎,但不幸的是Control。LayoutEngine是只读的,而TableLayoutPanel甚至不使用后备字段,它只是直接返回另一个对象,所以我甚至不能通过使用反射来分配私有后备字段来绕过它。

最后,我不得不求助于子类化控件,以覆盖OnLayout()。结果如下:

public class SynchronizedTableLayoutPanel : TableLayoutPanel
    {
    /// <summary>
    /// Specifies a key used to group <see cref="SynchronizedTableLayoutPanel"/>s together.
    /// </summary>
    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<SynchronizedTableLayoutPanel, Int32> Data;
    static SynchronizedTableLayoutPanel()
        {
        Data = new Dictionary<SynchronizedTableLayoutPanel, Int32>();
        }
    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
    }

这个版本只关心第一列,但它可以被调整为同步其他列或行。

这种方法不会闪烁,也不会引起大小的跳跃:

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;
    }
}
相关文章:
  • 没有找到相关文章