0

I have DataGrid filled from ObservableCollection and I have binded Buttons but the same solution does not work with ComboBox. I already tried few fixes but every time ComboBox is empty inside.

XAML:

<DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
    <DataGridTemplateColumn Header="Monday" Width="auto">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <StackPanel>

                    <ComboBox x:Name="comboBoxShift" Margin="10,0,0,0" DisplayMemberPath="{Binding Path=Shifts_value}" SelectedValuePath="{Binding Path=Shifts_id}" SelectedValue="{Binding Path=Shifts_selected}" VerticalAlignment="Top" Height="25" Width="auto" FontSize="10" DropDownClosed="comboBoxShift_DropDownClosed">             
                    </ComboBox>

                    <Button Name="ButtonStandby" Margin="10 0 0 0"  Content="Standby" Height="25" Width="auto" IsEnabled="True" FontSize="10" FontWeight="UltraBold" Background="{Binding Path=day1_f_standby}">
                    </Button>
                </StackPanel>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
</DataGrid.Columns>

Binding in code:

public class CalendarGlobals
{
     public static ObservableCollection<Person> currentTeamOC { get; set; }
}   

    public TeamScheduleWindow()
{ 
     InitializeComponent();
     dataGrid.ItemsSource = CalendarGlobals.currentTeamOC; 
}

ObservableCollection code:

public class Person
{
    public int day1_id_ca { get; set; }
    public int day1_f_shift { get; set; }
    public string day1_f_wfh { get; set; }
    public string day1_f_standby { get; set; }
    public int day1_f_edited { get; set; }
    public string day1_note { get; set; }

    public DataTable Shifts { get; set; }
    public List<string> Shifts_id { get; set; }
    public List<string> Shifts_value { get; set; }
    public List<string> Shifts_color { get; set; }
    public string Shifts_selected { get; set; }

    public Person(DataTable day1, DataTable shifts)
    {
        string[,] colors = new string[,]
        {
            {"Bisque", "BlueViolet"}, 
            {"Bisque", "BlueViolet"}, 
        };

        foreach (DataRow row in day1.Rows)
        {
            this.day1_id_ca = Convert.ToInt32(row["id_ca"]);
            this.day1_f_shift = Convert.ToInt16(row["field_shift"]);
            this.day1_f_wfh = colors[0,Convert.ToInt16(row["field_wfh"])];
            this.day1_f_standby = colors[1, Convert.ToInt16(row["field_standby"])];
            this.day1_f_edited = Convert.ToInt16(row["edited_by_user_id"]);
            this.day1_note = row["note"].ToString();
        }
//I tried to bind from DataTable, later from list - nothing worked.
        this.Shifts = shifts;

        this.Shifts_id = shifts.AsEnumerable().Select(x => x[0].ToString()).ToList();
        this.Shifts_value = shifts.AsEnumerable().Select(x => x[1].ToString()).ToList();
        this.Shifts_color = shifts.AsEnumerable().Select(x => x[2].ToString()).ToList();
    }
}

I've removed irrelevant code to make this post shorter. Thank you in advance for any help.

EDIT: Thanks @MKloster for help

I have used your solution with success but I have added new objects to list in ObservableCollection code

        Shifts = new BindingList<Shift> ();
        foreach (DataRow row in shifts.Rows)
        {
            Shifts.Add(new Shift { Value = row["value"].ToString(), ID = Convert.ToInt16(row["id_value"]), Color = row["color"].ToString() });
        }
