【问题标题】:Why does the ComboBox in my DataGrid column reset itself to null?为什么我的 DataGrid 列中的 ComboBox 会将自身重置为 null?
【发布时间】:2013-01-18 00:20:32
【问题描述】:

在我的 WPF 应用程序中,我正在开发一个相当简单的页面,它允许创建一个新对象或从组合框中选择一个对象,然后编辑该对象。

对象的可编辑部分之一是一对多关系中的相关数据库表,因此对于该部分,我使用了 DataGrid。 DataGrid 本身有一个数据绑定的 ComboBox 列,您可以在此处看到:

<DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
          CanUserAddRows="False" CanUserDeleteRows="True" 
          ItemsSource="{Binding Path=No.Lower_Assy}"
          DataGridCell.Selected="dgAssy_GotFocus">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Number &amp; Type">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Path=DataContext.ComboSource, RelativeSource={RelativeSource AncestorType=Page}}"
                              SelectedValuePath="bwk_No"
                              SelectedValue="{Binding Path=fwf_Higher_N, ValidatesOnDataErrors=True, ValidatesOnExceptions=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}">
                        <ComboBox.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="{Binding Path=Number}"/>
                                    <TextBlock Text="{Binding Path=Type}"/>
                                </StackPanel>
                            </DataTemplate>
                        </ComboBox.ItemTemplate>
                    </ComboBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <!-- other text columns omitted -->
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Content="Delete" Click="btnDeleteHigherAssy_Click" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

后面的代码:

private void dgAssy_GotFocus(object sender, RoutedEventArgs e)
{
    if (e.OriginalSource.GetType() == typeof(DataGridCell))
    {
        // Starts the edit on the row
        DataGrid grd = (DataGrid)sender;
        grd.BeginEdit(e);
    }
}

对于保存按钮:

private void btnSave_Click(object sender, RoutedEventArgs e)
{
    if (CanUserEdit())
    {
        if (string.IsNullOrWhiteSpace(model.Data.Error))
        {
            repo.Save(model.Data);

            StatusText = STATUS_SAVED;
            model.CanSave = false;

            // This is the data source for the main combo box on the page
            model.ComboSource = repo.GetData();

            // Set the combo box's selected item, in case this is a new object.
            // cboNo is the main combo box on the page which allows selecting
            // an object to edit

            // Apparently setting SelectedItem directly doesn't work on a databound combo box
            int index = model.ComboSource.ToList().FindIndex(x => x.bwk_No == model.Data.bwk_No);
            cboNo.SelectedIndex = index;   
        }
        else
        {
            MessageBox.Show("Invalid data:\n" + model.Data.Error, "Cannot save");
        }
    }
}

问题

当我从数据网格的组合框中选择一个项目时,它似乎可以工作,直到我单击保存按钮。然后发生了两件事:

  1. 组合框的选中项设置为null,使组合框空白。
  2. 作为 (1) 的结果,保存按钮被重新启用,因为数据已更改。 (保存按钮绑定到model.CanSave,如您所见,它在按钮处理程序中设置为false;如果没有数据错误,则由属性更改事件处理程序设置为true。)

为什么要重置?我密切关注代码流,可以看到正在处理组合框支持字段 (fwf_Higher_N) 的属性更改事件,它似乎以某种方式来自model.ComboSource = repo.GetData(); 行,但堆栈仅显示[external code]而且我不明白为什么该行会修改现有对象。


模型类

// Names have been changed to protect the innocent
private class MyDataViewModel : INotifyPropertyChanged
{
    private DbData _Data;
    public DbData Data
    {
        get { return _Data; }
        set
        {
            _Data = value;
            OnPropertyChanged("Data");
        }
    }

    private IQueryable<MyComboModel> _ComboSource;
    public IQueryable<MyComboModel> ComboSource {
        get { return _ComboSource; }
        set
        {
            _ComboSource = value;
            OnPropertyChanged("ComboSource");
        }
    }

    private bool _CanSave;
    public bool CanSave
    {
        get { return _CanSave; }
        set
        {
            _CanSave = value;
            OnPropertyChanged("CanSave");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

【问题讨论】:

  • 组合框的选中项设置为空,使组合框空白。 在您刚刚编辑的 DataGrid 中的行中?您更改了数据源,因此选择被清除,这是预期的。在我的理解中,cboNo 可能只是一个红鲱鱼。为什么每次保存都更新 ComboBox 源?可能有一种方法可以在不改变整个事情的情况下实现您的目标。
  • @Will:DataGrid 内的组合框的来源没有改变。
  • 让我知道这是否可以解决问题,我会浓缩将其分解为答案。
  • 是的,我以它是否是一个新对象为条件,现在它可以完美地适用于现有对象。由于新对象在第一次保存之前甚至没有启用该选项卡,这就是我所需要的。感谢您的帮助!

标签: wpf combobox wpfdatagrid


【解决方案1】:

您对正在发生的事情的描述与您的标记不太匹配。我将做一些假设,比如Page.DataContextMyDataViewModel 的一个实例。

我很抱歉这么说,但SSCCE 会在这里创造奇迹。我强烈建议当任何人陷入他们对代码的深入了解的情况下,他们不太了解他们打破了他们正在尝试做的事情并创建一个最小的原型,该原型要么表现出相同的行为,要么可以帮助您了解正在发生的事情错误的。在过去的五年中,我制作了 500 多个原型。

对于这种情况,您在btnSave_Click 中引用了一个名为cboNoComboBox,但我在xaml 中没有看到。这个 ComboBox 的 ItemSource 似乎绑定到 MyDataViewModel.ComboSource

此外,DataGrid 中的所有 ComboBox 似乎也绑定到模型的 ComboSource。而且,在按钮处理程序事件中,您更改属性中的内容

// This is the data source for the main combo box on the page
model.ComboSource = repo.GetData();

这会触发PropertyChanged,并且绑定到此属性的每个 ComboBox 都将被更新。这不仅意味着 cboNo,还意味着 DataGrid 中的每个 ComboBox。

ComboBox.ItemsSource 更改时,如果ComboBox.SelectedItem 不包含在项目源中,则SelectedItem 会被取消,这是预期的行为。

我刚刚创建了一个原型 (501+),似乎如果 ComboBox 绑定到的 IEnumerable 发生变化,但 IEnumerable 中的元素没有,那么 SelectedItem 没有被清空

var temp = combo.ItemsSource.OfType<object>().ToArray();            
combo.ItemsSource = temp;

因此,在btnSave_Click 事件处理程序中,您更改了此ItemsSource,这可能 具有组合中已经存在的相同实例,从而使所有组合框的SelectedItem 无效绑定到这个属性,然后只更新cboNo的SelectedIndex。

现在,至于怎么办……

嗯,不确定。从您的其余代码来看,您似乎需要做更多的代码隐藏工作,以确保只有必要的 ComboBox 更新其源...

【讨论】:

  • 感谢您的推动。我写了 "它似乎以某种方式来自行 model.ComboSource = repo.GetData();,但堆栈只显示 [外部代码],我不明白为什么该行会修改现有对象",但不能再进一步了;正如你所说,这种情况正是正在发生的事情。
猜你喜欢
  • 2011-09-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
相关资源
最近更新 更多