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

时间:2018-05-03 15:14:22

标签: c# winforms data-binding ado.net dataset

我有以下DataSet:

Products and Parts

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

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。我想使用某种查找来解决这个问题,但我不知道如何。
  • <<时,所选部分将保留在右列,而不是移至左列。如果您尝试使用相同的部件再次按<<,则会出现以下错误:

      

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

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

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

1 个答案:

答案 0 :(得分:4)

这是我最终想出来的。关键功能是UpdateFilters,它会创建一个分配给当前产品的零件ID列表,然后过滤两列&#34;手动&#34;使用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;
    }
}