代码之家  ›  专栏  ›  技术社区  ›  Ron Inbar

如何在WinForms中绑定多对多关系?

  •  6
  • Ron Inbar  · 技术社区  · 6 年前

    我有以下数据集:

    Products and Parts

    这个 Product Part 可以使用以下DataGridView编辑表:

    Main Form

    当用户双击产品网格中的一行时,将打开以下表单:

    Product-Part Association

    左栏应列出与此产品相关的零件。右栏应列出所有其他部分。使用(<<和>&燃气轮机;按钮,用户应能够选择属于当前产品的部件。

    我在一对多关系中也做过类似的事情,效果很好。代码如下:

    public partial class ProductPartsForm : Form
    {
        private int _productID;
        private DataSet1 _data;
    
        public ProductPartsForm(DataSet1 data, DataRowView productRowView)
        {
            var productRow = (DataSet1.ProductRow)productRowView.Row;
            _productID = productRow.ID;
            _data = data;
            InitializeComponent();
            productBindingSource.DataSource = productRowView;
            assignedPartBindingSource.DataSource = productBindingSource;
            assignedPartBindingSource.DataMember = "FK_Product_Part";
            assignedPartsListBox.DisplayMember = "Name";
            unassignedPartBindingSource.DataSource = _data;
            unassignedPartBindingSource.DataMember = "Part";
            unassignedPartsListBox.DisplayMember = "Name";
            unassignedPartBindingSource.Filter = $"isnull(ProductID, 0) = 0";
        } 
    
        private void assignButton_Click(object sender, EventArgs e)
        {
            var partRowView = (DataRowView)unassignedPartBindingSource.Current;
            var partRow = (DataSet1.PartRow)partRowView.Row;
            var productRowView = (DataRowView)productBindingSource.Current;
            var productRow = (DataSet1.ProductRow)productRowView.Row;
            partRow.ProductRow = productRow;
            UpdateUI();
        }
    
        private void unassignButton_Click(object sender, EventArgs e)
        {
            var partRowView = (DataRowView)assignedPartBindingSource.Current;
            var partRow = (DataSet1.PartRow)partRowView.Row;
            partRow.SetProductIDNull();
            UpdateUI();
        }
    
        private void UpdateUI()
        {
            assignedPartsListBox.Refresh();
            unassignedPartsListBox.Refresh();
            assignButton.Enabled = unassignedPartsListBox.Items.Count > 0;
            unassignButton.Enabled = assignedPartsListBox.Items.Count > 0;
        }
    }
    

    在多对多关系中,有两件事我无法完成:

    • 左列不显示零件的名称。它应该显示小写字母,如右列;相反,它显示字符串 System.Data.DataRowView .我想通过某种查找来解决这个问题,但我不知道如何解决。
    • 当您按下 << ,选定零件将保留在右列上,而不是移动到左列。如果你试着按 << 同样,对于相同的零件,您会得到以下错误:

      系统数据ConstraintException:“Column”ProductID,PartID“被约束为唯一。值“-4,-3”已存在。”

      (这是可以理解的)。我认为这可以通过使用过滤器表达式来修复,但我不确定如何编写它,以及如何在每次更改后自动更新正确的列。

    有没有人做过类似的事情,能帮我指出正确的方向?

    1 回复  |  直到 6 年前
        1
  •  4
  •   Ron Inbar    6 年前

    这是我最后想到的。关键功能是 UpdateFilters ,它创建分配给当前产品的零件ID列表,然后使用 IN NOT IN 操作员。

    public partial class ProductPartsForm : Form
    {
        private int _productID;
        private DataSet1 _data;
    
        public ProductPartsForm(DataSet1 data, DataRowView productRowView)
        {
            var productRow = (DataSet1.ProductRow)productRowView.Row;
            _productID = productRow.ID;
            _data = data;
            InitializeComponent();
            productBindingSource.DataSource = productRowView;
            assignedPartBindingSource.DataSource = _data;
            assignedPartBindingSource.DataMember = "Part";
            assignedPartsListBox.DisplayMember = "Name";
            unassignedPartBindingSource.DataSource = _data;
            unassignedPartBindingSource.DataMember = "Part";
            unassignedPartsListBox.DisplayMember = "Name";
        }
    
        private void ProductPartsForm_Load(object sender, EventArgs e)
        {
            UpdateFilters();
            UpdateUI();
        }
    
        private void assignButton_Click(object sender, EventArgs e)
        {
            var partRowView = (DataRowView)unassignedPartBindingSource.Current;
            var partRow = (DataSet1.PartRow)partRowView.Row;
            var productRowView = (DataRowView)productBindingSource.Current;
            var productRow = (DataSet1.ProductRow)productRowView.Row;
            _data.ProductPart.AddProductPartRow(productRow, partRow);
            UpdateFilters();
            UpdateUI();
        }
    
        private void unassignButton_Click(object sender, EventArgs e)
        {
            var partRowView = (DataRowView)assignedPartBindingSource.Current;
            var partRow = (DataSet1.PartRow)partRowView.Row;
            var productPartRow = _data.ProductPart
                .Single(pp => pp.ProductID == _productID && pp.PartID == partRow.ID);
            _data.ProductPart.RemoveProductPartRow(productPartRow);
            UpdateFilters();
            UpdateUI();
        }
    
        private void UpdateFilters()
        {
            var assignedIds = _data.ProductPart
                .Where(pp => pp.ProductID == _productID)
                .Select(pp => pp.PartID.ToString());
            if (assignedIds.Any())
            {
                assignedPartBindingSource.Filter = $"ID IN ({string.Join(",", assignedIds)})";
                unassignedPartBindingSource.Filter = $"ID NOT IN ({string.Join(",", assignedIds)})";
            }
            else
            {
                assignedPartBindingSource.Filter = "FALSE";
                unassignedPartBindingSource.RemoveFilter();
            }
        }
    
        private void UpdateUI()
        {
            assignedPartsListBox.Refresh();
            unassignedPartsListBox.Refresh();
            assignButton.Enabled = unassignedPartsListBox.Items.Count > 0;
            unassignButton.Enabled = assignedPartsListBox.Items.Count > 0;
        }
    }