DataGrid ComboBox 模板列中的选择更改会清除其他行中的值

Selection change in DataGrid ComboBox template column clears values in other rows

在我的项目中,我有一个 DataGrid,其中包含两个模板列,每个模板列都包含一个 ComboBox。当我更改任何行中 ComboBox 之一的选择时,更改会反映在所有行中。

我试过设置 IsSynchronizedWithCurrentItem="False",但这没有帮助。

任何人都可以提供解决此问题的方法吗?这是我的代码。

Xaml

<DataGrid 
        x:Name="dtg"
        CanUserAddRows="False"
        AutoGenerateColumns="False"
        IsSynchronizedWithCurrentItem="False"
        ItemsSource="{Binding Pdts, Mode=TwoWay}">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Type" Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox
                            IsEditable="True"
                            ItemsSource="{Binding DataContext.TypeCollection, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            DisplayMemberPath="TypeName"
                            SelectedItem="{Binding Type}"
                            SelectedValuePath="Id"
                            SelectionChanged="ComboBox_SelectionChanged_1"
                            SelectedValue="{Binding TypeId, Mode= TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTemplateColumn Header="Name" Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox
                            IsEditable="True"
                            KeyUp="ComboBox_KeyUp_1"
                            IsSynchronizedWithCurrentItem="False"
                            ItemsSource="{Binding DataContext.PdtCollection, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            DisplayMemberPath="PdtName"
                            SelectedItem="{Binding Pdt}"
                            SelectedValuePath="Id"
                            SelectedValue="{Binding Id, Mode= TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

视图模型

public class MainWindowViewModel : Base {
    public ObservableCollection<Type> TypeCollection { get; set; }
    public ObservableCollection<Pdt> PdtCollection { get; set; }
    public ObservableCollection<PdtAndType> Pdts { get; set; }

    public MainWindowViewModel() {
        TypeCollection = new ObservableCollection<Type>();
        PdtCollection = new ObservableCollection<Pdt>();
        Pdts = new ObservableCollection<PdtAndType>();

        TypeCollection.Add(new Type {Id = 1, TypeName = "one"});
        TypeCollection.Add(new Type {Id = 2, TypeName = "two"});
        TypeCollection.Add(new Type {Id = 3, TypeName = "three"});
        TypeCollection.Add(new Type {Id = 4, TypeName = "four"});
        TypeCollection.Add(new Type {Id = 5, TypeName = "five"});

        addPdt();
    }

    public void add(int typeId = 1) {
        Random r = new Random();
        int x = r.Next(10);
        PdtCollection.Clear();
        for (int i = 0; i < x; i++) {
            PdtCollection.Add(new Pdt {Id = 1, PdtName = "P" + i + 1});
        }
    }

    public void addPdt() {
        Pdts.Add(new PdtAndType());
    }
}

型号classPdt

public class Pdt : Base {
    private int _ID;

    public int Id {
        get { return _ID; }
        set {
            if (_ID != value) {
                _ID = value;
                onPropertyChanged("Id");
            }
        }
    }

    private String _PdtName;

    public String PdtName {
        get { return _PdtName; }
        set {
            if (_PdtName != value) {
                _PdtName = value;
                onPropertyChanged("PdtName");
            }
        }
    }

    private int _typeId;

    public int TypeId {
        get { return _typeId; }
        set {
            if (_typeId != value) {
                _typeId = value;
                onPropertyChanged("TypeId");
            }
        }
    }
}

型号class类型

public class Type : Base {
    private int _ID;

    public int Id {
        get { return _ID; }
        set {
            if (_ID != value) {
                _ID = value;
                onPropertyChanged("Id");
            }
        }
    }

    private String _typeName;

    public String TypeName {
        get { return _typeName; }
        set {
            if (_typeName != value) {
                _typeName = value;
                onPropertyChanged("TypeName");
            }
        }
    }
}

型号classPdtAndtype

 public class PdtAndType : Base {
    private int _typeId;

    public int TypeId {
        get { return _typeId; }
        set {
            if (_typeId == value) return;
            _typeId = value;
            onPropertyChanged(nameof(TypeId));
        }
    }

    private int _pdtId;

    public int PdtId {
        get { return _pdtId; }
        set {
            if (_pdtId == value) {
                return;
            }
            _pdtId = value;
            onPropertyChanged(nameof(PdtId));
        }
    }

    private Type _type;

    public Type Type {
        get { return _type; }
        set {
            if (_type == value) {
                return;
            }
            _type = value;
            onPropertyChanged(nameof(Type));
        }
    }

    private Pdt _pdt;

    public Pdt Pdt {
        get { return _pdt; }
        set {
            if (_pdt == value) {
                return;
            }
            _pdt = value;
            onPropertyChanged(nameof(Pdt));
        }
    }
}

您似乎将 SelectedItem 绑定到 ViewModel 上的同一个对象。

我猜你没有 post 整个 ViewModel,但这是我能做出的唯一结论。

我建议你为每一行制作模型,其中包含一个 SelectedType 和一个 SelectedPdt 属性。

如果您post关于 ViewModel 和模型的更多细节,我很乐意详细说明。


编辑

好的,我现在可以看到问题了。

您的组合框仅使用了 1 个 ViewModel。 在您的 KeyUp 事件处理程序中,您替换了 PdtCollection.

中的项目

这个集合是单一的,所有的组合框都绑定到这个单一的集合。 如果我理解你的目标,你想为每一行制作一个不同的集合。

由于 DataGrid 中的一行由 Pdts 集合表示,我建议您将 PdtCollection inside 你的 Pdt 对象。这样,您就可以将每一行的组合框绑定到行模型中的相关集合。

为了让事情更简单, 你 Pdt 应该看起来像这样:

class Pdt : Base
{
    /* 
        Other properties
    */

    private ObservableCollection<Pdt> _pdts;
    public ObservableCollection<Pdt> Pdts
    {
        get { return _pdts; }
        set { _pdts = value; }
    }
}

之后,您可以将 Window 的 DataContext 中的单个集合上的列绑定到 ROW 数据上下文。这应该看起来像这样:

<DataGridTemplateColumn Header="Name" Width="*">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox IsEditable="True"
                      KeyUp="ComboBox_KeyUp_1"
                      IsSynchronizedWithCurrentItem="False"
                      ItemsSource="{Binding PdtCollection}"
                      DisplayMemberPath="PdtName"
                      SelectedItem="{Binding Pdt}"
                      SelectedValuePath="Id"
                      SelectedValue="{Binding Id, Mode= TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

我认为您需要稍微调整一下代码(选择和显示的成员等,当然还要将内部 PdtCollection 初始化为您想要的任何值),但总体思路是这样的。

我希望我说得够清楚了。 快乐的编码。 :)