1

Логика функциональности следующая. У меня есть есть 2 Datagrid'a, один изначально имеет какие-либо значения (в моём случае это товары). Второй — пустой. Грубо говоря, я выбираю товары из 1-ого Datagrid, и они добавляются во второй, который является своего рода чеком, что выбрал пользователь при заказе:

Скрин Datagrid

Вот как должно работать:

Пример

Я делаю MVVM-структуру в своём приложении, поэтому делаю всё через привязки и команды, так что для меня в первый раз сделать это является довольно проблематичным.

Сначала создаются 2 ViewModel, первая отвечает за первый Datagrid, вторая — за второй соответственно.

Код ViewModel для первого Datagrid:

internal class BW_FirstDataGridViewModel : PropertyChangedHelper
{
    #region Properties
    private FabricFromCheck selectedFabric = new FabricFromCheck();
    public FabricFromCheck SelectedFabric
    {
        get { return selectedFabric; }
        set
        {
            selectedFabric = value;
            OnPropertyChanged();
            AddFabric();
        }
    }
private ObservableCollection<FabricFromCheck> _firstDataGridItems;
public ObservableCollection<FabricFromCheck> FirstDataGridItems
{
    get { return _firstDataGridItems; }
    set
    {
        _firstDataGridItems = value;
        OnPropertyChanged();
    }
}
#endregion

/*// Команда для добавления элемента во второй датагрид
public AddSelectedItemCommand<FabricFromCheck> AddItemCommand { get; }*/

public ICommand AddItemCommand { get; set; }

public BW_FirstDataGridViewModel()
{
    /*AddItemCommand = new AddSelectedItemCommand<FabricFromCheck>(BW_SecondDataGridViewModel.Instance);*/
    AddItemCommand = new RelayCommand(AddFabric);

    FirstDataGridItems = new ObservableCollection<FabricFromCheck>()
    {
        new FabricFromCheck("Стол", "500", 15),
        new FabricFromCheck("Комод", "500", 35),
        new FabricFromCheck("Стул", "500", 25),
        new FabricFromCheck("Табурет", "543", 15),
        new FabricFromCheck("Шкаф", "345", 10),
        new FabricFromCheck("Диван", "565", 4),
        new FabricFromCheck("Столешница", "3545", 7)
    };
}

public void AddFabric()
{
    if (SelectedFabric == null)
    {
        MessageBox.Show("Товар не выбран");
        return;
    }
    FabricToCheck item = new FabricToCheck(SelectedFabric);
    BW_SecondDataGridViewModel.Instance.SecondDataGridItems.Add(item);
}

}

Вторая ViewModel:

internal class BW_SecondDataGridViewModel : PropertyChangedHelper
{
    private ObservableCollection<FabricToCheck> _secondDataGridItems;
    public ObservableCollection<FabricToCheck> SecondDataGridItems
    {
        get { return _secondDataGridItems; }
        set
        {
            _secondDataGridItems = value;
            OnPropertyChanged();
        }
    }
// Создаем единственный экземпляр модели представления (Singleton)
public static BW_SecondDataGridViewModel Instance { get; } = new BW_SecondDataGridViewModel();

public BW_SecondDataGridViewModel()
{
    SecondDataGridItems = new ObservableCollection&lt;FabricToCheck&gt;();
}

}

Класс, который наследую для OnPropertyChanged():

