2011-11-12 3 views
3

나는 여러 개의 TableLayoutPanels을 가지고 있는데, 각각은 정보 라벨이있는 라벨과 데이터 라벨이있는 라벨의 두 열에 이름/값 정보 카테고리를 표시합니다.TableLayoutPanels 동기화하기

각 항목에서 첫 번째 열을 자동 크기 조정으로 설정하고 모든 레이블을 오른쪽 정렬하여 올바르게 작동합니다. 첫 번째 열 모든을 자동 크기 때이 이름 라벨의 모든을 고려하는 방법을 찾고 있어요

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 (명백하게)에 개별적으로 작동하고, 다음과 같이 보입니다 그래서 그것은 다음과 같습니다

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를 연결 한 다음 각 자식 컨트롤에 대해 SizeChanged를 사용하여 자식 컨트롤의 크기가 변경된 시점을 알 고 수동으로 열 너비를 설정해야한다고 가정합니다. ,하지만 나는 정확한 너비를 안정적으로 얻는 방법을 모르겠습니다.

첫 번째 메서드의 변형 - 첫 번째 열의 모든 컨트롤에서 GetPreferredSize()를 사용하여 가장 큰 너비를 찾고 모든 첫 번째 열을 고정 된 크기로 설정하려고 시도했지만 반환하는 것처럼 보였습니다. 너비가 약간 작았습니다. 몇 가지 추가 간격을 적용해야합니까?

TableLayoutPanel에 시각적으로 실제로 적용하지 않고 자동 크기 조정 계산을 수행하도록 요청하는 방법을 아는 사람이 있습니까? 아니면 테이블에 누워서 특정 폭의 통제가 있다는 것을 '가장 할'수 있습니다. 단지 그것을 고려합니다. 실제 컨트롤을 추가 할 수 없기 때문에 더 많은 셀을 할당하려고합니다. 나는 ILSpy로 소스를 보았지만, 꽤 좋지는 않습니다. 대부분의 작업이 TableLayout 클래스에 의해 수행되는 것처럼 보입니다. 내부 클래스는 물론 내부적으로 수행 할 수 없습니다. 어떤 아이디어에 대한

감사합니다 ...

+1

그들은 아스키 테이블에서 잘 작동합니다 ... – Daryl

+0

너무 나쁘면 WPF를 사용할 수 없습니다. 이것은 WPF에서 사소한 일입니다. –

+1

글자와 글자를 기준으로 텍스트의 길이를 결정할 수 있습니다. 가장 긴 텍스트를 찾으려면 모든 텍스트에서이 작업을 수행 한 다음 모든 레이아웃 패널의 첫 번째 열 너비를 수동으로 설정할 수 있습니까? – Daryl

답변

0

GetPreferredSize()에 의해 반환 된 너비가 인 것이 밝혀졌습니다.이 유용했습니다. 그냥 너무 늦었습니다. 나는 올바른 크기를 알아 내고 TableLayoutPanels의 OnLayout() 메서드에서 호출 된 코드 내에서 반환했습니다. 그리고 열 너비를 설정하면 다음에 레이아웃까지 아무런 효과가 없습니다.

IExtenderProvider를 구현하는 별도의 구성 요소를 사용하여 테이블을 조인하는 데 사용할 수있는 솔루션이 있지만 위의 문제로 인해 항상 컨트롤 변경보다 뒤처졌습니다. 모든 자식 컨트롤에 SizeChanged를 연결하는 경우에도 TableLayoutPanel은 이벤트를 먼저 가져오고 레이아웃을 시작합니다.

그래서 다른 방법은 보이지 않지만 레이아웃 프로세스 자체를 재정의 할 수는 없습니다. 필요한 계산을 수행하고 열의 크기를 조정 한 다음 실제 레이아웃 작업을 이전 레이아웃 엔진에 위임 한 LayoutEngine을 만들려고 시도했지만 불행히도 Control.LayoutEngine은 읽기 전용이며 TableLayoutPanel은 백킹 필드를 사용하지 않습니다. 다른 객체를 직접 반환하므로 리플렉션을 사용하여 private backing 필드를 할당함으로써 주변을 둘러 볼 수조차 없습니다.

결국에는 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 
    } 

이 버전은 첫 번째 열만 고려하지만 다른 열 또는 행을 동기화하도록 조정할 수 있습니다.

0

이러한 접근 방식은 깜빡하지 않거나 원인은 크기 점프 :

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

흠, 그것이 본질적으로 원래하고 있었던 것이지만 그것은 나에게 잘 맞지 않습니다. 방금 다시 시도 했으므로 (실제로 코드를 복사하고 붙여 넣기 만하면됩니다.) 실제로 두 행만으로도 충분하지만 두 행의 테이블이 각각 15 행으로 구성되어 있기 때문에 전체 레이아웃 프로세스가 거의 순간적으로 완료되어 매우 눈에.니다. '점프'는 한 테이블에 긴 행과 한 테이블에 짧은 행을 지정하면 더 분명합니다. 내 컴퓨터가 정확하게 느리지는 않습니다 (쿼드 코어 신동 II) ... 그냥 비효율적 인 TableLayoutPanel입니까? – Ashley

+0

레이블을 변경하기 전에 테이블의 SuspendLayout()/ResumeLayout()이 점핑 동작을 제거하는 것으로 보입니다.하지만 여전히 거의 1 초가 걸리고 시각적으로 다시 그려집니다. 아마도 더블 버퍼링이 그림을 처리 할 수 ​​있지만 속도는 처리 할 수 ​​없습니다. – Ashley

+0

@Ashley - 아, 그거 끔찍해. WinForms가이 작업을 사소하게 만드는 WPF 기능과 동등한 기능을 갖고 있는지 궁금해서 흥미가있었습니다. –

3

당신은 실제로 그리지 않고 픽셀의 길이를 결정하기 위해 Graphics.Measurestring를 사용할 수 있습니다. 일부는 slight imperfections with it이므로 일부 패딩을 추가하거나 제거하는 것에 대해 생각할 수 있습니다. 한 두 번 시험을 치르더라도 꽤 가까워 질 수 있습니다. 그것은 내가 아는 한 적절한 방법이며, 텍스트에 라벨이 포함되지 않습니다.

또한 시각적으로 표시하지 않고 크기를 계산할 수있는 TableLayoutPanel을 얻으려는 시도는 설계되지 않은 작업을 해킹하려고하는 것처럼 들립니다.

+0

나는 모든 컨트롤과 함께 사용할 수 있기 때문에 GetPreferredSize()로 붙어있다. 또는 텍스트없이. 생각해 줘서 고마워. – Ashley

관련 문제