Home > Back-end >  C# binding nested object to the DataGrid Combo box cell Item Source
C# binding nested object to the DataGrid Combo box cell Item Source

Time:11-25

I'm trying to build simple IT Tickets system that uses a class of Contacts - people who raise the ticket to the IT, it's the trivial class

public class Contact
{
    private int id;
    private string firstName;
    private string lastName;
    private string email;
    private string department;
    
    public int Id { get { return id; } set { id = value; } }
    public string FirstName { get { return firstName; } set { firstName = value; } }
    public string LastName { get { return lastName; } set { lastName = value; } }
    public string Email { get { return email; } set { email = value; } }    
    public string Departmet { get { return department; } set { department = value; } }

    public override string ToString()
    {
        return $"{FirstName} {LastName}";
    }
}

then I create the collection of Contact objects

public class Contacts : ObservableCollection<Contact>
{
    public Contacts()
    {
        Add(new Contact()
        {
            Id = 1,
            FirstName = "Name1",
            LastName = "LastName1",
            Email = "[email protected]",
            Departmet = "IT"
        });
        Add(new Contact()
        {
            Id = 2,
            FirstName = "Name2",
            LastName = "LastName2",
            Email = "[email protected]",
            Departmet = "I2"
        }); ;
        Add(new Contact()
        {
            Id = 1,
            FirstName = "Name3",
            LastName = "LastName3",
            Email = "[email protected]",
            Departmet = "IT"
        });
    }
}

and I have a Ticket class that references Contacts collection class

public class Ticket : IEditableObject
{
    private int id = 0;
    private Contact contact;
    private int contactId;
    private string subject;
    private string description;
    private Status status;
    private Severity severity;
    private Category category;


    public int Id  { get { return id; }  set {  id  ; } }
    public Contact Contact { get { return contact; } set { contact = value; } }
    public int ContactId { get { return contact.Id; } set { contactId = value; } }
    public string Subject { get { return subject; } set { subject = value; } }
    public string Description { get { return description; } set { description = value; } }
    public Status Status { get { return status; } set { status = value; } }
    public Severity Severity { get { return severity; } set { severity = value; } }
    public Category Category { get { return category; } set { category = value; } }

    // constructor
    public Ticket()
    {
    
    }

}

Finally I create the collection of Ricket objects and reference Contact = contactList[id]

public class TicketSystem : ObservableCollection<Ticket>
{
    private Contacts contactList = new Contacts();
    public TicketSystem()
    {
        Add(new Ticket()
        {
            Contact = contactList[1],
            ContactId = contactList[1].Id,
            Subject = "Subject",
            Description = "xxxx yyyy",
            Severity = Severity.Low,
            Status = Status.New,
            Category = Category.Hardware
        }); 
        Add(new Ticket()
        {
            Contact = contactList[2],
            ContactId = contactList[2].Id,
            Subject = "Subject 2",
            Description = "zzzz uuuuuu",
            Severity = Severity.Low,
            Status = Status.New,
            Category = Category.Hardware
            });
    }
}

I managed to bind all contact objects to the items source but the proper contact name does not bind, I think I'm missing something, the aim is that contact name associated to the ticket is presented and it can be changed via combobox

enter image description here

enter image description here

<DataGridComboBoxColumn Header="Contact" ItemsSource="{Binding Source={StaticResource contacts}}" SelectedItemBinding="{Binding Contact}"   Width="100" />

I tried different Bindings with no luck, any suggestions appreciated

CodePudding user response:

You have not configured to compare Contact instances by value. Therefore, if the Ticket.Contact property contains an element not from the contacts resource, then they will be considered different, despite the equality of their values. From your code (Sharpe and XAML), we can conclude that the contacts resource in XAML and the contacts list that you access when creating a TicketSystem are different collections. And the elements in them are not equal to each other.

I would suggest two solutions.
1) Using a static list:

    public static class Helper 
    {
        public static ObservableCollection<Contact> Contacts { get; } = new ObservableCollection<Contact>()
        {
            new Contact()
            {
                Id = 1,
                FirstName = "Name1",
                LastName = "LastName1",
                Email = "[email protected]",
                Departmet = "IT"
            },
            new Contact()
            {
                Id = 2,
                FirstName = "Name2",
                LastName = "LastName2",
                Email = "[email protected]",
                Departmet = "I2"
            },
            new Contact()
            {
                Id = 1,
                FirstName = "Name3",
                LastName = "LastName3",
                Email = "[email protected]",
                Departmet = "IT"
            }
        };
    }
<DataGridComboBoxColumn Header="Contact"
                        ItemsSource="{x:Static local:Helper.Contacts}" .../>
public class TicketSystem : ObservableCollection<Ticket>
{
    private Contacts contactList = Helper.Contacts;

2) Or set the binding only for the contact ID:

<DataGridComboBoxColumn Header="Contact"
                        ItemsSource="{Binding Source={StaticResource contacts}}"
                        SelectedValueBinding="{Binding ContactId}"
                        SelectedValuePath="Id"
                        Width="100" />

P.S. Also keep in mind that for TicketSystem you will most likely have the same problem.
Therefore, it is better to replace this class with a property in Helper similar to the one I showed for Contacts.

P.S.S. It is typical for WPF to use MVVM. In this case, the Contacts and Tickets properties must be declared in the ViewModel. And the View should not work with separate sources for each collection, but with one single instance of the ViewModel, which has all the necessary sources.

  • Related