public class PropertyChangedHelper : INotifyPropertyChanged
{
    ////////////////////////////////////////////////////////////////////
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Классы, использованные в ViewModels (FabricFromCheck && FabricToCheck):

internal class FabricFromCheck
{
    public string Name { get; set; }
    public string Size { get; set; }
    public int Amount { get; set; }
    public FabricFromCheck(string name, string size, int amount)
    {
        Name = name;
        Size = size;
        Amount = amount;
    }
    public FabricFromCheck() { }
}

internal class FabricToCheck { public string Name { get; set; } public string Size { get; set; } public FabricToCheck(string name, string size) { Name = name; Size = size; } public FabricToCheck(FabricFromCheck selected) { Name = selected.Name; Size = selected.Size; } }

По идее, метод срабатывает, и на строчке ниже выбранный элемент действительно передаётся, но в итоге в список для Второго Datagrid он не добавляется:

BW_SecondDataGridViewModel.Instance.SecondDataGridItems.Add(item);

XAML:

<DataGrid SelectedItem="{Binding SelectedFabric}" ItemsSource="{Binding FirstDataGridItems}" x:Name="AllFabrics_DataGrid" Grid.Row="1" Margin="7,0,0,0" IsReadOnly="True" AutoGenerateColumns="False" SelectionChanged="AllFabrics_DataGrid_SelectionChanged">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding AddItemCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
&lt;DataGrid.Columns&gt;
    &lt;DataGridTextColumn Header=&quot;Название&quot; Binding=&quot;{Binding Name}&quot;/&gt;
    &lt;DataGridTextColumn Header=&quot;Размер&quot; Binding=&quot;{Binding Size}&quot;/&gt;
    &lt;DataGridTextColumn Header=&quot;Количество&quot; Binding=&quot;{Binding Amount}&quot;/&gt;
&lt;/DataGrid.Columns&gt;

</DataGrid>

<DataGrid ItemsSource="{Binding SecondDataGridItems}" x:Name="Fabrics_InCheck_DataGrid" Grid.Row="1" Grid.Column="2" Margin="0,0,7,0" IsReadOnly="True" AutoGenerateColumns="False" > <DataGrid.Columns> <DataGridTextColumn Header="Название"/> <DataGridTextColumn Header="Размер"/> </DataGrid.Columns> </DataGrid>

Я в привязках и командах новичок, было бы очень приятно получить ответ от людей знающих. Долго промучался, всё равно не работает. Что-то делаю не так, только вот что?

Gonzy
  • 21
  • Намудрили, очень. 1. У вашего окна где-то указывается один конкретный DataContext, он у вас скорей всего ссылается на BW_FirstDataGridViewModel, значит свойства будут искаться в нем. FirstDataGridItems вижу, SelectedFabric вижу, AddItemCommand вижу, а SecondDataGridItems не вижу, хотя вы вот берете и запрашиваете ее {Binding SecondDataGridItems} из этого класса. Наверно стоит задать публичное свойство, ссылающееся на BW_SecondDataGridViewModel, и привязываться уже как {Binding Свойство.SecondDataGridItems}? И тут мы кстати понимаем, что синглтон нам и не нужен вовсе... – EvgeniyZ May 27 '23 at 21:29
  • Синглтон, зачем он тут? (вы его кстати не правильно реализовали). Мне кажется, он у вас возник в следствии того, что вы не смогли передать данные из объекта А в объект Б. Что мешает сделать условно class A { public A(B bType){ ... } } ... class B { .... A a = new A(this); ... } (создали конструктор, передали туда нужную зависимость при создании)? 3. А зачем вы вообще разделяете одну логику, на несколько классов? Вы ведь в BW_SecondDataGridViewModel создали лишь одно свойство, которое обернули в INPC, да еще и синглтон, почему этому свойству не лежится в BW_FirstDataGridViewModel?
  • – EvgeniyZ May 27 '23 at 21:33
  • Не пишите в названиях имена контролов, подчеркивания, и так далее. Вот ваш класс BW_FirstDataGridViewModel отвечает за DataGrid, или всеж за корзину? Я думаю 2-е... Так почему просто не назвать CartViewModel? Смотрите как легко и просто читается, и сразу понято что это, за что отвечает. 5. Ну и самое важное, подобная логика делается совершенно иначе, она делается при помощи ICollectionView и фильтрации данных (пример), где данные одни, фильтруются на 2 разные коллекции, одна которая выводит товары с флагом "Выбран", а другая наоборот.
  • – EvgeniyZ May 27 '23 at 21:38
  • Большое вам спасибо, разобрался. Всё было действительно проще, чем кажется – Gonzy May 28 '23 at 15:52