سؤال

لدي مجموعة ثنائية الأبعاد من الكائنات وأريد بشكل أساسي ربط كل كائن بخلية في شبكة 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);
        }
    }

}
هل كانت مفيدة؟

المحلول

والغرض من الشبكة ليس لربط البيانات الحقيقي، انها مجرد لوحة. أنا قائمة باستمرار أسهل طريقة لتحقيق التصور من قائمة الأبعاد

<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>

وفي رمز وراء تعيين ItemsSource من سفينة من نفس الطراز مع بنية بيانات TwoDimentional.

  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 لإضافة المزيد من البيانات المحددة من وجوه الخاص بك.

نصائح أخرى

هنا عنصر تحكم يسمى DataGrid2D التي يمكن ملؤها على أساس ثنائي الأبعاد أو
مصفوفة 1D (أو أي شيء ينفذ IList واجهه المستخدم).انها فئات فرعية DataGrid ويضيف خاصية تسمى ItemsSource2D والذي يستخدم للربط مع المصادر ثنائية أو أحادية الأبعاد.يمكن تنزيل المكتبة هنا ويمكن تنزيل كود المصدر هنا.

لاستخدامه فقط قم بإضافة مرجع إلى DataGrid2DLibrary.dll، أضف مساحة الاسم هذه

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

ثم قم بإنشاء DataGrid2D وربطه بـ IList أو صفيف ثنائي الأبعاد أو صفيف 1D مثل هذا

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

enter image description here


وظيفة قديمة
فيما يلي تطبيق يمكنه ربط مصفوفة ثنائية الأبعاد بشبكة بيانات WPF.

لنفترض أن لدينا هذه المجموعة ثنائية الأبعاد

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);
    }
}

وبعد ذلك نريد ربط هذه المصفوفة ثنائية الأبعاد بـ WPF DataGrid وستنعكس التغييرات التي نجريها في المصفوفة.للقيام بذلك، استخدمت فئة مرجع إريك ليبرت من هذا خيط.

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); } }  
} 

ثم قمت بإنشاء فئة مساعدة ثابتة بطريقة يمكن أن تأخذ مصفوفة ثنائية الأبعاد وتعيد DataView باستخدام فئة Ref أعلاه.

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;
}

قد يكون هذا كافيًا تقريبًا للربط ولكن المسار الموجود في الرابط سيشير إلى كائن Ref بدلاً من Ref.Value الذي نحتاجه لذلك يتعين علينا تغيير هذا عند إنشاء الأعمدة.

<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);

وسيبدو الناتج هكذا

alt text

أي تغييرات يتم إجراؤها في DataGrid سوف تنعكس في m_intArray.

وكتبت مكتبة صغيرة تضم مباني ملحقة لل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

إذا كنت تستخدم قائمة ضمن قائمة يمكنك استخدام قائمتي [س] [ص] الوصول إلى الخلية.

وهنا يتم حل آخر يعتمد على Meleak الصورة الجواب ولكن دون الحاجة لمعالج أحداث AutoGeneratingColumn في التعليمات البرمجية خلف من كل DataGrid binded:

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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top