Rasiel
  • 17
  • 5
  • I don't see the ItemsSource of the combobox. Am i missing something? – Babbillumpa Jul 12 '18 at 13:14
  • How do you add `Person` objects to the ObservableCollection? And also, where do you initialize the ObservableCollection? In your code it is null. – Lupu Silviu Jul 12 '18 at 13:14
  • @Babbillumpa the binding is here I think `dataGrid.ItemsSource = CalendarGlobals.currentTeamOC; ` – Rasiel Jul 12 '18 at 13:20
  • @LupuSilviu `Person anonym = new Person( Convert.ToInt32(row["id_user"]), row["name"].ToString(), row["surname"].ToString(), monday.ToString(), day1, day2, day3, day4, day5, shifts); TeamScheduleWindow.CalendarGlobals.currentTeamOC.Add(anonym);` – Rasiel Jul 12 '18 at 13:21
  • @Rasiel i mean the Itemssource for the combobox, not for the datagrid. Example: – Babbillumpa Jul 12 '18 at 13:22
  • Its working for buttons I have so its binded some how but my way is not working for this ComboBox – Rasiel Jul 12 '18 at 13:22
  • Possible duplicate of [Binding a WPF ComboBox to a custom list](https://stackoverflow.com/questions/561166/binding-a-wpf-combobox-to-a-custom-list) – Babbillumpa Jul 12 '18 at 13:24
  • @Babbillumpa isn't itemsource inherited from dataGrid? – Rasiel Jul 12 '18 at 13:29
  • I have added manually to comboBox ItemsSource="{Binding currentTeamOC}" but there is now difference. Buttons already have ItemSource without this line in their code. – Rasiel Jul 12 '18 at 13:32
  • @Rasiel you have to set Combobox Itemssource to the property of class Person that contains the collection you want to populate the combobox. I think that DataGrid Itemssource is correct. – Babbillumpa Jul 12 '18 at 13:34
  • So in my case, the property is `currentTeamOC` ? I've tried to add `ItemsSource="{Binding CalendarGlobals.currentTeamOC}"` or `ItemsSource="{Binding currentTeamOC}"` and nothing has changed – Rasiel Jul 12 '18 at 13:39
  • Try specifying a DesignInstance for the DataContext in the XAML; that way you can see available bindings in the editor. (d:DataContext="{d:DesignInstance classToBindTo}") – M Kloster Jul 12 '18 at 13:41
  • @Rasiel Buttons don't have ItemsSource. I think you are confusing ItemsSource and DataContext - DataContext is inherited, except for multi-item controls such as DataGrid, where ItemsSource specifies a list of data where each sub-control gets one of these as their DataContext. ComboBox can select between many items, and thus needs to specify ItemsSource, whereas Button does not handle multiple items, and thus does not have ItemsSource. – M Kloster Jul 12 '18 at 13:49
  • @Rasiel Also, what do you want the ComboBoxes to select? Currently you have bound the selected item to a list of strings, which seems rather odd... are you selecting from a list of lists of strings?? – M Kloster Jul 12 '18 at 13:53
  • @MKloster Thanks for explanation :) I want to bind ComboBox to string values (for ex. Text1, Text2 etc) and id's ( 1,3,4, etc). Shifts_value should be text visible on the list to select from ComboBox, and Shifts_id is id in my DB that is important to my code. I'll try to make some test with ItemSource, – Rasiel Jul 12 '18 at 13:59
  • @Rasiel Then you want to make a list of object, where ShiftType includes a text property and an id property, and you let DisplayMember point to the text property you want to show. Then you let ItemsSource for the ComboBox point to this list. – M Kloster Jul 12 '18 at 14:01
  • @MKloster thanks, I will try that, but can I have this object inside my ObservableColletion? If yes, as itemsource I need to set my OC? (here CalendarGlobals.currentTeamOC) – Rasiel Jul 16 '18 at 11:24

1 Answers1

1

This should have the basic components you need:

XAML:

<Window x:Class="ComboBoxInDataGridExample.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:ComboBoxInDataGridExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        d:DataContext="{d:DesignInstance local:MainWindow}">
    <Grid>
        <DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Grid.Row="1" Grid.Column="1" ItemsSource="{Binding Persons}" AutoGenerateColumns="False" CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="Monday" Width="auto">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <StackPanel>

                                <ComboBox x:Name="comboBoxShift" Margin="10,0,0,0" DisplayMemberPath="Description" SelectedValuePath="ID" SelectedValue="{Binding Path=Shifts_selectedId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Height="25" Width="auto" FontSize="10" SelectionChanged="comboBoxShift_SelectionChanged" ItemsSource="{Binding Shifts}">
                                </ComboBox>

                                <Button Name="ButtonStandby" Margin="10 0 0 0"  Content="Standby" Height="25" Width="auto" IsEnabled="True" FontSize="10" FontWeight="UltraBold" Background="{Binding Path=day1_f_standby}">
                                </Button>
                            </StackPanel>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

MainWindow.xaml.cs:

using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls;

namespace ComboBoxInDataGridExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public BindingList<Person> Persons { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            Persons = new BindingList<Person>
            {
                new Person{
                    day1_f_standby = "Blue",
                    Shifts = new BindingList<Shift>
                {
                    new Shift{ Description="First shift", ID = 1},
                    new Shift{ Description="Second shift", ID = 2},
                }
                },
                new Person{
                                day1_f_standby = "Red",
                    Shifts = new BindingList<Shift>
                {
                    new Shift{ Description="Early shift", ID = 3},
                    new Shift{ Description="Late shift", ID = 4},
                },
                     Shifts_selectedId = 3
                }
            };
            DataContext = this;
        }

        private void comboBoxShift_DropDownClosed(object sender, EventArgs e)
        {

        }

        private void comboBoxShift_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }
    }
}

Data classes:

using System;
using System.ComponentModel;

namespace ComboBoxInDataGridExample
{
    public class Shift
    {
        public string Description { get; set; }
        public int ID { get; set; }
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
    }

    public class Person
    {
        public int day1_id_ca { get; set; }
        public int day1_f_shift { get; set; }
        public string day1_f_wfh { get; set; }
        public string day1_f_standby { get; set; }
        public int day1_f_edited { get; set; }
        public string day1_note { get; set; }

        public BindingList<Shift> Shifts { get; set; }
        public int Shifts_selectedId { get; set; }

        public Shift SelectedItem { get { return null; } set { } }
    }
}
M Kloster
  • 679
  • 4
  • 13
  • Thanks a lot. My code is working now. In my case I don't understand why I couldn't add to XAML: `mc:Ignorable="d" d:DataContext="{d:DesignInstance local:TeamScheduleWindow}"` but it worked without it. Also I have added `Shifts` inside ObservableCollection using ` Shifts = new BindingList ();foreach (DataRow row in shifts.Rows){Shifts.Add(new Shift { Value = row["value"].ToString(), ID = Convert.ToInt16(row["id_value"]), Color = row["color"].ToString() });}` – Rasiel Jul 17 '18 at 13:26