如何在 WinForms 中绑定多对多关系?
Posted
技术标签:
【中文标题】如何在 WinForms 中绑定多对多关系?【英文标题】:How to bind a many-to-many relation in WinForms? 【发布时间】:2018-10-13 23:17:15 【问题描述】:我有以下数据集:
Product
和 Part
表可以使用这些 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列表,然后使用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;
【讨论】:
以上是关于如何在 WinForms 中绑定多对多关系?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 NSFetchedResultsController 中使用多对多关系?