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

How to bind a many-to-many relation in WinForms?

我有以下数据集:

可以使用这些 DataGridView 编辑 ProductPart 表:

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

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

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

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

对于多对多关系,有两件事我无法处理:

有没有人做过类似的事情,可以帮我指明正确的方向?

这是我终于想到的。关键函数是 UpdateFilters,它创建分配给当前产品的零件 ID 列表,然后使用 INNOT IN 运算符过滤两列 "manually"。

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