1

在我的 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));
        }
    }
}
4

1 回答 1

1

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

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

至于这种情况,您在 中引用了一个ComboBox名为cboNobtnSave_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 都将被更新。这不仅意味着cboNoDataGrid 中的每个 ComboBox。

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

我刚刚创建了一个原型(501+),看起来如果IEnumerableComboBox 绑定到更改,但 IEnumerable 中的元素没有,则SelectedItem不会被清空

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

因此,在btnSave_Click事件处理程序中,您更改了此 ItemsSource,它可能没有组合中已有的相同实例,从而使绑定到此属性的所有 ComboBox 的SelectedItem 无效,然后仅更新cboNoSelectedIndex。

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

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

于 2013-01-18T21:20:01.300 回答