0

I'm a beginner , just trying to understand MVVM , but I'm stuck on the many-to-many relationship. There is a rental table and a car table between which there are several to several connections. When I create a new rental , I select the cars I want to add from a listbox . But I don't know how to select those cars from the listbox and add them to the rental. Sorry if my phrasing is a bit complicated , but my English is not the best.

This is what the listbox looks like in AddEditRental.xaml :

<ListBox x:Name="CarsListBox" 
         Grid.Row="5" 
         ItemsSource="{Binding Cars}" 
         SelectedItem="{Binding SelectedCars, Mode=TwoWay}" 
         SelectionMode="Multiple"/>

This is what the RentalEditVM.cs looks like :

using RentalCar.Model;
using RentalCar.View;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Navigation;

namespace RentalCar.ViewModel
{
    internal class RentalEditVM : Entity
    {
        public ObservableCollection<Car> Cars { get; set; }

        private Rental? rental;
        public ListBox ListBox { get; set; }
        public RentalEditVM()
        {
            Cars = new ObservableCollection<Car>();
            Rental = new Rental();
        }

        public RentalEditVM(ObservableCollection<Car> cars, Rental rental = null)
        {
            Cars = cars;
            Rental = rental ?? new Rental();
        }

        public Rental? Rental
        {
            get { return rental; }
            set { SetProperty(ref rental, value); }
        }
    }
}

