【问题标题】:Binding data in WPF在 WPF 中绑定数据
【发布时间】:2014-01-04 13:58:06
【问题描述】:

我正在尝试使用 c# 制作简单的 WPF 应用程序,但遇到以下问题: 在我的应用程序中,我使用“Macierz”类来表示和使用 BigInteger 对象数组。现在我需要使用 DataGrid 以图形方式表示这个数组(或者如果有更好的方法可以使用其他方法),我不知道如何将这个 DataGrid 绑定到我的类。我曾尝试通过数据源窗口(VS 2013)执行此操作,但是当我将我的类添加到其中时,我的数组没有显示。 这是我班级的一些代码:

public class Macierz
{
    public Macierz(ulong A, ulong B)
    {
        a = A;
        b = B;
        M = new BigInteger[A, B];
    }


    private ulong a, b;
    public ulong A
    {
        get
        {
            return a;
        }
    }

    public ulong B
    {
        get
        {
            return b;
        }
    }

    private BigInteger[,] M;

    public BigInteger this[ulong A, ulong B]
    {
        get
        {
            return M[A, B];
        }
        set
        {
            M[A, B] = value;
        }
    }

我需要用文本框或类似的单元格表示数组,以便用户可以轻松更改特定单元格的值。

编辑: 现在,感谢 user3148019 的回答,我在课堂上添加了这个方法:

 public DataTable ToDataTable()
    {
        DataTable table = new DataTable();
        DataRow tmp;
        for (ulong i = 0; i < b; i++)
            table.Columns.Add(i.ToString(), typeof(BigInteger));

        for (ulong i = 0; i < a; i++)
        {
            tmp = table.NewRow();
            for (ulong j = 0; j < b; j++)
                tmp[j.ToString()] = M[i, j];

            table.Rows.Add(tmp);
        }
        return table;
    }

所以现在我可以使用 DataGrid 显示我的数组,但这需要两倍的内存,并且用户无法更改原始数组中的任何值。我试图更改我的类,用 DataTable 对象替换简单的 BigInteger[a, b] 数组,但访问特定单元格时遇到问题。例如:

public BigInteger tmp = 0;
for (ulong i = 0; i < a; i++)

            for (ulong j = 0; j < b; j++)
            {
                tmp += table.Rows[i].ItemArray[j];
            }

这会产生错误:

错误 1 ​​'System.Data.DataRowCollection.this[int]' 的最佳重载方法匹配有一些无效参数
错误 2 参数 1:无法从 'ulong' 转换为 'int'

DataTable 对象中的行数和列数是否限制为 int 的最大值,还是有其他方法可以访问 DataTable 成员?

编辑2:
我应该早点说,Macierz 永远不会更改数组内的任何值,因此只有用户可以更改单元格女巫的值,这意味着我不需要 INotifyCollectionChange(或者我错了吗?)。现在我也认为显示完整的数组并不总是必要的。我不希望 Macierz 从 DependencyObject 派生,因为我想在其他控制台应用程序中使用它。我想以最简单的方式实现我的目标。这就是我现在在 Macierz 拥有的:

 public DataTable ToDataTable(ulong AStart, ulong ACount, ulong BStart, ulong BCount)
    {
        if (AStart >= a)
            AStart = a - 1;
        if (BStart >= b)
            BStart = b - 1;

        ulong AStop = AStart + ACount;
        ulong BStop = BStart + BCount;

        if (AStop > a)
            AStop = a;
        if (BStop > b)
            BStop = b;

        DataTable table = new DataTable();
        DataRow tmp;
        BigInteger BItmp;

        for (ulong i = BStart; i < BStop; i++)
            table.Columns.Add(i.ToString(), typeof(BigInteger));
        table.Columns.Add("Suma", typeof(BigInteger));

        for (ulong i = AStart; i < AStop; i++)
        {
            tmp = table.NewRow();
            for (ulong j = BStart; j < BStop; j++)
                tmp[j.ToString()] = M[i, j];

            BItmp = 0;
            for (ulong j = 0; j < B; j++)
                BItmp += M[i, j];
            tmp["Suma"] = BItmp; 

            table.Rows.Add(tmp);
        }
        return table;
    }

在 MainWindow.xaml.cs 中:

private void PokazButton_Click(object sender, RoutedEventArgs e)
    {
        this.Cursor = Cursors.Wait;

        if (czyCala.IsChecked == true)
            GridTabela = M.ToDataTable(0, M.A, 0, M.B);
        else
            GridTabela = M.ToDataTable(VA, VACount, VB, VBCount);

        TabelaDataGrid.DataContext = this;
        TabelaDataGrid.ItemsSource = GridTabela.DefaultView;

        this.Cursor = null;
    }

这很好用,允许我以简单的方式向用户显示数组,但我现在不知道如何允许用户更改原始数组中的值。我试图使用 CellEditEnding DataGrid 事件,但我现在不知道如何访问、读取和验证用户在特定单元格中所做的输入,尤其是如何获取该单元格的坐标(我可能应该以某种方式获取这些信息,例如“e. Column.something" 或 "e.Row.something" 但我不知道该怎么做)。

【问题讨论】:

    标签: c# wpf data-binding wpfdatagrid


    【解决方案1】:

    如果没有额外的工作,数组无法绑定到 DataGrid。一种快速的解决方案是使用 DataTable 而不是多维数组:

    <Window x:Class="StackExchange.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid AutoGenerateColumns="True" Name="myGrid"/>
    </Grid>
    

    public DataTable Macierz;
    
    public MainWindow()
    {
        InitializeComponent();
    
        Macierz = new DataTable("Table");
        Macierz.Columns.Add("Col1", typeof(int));
        Macierz.Columns.Add("Col2", typeof(int));
        Macierz.Columns.Add("Col3", typeof(int));
    
        DataRow row1 = Macierz.NewRow();
        row1["Col1"] = 1;
        row1["Col2"] = 2;
        row1["Col3"] = 3;
        Macierz.Rows.Add(row1);
    
        DataRow row2 = Macierz.NewRow();
        row2["Col1"] = 4;
        row2["Col2"] = 5;
        row2["Col3"] = 6;
        Macierz.Rows.Add(row2);
    
        myGrid.DataContext = this;
        myGrid.ItemsSource = Macierz.DefaultView;
    }
    

    【讨论】:

      【解决方案2】:
      1. Macierz 应该派生自 DependencyObject

      2. 不能使用多维数组进行绑定

      3. 使用ObservableCollection&lt;&gt; 而不是简单的数组

      4. 使用DependencyProperty 而不是简单的数据类型

      5. 将DataGrid 的DataContext 设置为所需的Macierz 实例


      编辑:

      你问的问题很笼统,要找到一个好的答案,我建议你试试这个方法,当你掌握了窍门后,再问一个关于你可能遇到的其他问题的问题。例如如何将 ulong 用作 ObservableCollection 的 ListSelector,或者如何将页面添加到 ItemsControl,或者如何使用具有动态列数的 DataGrid。

      在我开始之前,我必须说这个答案只适用于两个维度都是 int 的小尺寸 Macierz,而对于 A*B > 10000,它变得非常慢。

      这里的想法是创建一个ItemsControl,这是一种用于显示ObservableCollection的基本容器类型:

      • ItemsSource:集合的来源(你绑定的)
      • ItemTemplate:每个项目的模板(如何显示集合的每个项目,我用过TextBlock(在Border内),因为它加载速度比TextBox快)
      • ItemContainerStyle:每个项目容器的样式。 (Grid.Row 和 Grid.Column 必须设置为此成员提供的样式。)
      • ItemsPanel:itemsControl 的面板模板。 (我使用了具有动态行数和列数的Grid,并使用了GridHelper)

      现在你可以有一个 M 的一维集合了。这个集合的泛型类型应该配备RowIndex 和ColumnIndex 以便每个元素都知道它属于Grid 中的哪个位置。

      这是 MainWindow.xaml.cs:

      InitializeComponent();
      var mac = new Macierz(100,40);
      
      //fill with test data
      for (int i = 0; i < mac.A; i++)
      {
          for (int j = 0; j < mac.B; j++)
          {
              mac[i, j].Data = string.Format("{0}...{1}", i, j);
          }
      }
      
      //set DataContext
      this.DataContext = mac;
      

      这是 MainWindow.xaml:

      <Window x:Class="WpfApplication1.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:local="clr-namespace:WpfApplication1"
              Title="MainWindow" Height="350" Width="525">
          <Grid>
              <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                  <ItemsControl ItemsSource="{Binding M}">
                      <ItemsControl.ItemTemplate>
                          <DataTemplate>
                              <Border BorderBrush="Gray" BorderThickness="1">
                                  <TextBlock Text="{Binding Data}"/>
                              </Border>
                          </DataTemplate>
                      </ItemsControl.ItemTemplate>
                      <ItemsControl.ItemContainerStyle>
                          <Style>
                              <Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
                              <Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
                          </Style>
                      </ItemsControl.ItemContainerStyle>
                      <ItemsControl.ItemsPanel>
                          <ItemsPanelTemplate>
                              <Grid local:GridHelper.RowCount="{Binding A}" 
                                   local:GridHelper.ColumnCount="{Binding B}">
                              </Grid>
                          </ItemsPanelTemplate>
                      </ItemsControl.ItemsPanel>
                  </ItemsControl>
              </ScrollViewer>
          </Grid>
      </Window>
      

      这是 Macierz 课程:

      public class Macierz : DependencyObject
      {
          public Macierz(int A, int B)
          {
              this.A = A;
              this.B = B;
              for (int i = 0; i < A; i++)
              {
                  for (int j = 0; j < B; j++)
                  {
                      M.Add(new BigInteger(i, j));
                  }
              }
          }
      
          //A Dependency Property
          public int A
          {
              get { return (int)GetValue(AProperty); }
              private set { SetValue(AProperty, value); }
          }
          public static readonly DependencyProperty AProperty =
              DependencyProperty.Register("A", typeof(int), typeof(Macierz), new UIPropertyMetadata(0));
          //B Dependency Property
          public int B
          {
              get { return (int)GetValue(BProperty); }
              private set { SetValue(BProperty, value); }
          }
          public static readonly DependencyProperty BProperty =
              DependencyProperty.Register("B", typeof(int), typeof(Macierz), new UIPropertyMetadata(0));
          //Rows Observable Collection
          public ObservableCollection<BigInteger> M { get { return _m; } }
          private ObservableCollection<BigInteger> _m = new ObservableCollection<BigInteger>();
      
          public BigInteger this[int i, int j]
          {
              get { return M[i * B + j]; }
              set { M[i * B + j] = value; }
          }
      }
      

      这是 BigInteger 类:

      //replace this class with your own implementation.
      //derive from DependencyObject and use DependencyProperty to store data
      public class BigInteger : DependencyObject
      {
          public BigInteger(int row, int col)
          {
              RowIndex = row;
              ColumnIndex = col;
          }
          //Data Dependency Property
          public string Data
          {
              get { return (string)GetValue(DataProperty); }
              set { SetValue(DataProperty, value); }
          }
          public static readonly DependencyProperty DataProperty =
              DependencyProperty.Register("Data", typeof(string), typeof(BigInteger), new UIPropertyMetadata(""));
          //RowIndex Dependency Property
          public int RowIndex
          {
              get { return (int)GetValue(RowIndexProperty); }
              set { SetValue(RowIndexProperty, value); }
          }
          public static readonly DependencyProperty RowIndexProperty =
              DependencyProperty.Register("RowIndex", typeof(int), typeof(BigInteger), new UIPropertyMetadata(0));
          //ColumnIndex Dependency Property
          public int ColumnIndex
          {
              get { return (int)GetValue(ColumnIndexProperty); }
              set { SetValue(ColumnIndexProperty, value); }
          }
          public static readonly DependencyProperty ColumnIndexProperty =
              DependencyProperty.Register("ColumnIndex", typeof(int), typeof(BigInteger), new UIPropertyMetadata(0));
      }
      

      您还需要一个辅助类。这是 GridHelper 的简化版本。你可以在Rachel的blog找到原来的类:

      public class GridHelper
      {
          #region RowCount Property
      
          /// <summary>
          /// Adds the specified number of Rows to RowDefinitions. 
          /// Default Height is Auto
          /// </summary>
          public static readonly DependencyProperty RowCountProperty =
              DependencyProperty.RegisterAttached(
                  "RowCount", typeof(int), typeof(GridHelper),
                  new PropertyMetadata(-1, RowCountChanged));
      
          // Get
          public static int GetRowCount(DependencyObject obj)
          {
              return (int)obj.GetValue(RowCountProperty);
          }
      
          // Set
          public static void SetRowCount(DependencyObject obj, int value)
          {
              obj.SetValue(RowCountProperty, value);
          }
      
          // Change Event - Adds the Rows
          public static void RowCountChanged(
              DependencyObject obj, DependencyPropertyChangedEventArgs e)
          {
              if (!(obj is Grid) || (int)e.NewValue < 0)
                  return;
      
              Grid grid = (Grid)obj;
              grid.RowDefinitions.Clear();
      
              for (int i = 0; i < (int)e.NewValue; i++)
                  grid.RowDefinitions.Add(
                      new RowDefinition() { Height = GridLength.Auto });
      
              //SetStarRows(grid);
          }
      
          #endregion
      
          #region ColumnCount Property
      
          /// <summary>
          /// Adds the specified number of Columns to ColumnDefinitions. 
          /// Default Width is Auto
          /// </summary>
          public static readonly DependencyProperty ColumnCountProperty =
              DependencyProperty.RegisterAttached(
                  "ColumnCount", typeof(int), typeof(GridHelper),
                  new PropertyMetadata(-1, ColumnCountChanged));
      
          // Get
          public static int GetColumnCount(DependencyObject obj)
          {
              return (int)obj.GetValue(ColumnCountProperty);
          }
      
          // Set
          public static void SetColumnCount(DependencyObject obj, int value)
          {
              obj.SetValue(ColumnCountProperty, value);
          }
      
          // Change Event - Add the Columns
          public static void ColumnCountChanged(
              DependencyObject obj, DependencyPropertyChangedEventArgs e)
          {
              if (!(obj is Grid) || (int)e.NewValue < 0)
                  return;
      
              Grid grid = (Grid)obj;
              grid.ColumnDefinitions.Clear();
      
              for (int i = 0; i < (int)e.NewValue; i++)
                  grid.ColumnDefinitions.Add(
                      new ColumnDefinition() { Width = GridLength.Auto });
      
              //SetStarColumns(grid);
          }
      
          #endregion
      }
      

      【讨论】:

      • 我将我的类更改为从 DependencyObject 派生,但我不知道下一步该做什么。我应该用这样的东西替换我的数组: ObservableCollection> 吗?我只想表示这个数组,为什么要使用 DependencyProperty 而不是简单的数据类型?
      • 绑定仅适用于那些可以通知视图更改的对象,因此数据类型应该在它们更改时引发事件或者是 DependencyProperties 以便能够自动通知。您可以使用 ObservableCollection> 但有更简单的方法。看看我对我的帖子所做的编辑。
      猜你喜欢
      • 2015-07-07
      • 1970-01-01
      • 2017-01-10
      • 1970-01-01
      • 1970-01-01
      • 2020-07-20
      • 1970-01-01
      相关资源
      最近更新 更多