【问题标题】:Implementing two-way DataGrid data binding without using MVVM在不使用 MVVM 的情况下实现双向 DataGrid 数据绑定
【发布时间】:2012-08-28 15:34:57
【问题描述】:

我会尽可能清楚地说明这一点,但我很清楚我可能问错了问题。我在 WPF (.NET 4.0) 中有一个 datagrd,我将它绑定到数据集中的数据表。相关的代码隐藏(在窗口加载时运行)是:

// create a connection to Top Trumps database
String cs = ConfigurationManager.ConnectionStrings["csTopTrumps"].ConnectionString;
SqlConnection cn = new SqlConnection(cs);

// create a new dataset
DataSet ds = new DataSet();

// open the connection (not strictly necessary, as the
// data adapter will do this when you use it anyway)
cn.Open();

// fill data table called Cards with contents of tblCard
SqlDataAdapter daCards = new SqlDataAdapter();
daCards.SelectCommand =
new SqlCommand("SELECT * FROM tblCard ORDER BY CardTitle", cn);
daCards.Fill(ds, "Cards");

// now set the data context for the entire window
this.DataContext = ds;

我的数据网格定义开始:

<DataGrid ItemsSource="{Binding Tables[Cards]}"

所有这些都很好,我理解。我现在想以两种方式进行数据绑定,以便在数据网格中所做的更改将更新回底层数据库。我知道我可以做到这一点的一种方法是获取已更改行的值并使用带有 Update 方法的 tableadapter 将所做的任何更改写回底层数据库(可能是在 SelectedCellsChanged 事件或类似事件上)。但是,任何这样的方法都很难实现,因为在 WPF 的代码隐藏中获取单元格值很困难(我的理解是这是错误的方法,因为我应该使用 WPF 框架,所以请帮助我)。

那么...我如何设置双向数据绑定来为我完成工作?我见过有人提到设置数据网格属性,例如:

SelectedItem="{Binding Path=SelectedIndex,Mode=TwoWay}"

但我不明白如何让它与数据适配器和数据集一起使用。我不想要任何涉及 MVVM 或模型的解决方案,因为我没有使用这些(是的,我可能应该使用,我知道)。我还想避免大量复杂的 C# 代码,包括可观察的集合、通过控制层次结构递归搜索以查找父对象等。

我已经搜索了很长时间,我认为我所要求的确实存在 - 如果不存在,请告诉我它不存在,并为浪费您的时间道歉,亲爱的读者!

【问题讨论】:

    标签: wpf binding datagrid


    【解决方案1】:

    我完全不明白你。您希望避免使用 WPF 框架(可观察集合、绑定、递归搜索等),但您想找到一种使用 WPF 框架的方法。

    首先你需要这些功能:

    public static T GetVisualChild<T>(Visual parent) where T : Visual
    {
        T child = default(T);
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
           if (child != null)
           {
               break;
           }
       }
           return child;
    }
    
    public static DataGridRow GetSelectedRow(this DataGrid grid)
    {
        return (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);
    }
    public static DataGridRow GetRow(this DataGrid grid, int index)
    {
        DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        if (row == null)
        {
            // May be virtualized, bring into view and try again.
            grid.UpdateLayout();
            grid.ScrollIntoView(grid.Items[index]);
            row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(index);
        }
        return row;
    }
    
    public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
    {
        if (row != null)
        {
            DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);
    
            if (presenter == null)
            {
                grid.ScrollIntoView(row, grid.Columns[column]);
                presenter = GetVisualChild<DataGridCellsPresenter>(row);
            }
    
            DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
            return cell;
        }
        return null;
    }
    
    public static DataGridCell GetCell(this DataGrid grid, int row, int column)
    {
        DataGridRow rowContainer = grid.GetRow(row);
        return grid.GetCell(rowContainer, column);
    }
    

    现在 DataGrid 实际上有几乎有用的事件。它被称为 CellEditEnding。 把它挂起来

    private bool isManualEditCommit;
    private void yourHandler(
      object sender, DataGridCellEditEndingEventArgs e) 
    {
     if (!isManualEditCommit) 
     {
      isManualEditCommit = true;
      DataGrid grid = (DataGrid)sender;
      grid.CommitEdit(DataGridEditingUnit.Row, true);
      isManualEditCommit = false;
     } else {
        /* you can write foreach loop that would loop through all the DataGridCells
          with DataGridCell cell = grid.GetCell(row, col),
          you can recieve value like GetVisualChild<TextBlock>(cell).Text
          and update everything when you're at last value */
     }
    }
    

    也读过这个; WPF DataGrid CellEditEnding - DataSet Not Updating Till Row Lost Focus

    这是一种艰难的方式,但如果您有经验,您可以提供更好的代码。 尝试搜索术语数据绑定、datagrid、datagridtemplatecolumn、双向绑定、cellEditEnding。

    祝你好运!

    【讨论】:

    • 非常感谢。周五我下次去办公室的时候试试这个。相信我,我已经搜索了您提到的所有内容(我已经知道 WinForms 和 ASP.NET,但正在努力寻找 WPF 的前进方向)。有机会看这篇文章时会添加另一条评论。
    • 这就像我担心的那样复杂。为什么我不能只使用: private void dg_MouseDoubleClick(object sender, MouseButtonEventArgs e) { // 创建指向当前数据行的指针 DataRowView dr = dg.SelectedItem as DataRowView; // 显示绑定到 CardTitle 字段的列 MessageBox.Show(dr["CardTitle"].ToString());它似乎工作(给出字段值);为什么这种方法不好?
    【解决方案2】:

    好吧,有点晚了,我想我有我想要的答案了。数据网格绑定到数据表(在我的实例中)。这意味着您可以获取所选项目(可能是用户双击的行),将其转换为数据行,然后获取您感兴趣的字段。

    private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    

    {

    // turn the datagrid selected "item" into a data row
    DataRowView dr = dg.SelectedItem as DataRowView;
    
    // get at the value for any field (here it's the one called CARD) in the underlying datatable
    string CardTitle = (string)dr["Card"];
    
    // display it ...
    MessageBox.Show("You are on card " + CardTitle);
    

    }

    这将解决双击数据网格中一行的问题,这就是我的想法。如果您使用类,则可以将所选项目转换为相关对象,并获取其属性。

    我希望这对某人有帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 2018-07-27
      • 1970-01-01
      • 2018-05-22
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多