And finally this is what MainVM.cs looks like :

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore.Query;
using RentalCar.Logic;
using RentalCar.Model;
using RentalCar.View;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace RentalCar.ViewModel
{
    internal class MainVM : ObservableRecipient
    {
        private MainLogic logic;
        public ObservableCollection<Car> Cars { get; set; }
        public ObservableCollection<Rental> Rentals { get; set; }
        public ObservableCollection<Owner> Owners { get; set; }
        public ICommand CreateOwner { get; }
        public ICommand CreateRental { get; }
        public ICommand CreateCar { get; }
        public ICommand Delete { get; }
        public ICommand Edit { get; }
        public MainVM()
        {
            logic = new MainLogic();
            Cars = new ObservableCollection<Car>();
            Rentals = new ObservableCollection<Rental>();
            Owners = new ObservableCollection<Owner>();
            Cars.CollectionChanged += ObservableCollectionChanged;
            Rentals.CollectionChanged += ObservableCollectionChanged;
            Owners.CollectionChanged += ObservableCollectionChanged;
            logic.Load(Rentals, Cars, Owners);
            CreateOwner = new RelayCommand(() =>
            {
                var w = new AddEditOwner();
                if (w.ShowDialog() == true)
                {
                    var owner = (Owner)w.DataContext;
                    if (logic.Save(owner))
                    {
                        Owners.Add(owner);
                    }
                }
            });

            CreateRental = new RelayCommand(() =>
            {
                var w = new AddEditRental();
                w.DataContext = new RentalEditVM(Cars);
                if (w.ShowDialog() == true)
                {
                    var rental = ((RentalEditVM)w.DataContext).Rental;
                    if (logic.Save(rental))
                    {
                        Rentals.Add(rental);
                    }
                }
            });

            CreateCar = new RelayCommand(() =>
            {
                var w = new AddEditCar();
                w.DataContext = new CarEditVM(Owners);
                if (w.ShowDialog() == true)
                {
                    var car = ((CarEditVM)w.DataContext).Car;
                    if (logic.Save(car))
                    {
                        Cars.Add(car);
                    }
                }
            });

            Delete = new RelayCommand<Entity>(entity =>
            {
                if(entity != null && ConfirmAction())
                {
                    logic.Delete(entity);
                }
            });

            Edit = new RelayCommand<Entity>(entity =>
            {
                if (entity == null) return;
                Window w;

                if(entity is Car)
                {
                    w = new AddEditCar();
                    var vm = w.DataContext as CarEditVM;
                    w.DataContext = new CarEditVM(Owners , SelectedCar!);
                }
                else if(entity is Rental)
                {
                    w = new AddEditRental();
                    var vm = w.DataContext as RentalEditVM;
                    w.DataContext = new RentalEditVM(Cars, SelectedRental!);
                }
                else
                {
                    w = new AddEditOwner();
                }
                if(w.ShowDialog() == true) 
                { 
                    if(!logic.Save(entity)) 
                    {
                        logic.RevertChanges(entity);
                    }
                }
            });
        }

        private void ObservableCollectionChanged(object? sender , NotifyCollectionChangedEventArgs e)
        {
            if(e.NewItems != null)
            {
                foreach (ObservableObject x in e.NewItems)
                {
                    x.PropertyChanged += Entity_PropertyChanged;
                }
            }
            if(e.OldItems != null)
            {
                foreach (ObservableObject x in e.OldItems)
                {
                    x.PropertyChanged -= Entity_PropertyChanged;
                }
            }
        }

        private void Entity_PropertyChanged(object? sender , System.ComponentModel.PropertyChangedEventArgs e)
        {
            if(e.PropertyName == "DeletedAt")
            {
                if(sender is Car)
                {
                    Cars.Remove(sender as Car);
                }
                else if(sender is Rental)
                {
                    Rentals.Remove(sender as Rental);
                }
                else
                {
                    Owners.Remove(sender as Owner);
                }
            }
        }

        private bool ConfirmAction()
        {
            return MessageBoxResult.Yes == MessageBox.Show("Are you sure about this?", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
        }

        private Car? selectedCar;
        private Rental? selectedRental;
        private Owner? selectedOwner;

        public Car? SelectedCar
        {
            get { return selectedCar; }
            set { SetProperty(ref selectedCar, value); }
        }

        public Rental? SelectedRental
        {
            get { return selectedRental; }
            set { SetProperty(ref selectedRental, value); }
        }

        public Owner? SelectedOwner
        {
            get { return selectedOwner; }
            set { SetProperty(ref selectedOwner, value); }
        }
    }
}

I hope someone can help with this , because I have been suffering with it for a while

I've tried several ways to solve it , but no way , watched videos , talked to ChatGPT , but no way to solve it.

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Did you try binding ListBox' SelectedItems property to MainVM's Rentals? – o_w May 01 '23 at 10:34
  • No , because I don't know how can I access my ListBox from MainVM – Deák Attila May 01 '23 at 10:35
  • Using Binding. You already do that with ItemsSource from xaml. Try binding SelectedItems instead of SelectedItem. SelectedItems="{Binding Rentals, Mode=TwoWay}" – o_w May 01 '23 at 10:38
  • Yeah , but when I try SelectedItems it says "The property SelectedItems does not have an accessible setter" , I get this error – Deák Attila May 01 '23 at 10:40
  • Right, I forgot you can't bind it, sorry. Use SelectedItemsChange event. You can place EventToCommandBehavior under ListBox's Behaviors in xaml. If EventToCommandBehavior is not available you can search online for an existing implementation(its a small piece of code), and it'll raise a method in your VM when the selection inside the ListBox changes. – o_w May 01 '23 at 10:56
  • The easiest is to handle the SelectionChanged event in your view. Then call a method on the view model to pass in the selected items as an argument. – BionicCode May 01 '23 at 16:31
  • Another solution is to add a IsSelected property to the data model and bind it to the ListBoxItem.IsSelected. Then commit the selection using a Button and a command. In the command handler you filter the selected items from the source collection. Both solutions are MVVM compliant. – BionicCode May 01 '23 at 16:33
  • 1
    Thank you guys the solutions , now I downloaded a Nuget package (Microsoft.Xaml.Behaviors.Wpf) , and with that I could create Interaction triggers , and I could assign a command to my listbox with a commandparameter , and with that I easily could bind the listbox to my viewmodel , sorry for my English , I hope you understand what I want to say. – Deák Attila May 01 '23 at 16:39

0 Answers0