简体   繁体   中英

DataGridComboBoxColumn different ItemsSource for different rows

Here the question similar to my question is asked, but I did not find the solution there.

My Question : How to bind different data(say Lists) to the "DataGridComboBoxColumn" for each ComboBox in a different rows. Here is the code I tried

XAML:

<Grid>
    <DataGrid x:Name="dg_TimeTable" AutoGenerateColumns="False" Margin="0,0,0,97" ColumnWidth="*">
        <DataGrid.Columns>
            <DataGridTextColumn IsReadOnly="True" Binding="{Binding CLASS}" Header="CLASS" />

            <DataGridComboBoxColumn Header="PERIOD" x:Name="gPeriods" SelectedValueBinding="{Binding PERIOD, Mode=TwoWay}" DisplayMemberPath="{Binding PERIOD}" />

            <DataGridComboBoxColumn Header="TEACHERS" x:Name="gTeachers" SelectedValueBinding="{Binding TEACHER, Mode=TwoWay}" DisplayMemberPath="{Binding TEACHER}" />

            <DataGridComboBoxColumn Header="SUBJECTS" x:Name="gSubjects" SelectedValueBinding="{Binding SUBJECT, Mode=TwoWay}" DisplayMemberPath="{Binding SUBJECT}"/>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

.cs

using System.Collections.ObjectModel; // For ObservableCollection

public partial class MainWindow : Window
{
    ObservableCollection<string> listTeachersSix = null;
    ObservableCollection<string> listTeachersSeven = null;
    ObservableCollection<string> listTeachersEight = null;
    ObservableCollection<string> listTeachersNine = null;
    ObservableCollection<string> listTeachersTen = null;
    ObservableCollection<string> listSubjects = null;
    ObservableCollection<int> listPeriods = null;

    public MainWindow()
    {
        InitializeComponent();

        listTeachersSix = new ObservableCollection<string>();
        listTeachersSeven = new ObservableCollection<string>();
        listTeachersEight = new ObservableCollection<string>();
        listTeachersNine = new ObservableCollection<string>();
        listTeachersTen = new ObservableCollection<string>();
        listSubjects = new ObservableCollection<string>();
        listPeriods = new ObservableCollection<int>();

        listTeachersSix.Add("Vijay");
        listTeachersSix.Add("Naveen");
        listTeachersSix.Add("Gopal");
        listTeachersSix.Add("Somesh");

        listTeachersSeven.Add("Raj");
        listTeachersSeven.Add("Rama Krishna");
        listTeachersSeven.Add("Rakesh");
        listTeachersSeven.Add("Ram Babu");

        listTeachersEight.Add("Murali");
        listTeachersEight.Add("Ritesh");
        listTeachersEight.Add("Nagesh");
        listTeachersEight.Add("Tarun");

        listTeachersNine.Add("Bhaskar");
        listTeachersNine.Add("Babji");
        listTeachersNine.Add("Bhanu");
        listTeachersNine.Add("Balaji");

        listTeachersTen.Add("Lal");
        listTeachersTen.Add("Mohan");
        listTeachersTen.Add("Raj Sekhar");
        listTeachersTen.Add("Sunil");

        for (int i = 0; i <= 8; i++)
            listPeriods.Add(i);

        listSubjects.Add("Maths");
        listSubjects.Add("Physics");
        listSubjects.Add("Social");
        listSubjects.Add("English");
        listSubjects.Add("Hindi");
        listSubjects.Add("Telugu");


        List<Info> listTimeTable = new List<Info>()
        {
            new Info () { CLASS="6", PERIOD=1, TEACHER="Vijay", SUBJECT="Maths" },
            new Info () { CLASS="7", PERIOD=5, TEACHER="Raj", SUBJECT="Physics" },
            new Info () { CLASS="8", PERIOD=7, TEACHER="Murali", SUBJECT="Social" },
            new Info () { CLASS="10", PERIOD=4, TEACHER="Mohan", SUBJECT="English" },
            new Info () { CLASS="6", PERIOD=8, TEACHER="Naveen", SUBJECT="Maths" },
            new Info () { CLASS="9", PERIOD=3, TEACHER="Bhaskar", SUBJECT="Hindi" },
            new Info () { CLASS="8", PERIOD=6, TEACHER="Ritesh", SUBJECT="English" },
            new Info () { CLASS="10", PERIOD=2, TEACHER="Lal", SUBJECT="Social" }
        };

        dg_TimeTable.ItemsSource = listTimeTable;

        gPeriods.ItemsSource = listPeriods;

        gSubjects.ItemsSource = listSubjects;

        gTeachers.ItemsSource = listTeachersSix;

    }
}

