为什么我的DataGrid列中的ComboBox将自身重置为null?

时间:2013-01-18 00:20:32

标签: wpf combobox wpfdatagrid

在我的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。)
  3. 为什么要重置?我已经密切关注代码流,并且可以看到正在处理的组合框的支持字段(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));
            }
        }
    }
    

1 个答案:

答案 0 :(得分:1)

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

我很遗憾地说出来,但SSCCE会在这里创造奇迹。我强烈建议,当任何人遇到他们擅长深入代码的情况时,他们并不完全明白他们会突破他们试图做的事情并创建一个表现出相同行为的最小原型,或者帮助您了解将要发生的事情错误。在过去的五年里,我制作了500多个原型。

对于这种情况,您在ComboBox中引用了名为 cboNo btnSave_Click,但我在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中的元素不是,那么{ {1}} 未被删除

SelectedItem

因此,在var temp = combo.ItemsSource.OfType<object>().ToArray(); combo.ItemsSource = temp; 事件处理程序中,您更改了这个ItemsSource,它可能具有已经在组合中的相同实例,因此将的SelectedItem清空所有ComboBox都绑定到此属性,然后只更新btnSave_Click的SelectedIndex。

现在,至于该怎么做......

嗯,不确定。从其他代码开始,您需要执行更多代码隐藏工作,以确保只有必要的ComboBox更新其源代码...