DataGridComboBoxColumn不同行具有不同的ItemsSource

7

这里有一个类似于我的问题的提问,但我在那里没有找到解决方案。

我的问题: 如何为不同行中的每个ComboBox绑定不同的数据(比如列表)到“DataGridComboBoxColumn”中。以下是我尝试的代码:

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; }
}

当我运行这段代码时,输出如下。

enter image description here

正如您在我的代码中看到的,不同的班级有不同的老师。当我点击第九个班级时,

enter image description here

"listTeachersNine" 应该添加到 "gTeachers.ItemsSource"。但如果我这样做"
gTeachers.ItemsSource = listTeachersNine;

所有其他行都会受到影响。如何在不改变其他行的情况下做到这一点。请给我一些想法...提前感谢。

更新:

尽管我尝试了以下代码

 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;

            }
       }

我尝试了注释和未注释的部分,但都没有用。当我运行我的应用程序时,它首先显示如上图表中所示。但是,当单击特定行时,所有其他行的数据都会消失。但问题是,它正在更新新教师的数据。请看下面的两张图片。

enter image description here

enter image description here

所以在不更改其他行的情况下,我可以更新我想要的那一行吗?

你解决了吗?我遇到了问题。 - FadedCoder
3个回答

3

我可以想到几种方法来实现这个。

如果你不关心严格的MVVM设计,你可以使用一个IValueConverter,将你的CLASS传递给它,让转换器确定要返回哪个列表。大致如下:

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;
        ...
    }
}

如果您想使其动态化并避免在转换器中使用硬编码的列表引用,您可以使用IMultiValueConverter,并将变量数组作为另一个绑定值传递给它,并调整代码以从其他绑定值传递的数组中找到正确的ItemsSource
请注意,要使用其中任何一种,您可能需要从DataGridComboBoxColumn切换到DataGridTemplateColumn,因为从ComboBoxColumn的绑定不会正常工作。
<DataGridTemplateColumn Header="TEACHERS">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding CLASS, Converter={StaticResource TestConverter}}" 
                      SelectedItem="{Binding TEACHER}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

另一种替代方案是将“ValidTeachers”列表添加到每个DataRow的项目中,即在您的情况下为“Info”对象。
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; }
}

您还需要一个更改事件,以便在CLASS更改时更新ValidTeachers数组。

我确信还有其他更简洁的选项,但如果您正在寻找简单易用的东西,我建议其中之一。


第二种解决方案非常不错而且简单。我会尝试一下,很快在这里更新。谢谢 :) - Gopi
@Gopi gTeachers 是一个 DataGridComboBoxColumn,因此在那里设置 ItemsSource 将应用于所有行。您只想为选定的行设置 ItemsSource。我建议使用我在答案中发布的转换器。您可能能够编写一些代码来查找所选行中的 ComboBox 以仅更改该行,但在我看来这将更加困难。 - Rachel
你能看到这个问题吗?http://stackoverflow.com/questions/40416448/how-to-bind-data-to-the-datagrid-present-in-the-usercontrol-in-wpf - Gopi

2
请查看此处的答案,作者是 Vincent Sibal - MSFT
这将帮助您根据同一行中另一列的选择,在一个列中设置某一行的ItemsSource。它使用样式来在每次单击ComboBox以编辑值时设置ItemsSource。
以下是上述网站中提供的示例(如果链接在将来被删除):
例如,如果我有一个DataGrid,并且我希望标记为“当前类别”的列中的选择影响名为“当前产品”的DataGridComboBoxColumn中的选项。
ProductsInCategory集合/列表中的项目用作Current Product列的可选值。该列表中的项目在CurrentCategory的setter中设置,如下所示:
public int CurrentCategory
{       
  get { return _currentCategory; }

  set
  {
      _currentCategory = value;

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

}

以下是 XAML 代码,在当前类别选择更改时将用于重新连接当前产品项源。
<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>

你能否在回答中复制或引用链接的相关部分?链接往往会随着时间的推移而被删除。 - Mario Tacke
@MarioTacke 完成。 - JMB

0

请参考链接msdn链接

我现在不能花太多时间在这上面。我拿了你的原始代码并对其进行了添加。它还不完整,但可以节省一些精力。

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# 代码

        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;
    }

@Makul Varshney 您的代码示例可以将特定列表绑定到特定组合框,效果良好。但是我遇到了“双向绑定需要路径或XPath”异常。现在我正在解决这个问题。完成后会通知您更新。谢谢 :) - Gopi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接