public class Info
{
    public string CLASS { get; set; }
    public int PERIOD { get; set; }
    public string SUBJECT { get; set; }
    public string TEACHER { get; set; }
}

when I run this code, the output is as below.

在此输入图像描述

As you all see in my code there are different teachers for different classes. When I click on say 9th class,

在此输入图像描述

"listTeachersNine" should be added to the "gTeachers.ItemsSource" . But if I do so

gTeachers.ItemsSource = listTeachersNine;

all the other rows are effecting. How can I do this without change in the other rows. Please give me some idea... Thanks in advance

UPDATE :

Even though I tried this below code

 private void dg_TimeTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        int num = (sender as DataGrid).SelectedIndex;
        try
        {


            if (num == 0)
            {
                //listTeachersSix.Clear();

                //listTeachersSix.Add("Vijay");
                //listTeachersSix.Add("Naveen");
                //listTeachersSix.Add("Gopal");
                //listTeachersSix.Add("Somesh");
                gTeachers.ItemsSource = listTeachersSix;

            }
            else if (num == 1)
            {
                //listTeachersSix.Clear();

                //listTeachersSix.Add("Raj");
                //listTeachersSix.Add("Rama Krishna");
                //listTeachersSix.Add("Rakesh");
                //listTeachersSix.Add("Ram Babu");
                gTeachers.ItemsSource = listTeachersSeven;
            }
            else if (num == 2)
            {
                //listTeachersSix.Clear();

                //listTeachersSix.Add("Murali");
                //listTeachersSix.Add("Ritesh");
                //listTeachersSix.Add("Nagesh");
                //listTeachersSix.Add("Tarun");
                gTeachers.ItemsSource = listTeachersEight;

            }
       }

I tried both commented part and uncommented part but its no use. When I run my application starting it shows as above first diagram. But when click on particular row, all the other rows data are vanishing. But the thing is, it is updating the new teachers data. Look at the below two pictures.

在此输入图像描述

在此输入图像描述

So without changing the other rows can I update the row I wanted.....

There's a few ways I can think to do this.

If you don't care about strict MVVM design, you could use a IValueConverter , pass it your CLASS, and have the converter figure out which list to return. Something along the lines of

public object Convert(object value, Type targetType,
    object parameter, CultureInfo culture)
{
    string key = value as string;
    switch (key)
    {
        case "1":
            return SomethingStatic.TeacherList1;
        case "2":
            return SomethingStatic.TeacherList2;
        ...
    }
}

If you want to make it dynamic and avoid hardcoded list references in your Converter, you could use an IMultiValueConverter and pass it the variable arrays as another bound value and tweak the code to simply find the right ItemsSource from the array passed in from the other bound values.

Note that to use either of these, you'll probably have to switch from a DataGridComboBoxColumn to a DataGridTemplateColumn because the bindings won't work correctly from the ComboBoxColumn .

<DataGridTemplateColumn Header="TEACHERS">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding CLASS, Converter={StaticResource TestConverter}}" 
                      SelectedItem="{Binding TEACHER}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Another alternate solution would be to add the list of ValidTeachers to item for each DataRow, in your case the Info object.

public class Info
{
    public string CLASS { get; set; }
    public int PERIOD { get; set; }
    public string SUBJECT { get; set; }
    public string TEACHER { get; set; }
    public List<string> ValidTeachers { get; set; }
}

You'd also need a Change Event so whenever CLASS changes, the ValidTeachers array gets updated.

I'm sure there's other options that might be cleaner, but if you're looking for something simple and easy I'd recommend one of these.

