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

Posted

技术标签:

【中文标题】如何在 WinForms 中绑定多对多关系?【英文标题】:How to bind a many-to-many relation in WinForms? 【发布时间】:2018-10-13 23:17:15 【问题描述】:

我有以下数据集:

ProductPart 表可以使用这些 DataGridViews 进行编辑:

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

左栏应该列出与该产品相关的部件。右栏应该列出所有其他部分。使用 > 按钮,用户应该能够选择哪些部件属于当前产品。

我用一对多的关系做了类似的事情,而且效果很好。代码如下:

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。我想使用某种查找来解决此问题,但我不知道如何。

当您按下<< 时,选定的部分会停留在右列而不是移动到左列。如果您尝试使用相同的部分再次按<<,则会收到以下错误:

System.Data.ConstraintException: 'Column 'ProductID, PartID' 被限制为唯一。值 '-4, -3' 已经存在。'

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

有没有人做过类似的事情并且可以帮助我指出正确的方向?

【问题讨论】:

如果您可以发布单独的问题而不是将您的问题合并为一个问题,则最好。这样,它可以帮助人们回答您的问题,也可以帮助其他人至少寻找您的一个问题。谢谢! 我不知道如何分解它。我有点希望整个问题会有某种教科书式的解决方案;类似于 UI 的主细节样式,但用于多对多关系而不是一对多。我会尽量缩小范围。 好吧,你至少应该edit 这个问题来显示<<>> 按钮的点击事件处理程序,以及productBindingSource 引用的表。 “我试图改写这个问题。新问题在这里。”,请不要问重复的问题!删除重复项并使用重复项的信息编辑此问题 【参考方案1】:

这是我最终想出的。关键函数是UpdateFilters,它创建分配给当前产品的部件ID列表,然后使用INNOT 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;
    

【讨论】:

以上是关于如何在 WinForms 中绑定多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章

雄辩的多对多对多 - 如何轻松加载远距离关系

如何在 NSFetchedResultsController 中使用多对多关系?

来自 DataTemplate 的 WPF 数据绑定 - 多对多关系

如何在 EF Core 中查询多对多关系

如何在 Django 中向多对多关系中添加字段?

如何避免在 SQLAlchemy - python 的多对多关系表中添加重复项?