1

私の WPF アプリケーションでは、新しいオブジェクトを作成するか、コンボ ボックスからオブジェクトを選択してオブジェクトを編集できる、かなり単純なページを開発しています。

編集可能なオブジェクトの部分の 1 つは、1 対多の関係にある関連データベース テーブルであるため、その部分には 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");
        }
    }
}

問題

データ グリッドのコンボ ボックスからアイテムを選択すると、保存ボタンをクリックするまで機能するように見えます。次に、次の 2 つのことが起こります。

  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.DataContextのインスタンスであるなど、いくつかの仮定を行いますMyDataViewModel

申し訳ありませんが、SSCCEはここで不思議に思うでしょう。誰かがコードにひじを突っ込んでいる状況に陥ったとき、彼らがやろうとしていることを打ち破り、同じ振る舞いを示すか、何が起こっているのかを学ぶのに役立つ最小限のプロトタイプを作成することを完全には理解していないことを強くお勧めします間違い。私は過去5年間で500以上のプロトタイプを作成しました。

この状況については、でComboBox名前付きのcboNoを参照していますが、 xamlではbtnSave_Click表示されません。この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に、アイテムソースに含まれていない場合、それはnullになると予想される動作ですSelectedItem

プロトタイプ(501+)をスピンアップしたIEnumerableところ、ComboBoxが変更にバインドされているが、IEnumerableの要素が変更されていないSelectedItem場合、nullアウトされていないようです。

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

したがって、btnSave_Clickイベントハンドラー内で、このItemsSourceを変更します。これは、コンボに既に存在するのと同じインスタンスを持っていない可能性があります。したがって、このプロパティにバインドされているすべてのComboBoxのSelectedItemを無効にしてから、のSelectedIndexのみを更新しcboNoます。

さて、どうしたらいいのか…

よくわかりません。コードの残りの部分から、必要なComboBoxのみがソースを更新するようにするために、コードビハインド作業をさらに行う必要があるようです...

于 2013-01-18T21:20:01.300 に答える