Check out the answer here by Vincent Sibal - MSFT :

This should help you with setting the ItemsSource of one column in a certain row based on the selection in another column of that same row. It is using styles to set the ItemsSource every time you click on a ComboBox to edit the value.

Here is the example given at the above site, in case the link is removed in the future:

The example is if I have a DataGrid, and I want the selection in a column labeled Current Category to affect the options in the DataGridComboBoxColumn called Current Product.

The items in the ProductsInCategory collection/list serve as the selectable values for the Current Product column. The items in this list are set in the setter for the CurrentCategory, as seen below:

public int CurrentCategory
{       
  get { return _currentCategory; }

  set
  {
      _currentCategory = value;

      ProductsInCategory = DBAccess.GetProductsInCategory(_currentCategory).Tables["Products"].DefaultView;
      OnPropertyChanged("CurrentCategory");
  } 

}

Below is the xaml that will be used to hook up the Current Product ItemsSource anew whenever the CurrentCategory selection is changed.

<dg:DataGridComboBoxColumn Header="Current Product"
    SelectedValueBinding="{Binding Path=CurrentProduct}"
    SelectedValuePath="ProductID"
    DisplayMemberPath="ProductName">          

  <dg:DataGridComboBoxColumn.ElementStyle>
   <Style TargetType="ComboBox">
      <Setter Property="ItemsSource" Value="{Binding Path=ProductsInCategory}" />
   </Style>
  </dg:DataGridComboBoxColumn.ElementStyle>

  <dg:DataGridComboBoxColumn.EditingElementStyle>
    <Style TargetType="ComboBox">
      <Setter Property="ItemsSource" Value="{Binding Path=ProductsInCategory}" />
    </Style>
  </dg:DataGridComboBoxColumn.EditingElementStyle>

</dg:DataGridComboBoxColumn>

Refer the link msdn link

I cannot devote much time on it now. I took your original code and added to it. Its not complete but it will save some effort.

XAML

    <Grid>
    <DataGrid x:Name="dg_TimeTable" AutoGenerateColumns="False" Margin="0,0,0,97" ColumnWidth="*" PreparingCellForEdit="dg_TimeTable_PreparingCellForEdit">
        <DataGrid.Columns>
            <DataGridTextColumn IsReadOnly="True" Binding="{Binding CLASS}" Header="CLASS" />

            <DataGridComboBoxColumn Header="PERIOD" x:Name="gPeriods" SelectedValueBinding="{Binding PERIOD, Mode=TwoWay}" DisplayMemberPath="{Binding PERIOD}" />

            <DataGridComboBoxColumn Header="TEACHERS" x:Name="gTeachers" SelectedValueBinding="{Binding TEACHER, Mode=TwoWay}" DisplayMemberPath="{Binding TEACHER}" />

            <DataGridTemplateColumn Header="TEACHERS" x:Name="colTeacherList" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=TEACHER, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <ComboBox Name="cmbTeacherList" SelectedItem="{Binding myItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
            </DataGridTemplateColumn>

            <DataGridComboBoxColumn Header="SUBJECTS" x:Name="gSubjects" SelectedValueBinding="{Binding SUBJECT, Mode=TwoWay}" DisplayMemberPath="{Binding SUBJECT}"/>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

C# code

        private void dg_TimeTable_PreparingCellForEdit(object sender, DataGridPreparingCellForEditEventArgs e)
    {
        int rowIndex = dg_TimeTable.SelectedIndex;

        if (e.Column == colTeacherList)
        {
            FrameworkElement element = e.EditingElement;
            ComboBox cb = GetVisualChild<ComboBox>(element);
            if (cb != null)
            {
                switch(dg_TimeTable.SelectedIndex)
                {
                    case 1:
                        cb.ItemsSource = listTeachersSeven;
                        break;
                    case 2:
                        cb.ItemsSource = listTeachersEight;
                        break;
                    default:
                        cb.ItemsSource = listTeachersSix;
                        break;
                }                        
            }
        }
    }

    static T GetVisualChild<T>(Visual parent) where T : Visual
    {
        T child = default(T);
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM