development

2 차원 배열을 기반으로 WPF 그리드를 채우는 방법

big-blog 2021. 1. 6. 20:42
반응형

2 차원 배열을 기반으로 WPF 그리드를 채우는 방법


2 차원 개체 배열이 있고 기본적으로 각 개체를 WPF 그리드의 셀에 데이터 바인딩하고 싶습니다. 현재 저는이 작업을하고 있지만 대부분 절차 적으로 수행하고 있습니다. 올바른 수의 행 및 열 정의를 만든 다음 셀을 반복하고 컨트롤을 만들고 각각에 대해 올바른 바인딩을 설정합니다.

최소한 템플릿을 사용하여 xaml에서 컨트롤과 바인딩을 지정할 수 있기를 바랍니다. 이상적으로는 절차 코드를 제거하고 데이터 바인딩으로 모든 작업을 수행하고 싶지만 이것이 가능할지 확신 할 수 없습니다.

현재 사용중인 코드는 다음과 같습니다.

public void BindGrid()
{
    m_Grid.Children.Clear();
    m_Grid.ColumnDefinitions.Clear();
    m_Grid.RowDefinitions.Clear();

    for (int x = 0; x < MefGrid.Width; x++)
    {
        m_Grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star), });
    }

    for (int y = 0; y < MefGrid.Height; y++)
    {
        m_Grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star), });
    }

    for (int x = 0; x < MefGrid.Width; x++)
    {
        for (int y = 0; y < MefGrid.Height; y++)
        {
            Cell cell = (Cell)MefGrid[x, y];                    

            SolidColorBrush brush = new SolidColorBrush();

            var binding = new Binding("On");
            binding.Converter = new BoolColorConverter();
            binding.Mode = BindingMode.OneWay;

            BindingOperations.SetBinding(brush, SolidColorBrush.ColorProperty, binding);

            var rect = new Rectangle();
            rect.DataContext = cell;
            rect.Fill = brush;
            rect.SetValue(Grid.RowProperty, y);
            rect.SetValue(Grid.ColumnProperty, x);
            m_Grid.Children.Add(rect);
        }
    }

}

그리드의 목적은 실제 데이터 바인딩을위한 것이 아니라 패널 일뿐입니다. 2 차원 목록을 시각화하는 가장 쉬운 방법을 나열하고 있습니다.

<Window.Resources>
    <DataTemplate x:Key="DataTemplate_Level2">
            <Button Content="{Binding}" Height="40" Width="50" Margin="4,4,4,4"/>
    </DataTemplate>

    <DataTemplate x:Key="DataTemplate_Level1">
        <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DataTemplate>

</Window.Resources>
<Grid>
    <ItemsControl x:Name="lst" ItemTemplate="{DynamicResource DataTemplate_Level1}"/>
</Grid>

그리고 뒤에있는 코드에서 TwoDimentional 데이터 구조로 lst의 ItemsSource를 설정합니다.

  public Window1()
    {
        List<List<int>> lsts = new List<List<int>>();

        for (int i = 0; i < 5; i++)
        {
            lsts.Add(new List<int>());

            for (int j = 0; j < 5; j++)
            {
                lsts[i].Add(i * 10 + j);
            }
        }

        InitializeComponent();

        lst.ItemsSource = lsts;
    }

그러면 다음 화면이 출력됩니다. DataTemplate_Level2를 편집하여 개체의 특정 데이터를 추가 할 수 있습니다.

대체 텍스트


다음은 DataGrid2D2D 또는
1D 배열 (또는 IList인터페이스 를 구현하는 모든 것)을 기반으로 채울 수 있는 Control 호출 입니다 . 2D 또는 1D 소스에 대한 바인딩에 사용되는 DataGrid속성을 하위 클래스로 만들고 추가 ItemsSource2D합니다. 라이브러리는 여기에서 다운로드 할 수 있으며 소스 코드는 여기에서 다운로드 할 수 있습니다 .

이를 사용하려면 DataGrid2DLibrary.dll에 대한 참조를 추가하고이 네임 스페이스를 추가하십시오.

xmlns:dg2d="clr-namespace:DataGrid2DLibrary;assembly=DataGrid2DLibrary"

그런 다음 DataGrid2D를 만들고 다음과 같이 IList, 2D 배열 또는 1D 배열에 바인딩합니다.

<dg2d:DataGrid2D Name="dataGrid2D"
                 ItemsSource2D="{Binding Int2DList}"/>

여기에 이미지 설명 입력


OLD POST
다음은 2D 배열을 WPF 데이터 그리드에 바인딩 할 수있는 구현입니다.

이 2D 배열이 있다고 가정 해 보겠습니다.

private int[,] m_intArray = new int[5, 5];
...
for (int i = 0; i < 5; i++)
{
    for (int j = 0; j < 5; j++)
    {
        m_intArray[i,j] = (i * 10 + j);
    }
}

그런 다음이 2D 배열을 WPF DataGrid에 바인딩하고 변경 사항이 배열에 반영됩니다. 이를 위해이 스레드 에서 Eric Lippert의 Ref 클래스를 사용 했습니다 .

public class Ref<T>  
{ 
    private readonly Func<T> getter;  
    private readonly Action<T> setter; 
    public Ref(Func<T> getter, Action<T> setter)  
    {  
        this.getter = getter;  
        this.setter = setter;  
    } 
    public T Value { get { return getter(); } set { setter(value); } }  
} 

그런 다음 2D 배열을 가져와 위의 Ref 클래스를 사용하여 DataView를 반환 할 수있는 메서드로 정적 도우미 클래스를 만들었습니다.

public static DataView GetBindable2DArray<T>(T[,] array)
{
    DataTable dataTable = new DataTable();
    for (int i = 0; i < array.GetLength(1); i++)
    {
        dataTable.Columns.Add(i.ToString(), typeof(Ref<T>));
    }
    for (int i = 0; i < array.GetLength(0); i++)
    {
        DataRow dataRow = dataTable.NewRow();
        dataTable.Rows.Add(dataRow);
    }
    DataView dataView = new DataView(dataTable);
    for (int i = 0; i < array.GetLength(0); i++)
    {
        for (int j = 0; j < array.GetLength(1); j++)
        {
            int a = i;
            int b = j;
            Ref<T> refT = new Ref<T>(() => array[a, b], z => { array[a, b] = z; });
            dataView[i][j] = refT;
        }
    }
    return dataView;
}

이것은 바인딩하기에 충분하지만 Binding의 Path는 필요한 Ref.Value 대신 Ref 개체를 가리 키므로 열이 생성 될 때이를 변경해야합니다.

<DataGrid Name="c_dataGrid"
          RowHeaderWidth="0"
          ColumnHeaderHeight="0"
          AutoGenerateColumns="True"
          AutoGeneratingColumn="c_dataGrid_AutoGeneratingColumn"/>

private void c_dataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
    DataGridTextColumn column = e.Column as DataGridTextColumn;
    Binding binding = column.Binding as Binding;
    binding.Path = new PropertyPath(binding.Path.Path + ".Value");
}

그 후에 우리는

c_dataGrid.ItemsSource = BindingHelper.GetBindable2DArray<int>(m_intArray);

출력은 다음과 같습니다.

대체 텍스트

에서 변경된 사항은 DataGridm_intArray에 반영됩니다.


.NET Framework에 대한 연결된 속성의 작은 라이브러리를 작성했습니다 DataGrid. 여기에 소스가 있습니다

샘플, 여기서 Data2D는 int[,]다음과 같습니다.

<DataGrid HeadersVisibility="None"
          dataGrid2D:Source2D.ItemsSource2D="{Binding Data2D}" />

렌더링 : 여기에 이미지 설명 입력


다음 링크를 확인하십시오. http://www.thinkbottomup.com.au/site/blog/Game_of_Life_in_XAML_WPF_using_embedded_Python

List 내에서 List를 사용하는 경우 myList [x] [y]를 사용하여 셀에 액세스 할 수 있습니다.


다음은 Meleak 의 답변을 기반으로 하지만 바인딩 된AutoGeneratingColumn 각 코드 뒤에 이벤트 핸들러가 필요하지 않은 또 다른 솔루션입니다 DataGrid.

public static DataView GetBindable2DArray<T>(T[,] array)
{
    var table = new DataTable();
    for (var i = 0; i < array.GetLength(1); i++)
    {
        table.Columns.Add(i+1, typeof(bool))
                     .ExtendedProperties.Add("idx", i); // Save original column index
    }
    for (var i = 0; i < array.GetLength(0); i++)
    {
        table.Rows.Add(table.NewRow());
    }

    var view = new DataView(table);
    for (var ri = 0; ri < array.GetLength(0); ri++)
    {
        for (var ci = 0; ci < array.GetLength(1); ci++)
        {
            view[ri][ci] = array[ri, ci];
        }
    }

    // Avoids writing an 'AutogeneratingColumn' handler
    table.ColumnChanged += (s, e) => 
    {
        var ci = (int)e.Column.ExtendedProperties["idx"]; // Retrieve original column index
        var ri = e.Row.Table.Rows.IndexOf(e.Row); // Retrieve row index

        array[ri, ci] = (T)view[ri][ci];
    };

    return view;
}

참조 URL : https://stackoverflow.com/questions/276808/how-to-populate-a-wpf-grid-based-on-a-2-dimensional-array

반응형