Home > Blockchain >  Combobox data binding inside ItemsControl
Combobox data binding inside ItemsControl

Time:09-15

I have combobox inside a ItemsControl. How to bind the selected value of the combobox to the ItemsControl items with object not with int value. When i change the combobox everything is working well but after changing the PersonList with the new gender objects when the button clicked, combobox is not selected with the correct gender. I know when the new genders are added, binding is not working. But in my real situation the data are taken from database so the gender list is refreshed with the new one. How to create those kind of binding with object ?

Gender Class

    public class Gender : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            private int id { get; set; }
            public int Id { get { return id; } set { id = value; OnPropertyChanged(nameof(Id)); } }
    
            private string name { get; set; }
            public string Name { get { return name; } set { name = value; OnPropertyChanged(nameof(Name)); } }
    
            protected virtual void OnPropertyChanged(string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

Person Class

public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string name { get; set; }
        public string Name { get { return name; } set { name = value; OnPropertyChanged(nameof(Name)); } }

        private string surname { get; set; }
        public string Surname { get { return surname; } set { surname = value; OnPropertyChanged(nameof(Surname)); } }

        private Gender personGender { get; set; }
        public Gender PersonGender { get { return personGender; } set { personGender = value; OnPropertyChanged(nameof(PersonGender)); } }

xml protected virtual void OnPropertyChanged(string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }

Window xaml

    <Window x:Class="ComboText.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:ComboText"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="2*"/>
                <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>
            <ItemsControl Grid.Row="0" x:Name="myGrid" ItemsSource="{Binding PersonList}" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate >
                        <Grid Margin="10,10,0,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="1*"/>
                                <ColumnDefinition Width="2*"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Grid.Column="0" TextWrapping="WrapWithOverflow" Margin="20,0,20,0" Text="{Binding Name}" />
                            <TextBlock Grid.Column="1" TextWrapping="WrapWithOverflow" Margin="10,0,20,0" Text="{Binding Surname}" />
                            <TextBlock Grid.Column="3" TextWrapping="WrapWithOverflow" Margin="10,0,20,0" Text="{Binding PersonGender.Id}" />
                            <ComboBox  Grid.Column="4" x:Name="tagComboBox" MinWidth="100" Margin="10,0,20,0"  SelectedValue="{Binding PersonGender, Mode=TwoWay}"  ItemsSource="{Binding GenderList, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" DisplayMemberPath="Name"></ComboBox>
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <Button Grid.Row="1" Click="Button_Click">CLICK</Button>
        </Grid>
    </Window>

Window code behind

 public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public ObservableCollection<Person> personList { get; set; }
        public ObservableCollection<Person> PersonList { get { return personList; } set { personList = value; OnPropertyChanged(nameof(PersonList)); } }

        public ObservableCollection<Gender> genderList { get; set; }
        public ObservableCollection<Gender> GenderList { get { return genderList; } set { genderList = value; OnPropertyChanged(nameof(GenderList)); } }

        //Gender male, female, none;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            PersonList = new ObservableCollection<Person>();
            GenderList = new ObservableCollection<Gender>();

            Person sahin = new Person { Name = "Sahin", Surname = "Atam" };
            Person seda = new Person { Name = "Seda", Surname = "Atam" };
            Person umran = new Person { Name = "Ümran", Surname = "Atam" };

            personList.Add(sahin);
            personList.Add(seda);
            personList.Add(umran);


            Gender male = new Gender { Id=1, Name = "Male" };
            Gender female = new Gender { Id=2, Name = "Female" };
            Gender none = new Gender {Id=3, Name = "None" };

            GenderList.Add(male);
            GenderList.Add(female);
            GenderList.Add(none);
        }

        //When propert changes, notify
        protected virtual void OnPropertyChanged(string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
          Gender genderMale = new Gender {Id=1, Name = "Male" };
          Gender genderFemale = new Gender {Id=2, Name = "Female" };

           Person ali = new Person { Name = "Ali", Surname = "Atam" ,PersonGender=genderMale};
           Person muk= new Person { Name = "Mukaddes", Surname = "Atam" , PersonGender= genderFemale };

            PersonList.Clear();
            PersonList.Add(ali);
            PersonList.Add(muk);

        }
    }

CodePudding user response:

Pick the existing Gender objects from the GenderList instead of creating new ones that are not included in the list of selectable genders:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Gender genderMale = GenderList.FirstOrDefault(x => x.Id == 1);
    Gender genderFemale = GenderList.FirstOrDefault(x => x.Id == 2);

    Person ali = new Person { Name = "Ali", Surname = "Atam", PersonGender = genderMale };
    Person muk = new Person { Name = "Mukaddes", Surname = "Atam", PersonGender = genderFemale };

    PersonList.Clear();
    PersonList.Add(ali);
    PersonList.Add(muk);
}

CodePudding user response:

Conceptually, you approached the implementation incorrectly. Gender - must be either a ValueType or an immutable DTO. The Gender list must also be an immutable list created once at application startup.

One possible implementation:

using System;
using System.Collections.Generic;

namespace Core2022.SO.nihasmata
{
    public class GenderDto
    {
        public int Id { get; }

        public string Name { get; }

        private GenderDto(int id, string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException("name");
            Id = id;
            Name = name;
        }

        public static IReadOnlyList<GenderDto> Genders { get; } = Array.AsReadOnly(new GenderDto[]
        {
            new GenderDto (0, "None"),
            new GenderDto (1,"Male"),
            new GenderDto (2,"Female")
        });
    }
}
using Simplified;

namespace Core2022.SO.nihasmata
{
    public class Person : BaseInpc
    {
        private string _name = string.Empty;
        private string _surname = string.Empty;
        private GenderDto _gender = GenderDto.Genders[0];
        public string Name { get => _name; set => Set(ref _name, value ?? string.Empty); }

        public string Surname { get => _surname; set  => Set(ref _surname, value ?? string.Empty); }

        public GenderDto Gender { get => _gender; set  => Set(ref _gender, value ?? GenderDto.Genders[0]); }
    }
}

The BaseInpc class. is my implementation of the interface.

XAML:

<ComboBox SelectedItem="{Binding Gender}"
          ItemsSource="{x:Static ns:GenderDto.Genders}"
          ......>
</ComboBox>
  • Related