Как связать отношение «многие ко многим» в WinForms? - PullRequest
0 голосов
/ 03 мая 2018

У меня есть следующий набор данных:

Products and Parts

Таблицы Product и Part можно редактировать с помощью следующих DataGridViews:

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: 'Столбец' ProductID, PartID 'должен быть уникальным. Значение '-4, -3' уже присутствует. '

    (что понятно). Я думаю, что это можно исправить с помощью выражения фильтра, но я не уверен, как его написать и как автоматически обновлять правый столбец после каждого изменения.

Кто-нибудь делал что-то подобное и может помочь мне указать верное направление?

1 Ответ

0 голосов
/ 08 мая 2018

Вот что я наконец-то придумал. Ключевой функцией является UpdateFilters, которая создает список идентификаторов деталей, назначенных текущему продукту, а затем фильтрует два столбца «вручную», используя операторы 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;
    }
}
...