绑定到DataGrid';的属性;激发CellEditEnding时,s SelectedItem不会更改其子属性

本文关键字:属性 SelectedItem CellEditEnding DataGrid 绑定 激发 | 更新日期: 2023-09-27 18:24:45

我有一个DataGrid,它看起来像:

<DataGrid Grid.Row="3" Grid.Column="1" ItemsSource="{Binding Purchases}" SelectionMode="Single" SelectionUnit="FullRow"
          SelectedItem="{Binding SelectedPurchase, Source={x:Static ex:ServiceLocator.Instance}}" 
          AutoGenerateColumns="False" CanUserAddRows="False">
    <e:Interaction.Triggers>
        <e:EventTrigger EventName="CellEditEnding">
            <e:InvokeCommandAction Command="{Binding DataContext.CellEditEndingCommand, 
                                                     RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"/>
        </e:EventTrigger>
    </e:Interaction.Triggers>
    <DataGrid.Columns>
        .......
        ........
    <DataGrid.Columns>
</DataGrid>

房地产选择购买看起来像:

private Purchase _selectedPurchase;
public Purchase SelectedPurchase
{
    get
    {
        return _selectedPurchase;
    }
    set
    {
        _selectedPurchase = value;
        NotifyPropertyChanged("SelectedPurchase");
    }
}

CellEditEndingCommand

public ICommand CellEditEndingCommand { get; set; }
private void CellEditEndingMethod(object obj)
{
    XDocument xmlPurchases = XDocument.Load(DirectoryPaths.DataDirectory + "Purchases.xml");
    var currentPurchaseInData = (from purchase in xmlPurchases.Element("Purchases").Elements("Purchase")
                                 where Convert.ToInt32(purchase.Attribute("Id").Value) == ServiceLocator.Instance.SelectedPurchase.Id
                                 select purchase).FirstOrDefault();
    currentPurchaseInData.SetElementValue("CreditorId", ServiceLocator.Instance.SelectedPurchase.Creditor.Id);
    currentPurchaseInData.SetElementValue("AnimalId", ServiceLocator.Instance.SelectedPurchase.Animal.Id);
    currentPurchaseInData.SetElementValue("QuantityInLitre", ServiceLocator.Instance.SelectedPurchase.Litre);
    currentPurchaseInData.SetElementValue("FAT", ServiceLocator.Instance.SelectedPurchase.FAT);
    currentPurchaseInData.SetElementValue("RatePerLitre", ServiceLocator.Instance.SelectedPurchase.RatePerLitre);
    xmlPurchases.Save(DirectoryPaths.DataDirectory + "Purchases.xml");
}

现在,如果我更改DataGridCell中的任何值,然后点击EnterCellEditEndingCommand将被激发,CellEditEnding方法将被激发。但是,如果我在CellEditEndingMethod中保留一个断点并查看它,那么我可以看到SelectedPurchase的任何属性的值都不会更改为新值。

让我举一个例子来更正确地解释上面的行:

当我在CellEditEndingMethod内的任何一行上保留断点,并查看Litre、FAT等属性时,这些属性值不会更改。我的意思是,我希望这处房产能有新的价值,但它保持着旧的价值。此外,在视图中,我可以看到新的值,但在XML文件中仍然有旧的值。

更新:

Purchases = new ObservableCollection<Purchase>(
    from purchase in XDocument.Load(DirectoryPaths.DataDirectory + "Purchases.xml")
                              .Element("Purchases").Elements("Purchase")
    select new Purchase
    {
        Id = Convert.ToInt32(purchase.Attribute("Id").Value),
        Creditor = (
                        from creditor in XDocument.Load(DirectoryPaths.DataDirectory + "Creditors.xml")
                                                  .Element("Creditors").Elements("Creditor")
                        where creditor.Attribute("Id").Value == purchase.Element("CreditorId").Value
                        select new Creditor
                        {
                            Id = Convert.ToInt32(creditor.Attribute("Id").Value),
                            NameInEnglish = creditor.Element("NameInEnglish").Value,
                            NameInGujarati = creditor.Element("NameInGujarati").Value,
                            Gender = (
                                        from gender in XDocument.Load(DirectoryPaths.DataDirectory + @"Basic'Genders.xml")
                                                                .Element("Genders").Elements("Gender")
                                        where gender.Attribute("Id").Value == creditor.Element("GenderId").Value
                                        select new Gender
                                        {
                                            Id = Convert.ToInt32(gender.Attribute("Id").Value),
                                            Type = gender.Element("Type").Value,
                                            ImageData = gender.Element("ImageData").Value
                                        }
                                     ).FirstOrDefault(),
                            IsRegisteredMember = creditor.Element("IsRegisteredMember").Value == "Yes" ? true : false,
                            Address = creditor.Element("Address").Value,
                            City = creditor.Element("City").Value,
                            ContactNo1 = creditor.Element("ContactNo1").Value,
                            ContactNo2 = creditor.Element("ContactNo2").Value
                        }
                   ).FirstOrDefault(),
        Animal = (
                    from animal in XDocument.Load(DirectoryPaths.DataDirectory + @"Basic'Animals.xml")
                                            .Element("Animals").Elements("Animal")
                    where animal.Attribute("Id").Value == purchase.Element("AnimalId").Value
                    select new Animal
                    {
                        Id = Convert.ToInt32(animal.Attribute("Id").Value),
                        Type = animal.Element("Type").Value,
                        ImageData = animal.Element("ImageData").Value,
                        Colour = animal.Element("Colour").Value
                    }
                 ).FirstOrDefault(),
        Litre = Convert.ToDouble(purchase.Element("QuantityInLitre").Value),
        FAT = Convert.ToDouble(purchase.Element("FAT").Value),
        RatePerLitre = Convert.ToDouble(purchase.Element("RatePerLitre").Value)
    }
  );

绑定到DataGrid';的属性;激发CellEditEnding时,s SelectedItem不会更改其子属性

CellEditEnding事件不是为了更新数据行,而是为了验证单个单元格,并在内容无效时将其保持在编辑模式。真正的更新是在提交整行时完成的。请尝试在中的HandleMainDataGridCellEditEnding方法中添加代码http://codefluff.blogspot.de/2010/05/commiting-bound-cell-changes.html添加到CellEditEndingMethod。那里解释得很好。您可以将if (!isManualEditCommit) {}替换为if (isManualEditCommit) return;

更新

您可以通过接口IEditableObject扩展Purchase类。DataGrid将在提交数据后调用该接口的方法EndEdit(),因此您可以在那里执行XML操作。因此,您不需要任何进一步的按钮,因为一个单元格会自动进入编辑模式,并且在您离开行时会完成提交。我认为CollectionChanged解决方案不起作用,因为如果编辑数据集,所有更改都发生在单个对象(Purchase)内部,而不是集合中。CollectionChanged将通过向集合添加或删除对象来调用

第二次更新

另一个尝试是将其整合在一起:

我简化了您的购买类以进行演示:

class Purchase
{
    public string FieldA { get; set; }
    public string FieldB { get; set; }
}

创建一个派生类以保持真正的Purchase类干净:

class EditablePurchase : Purchase, IEditableObject
{
    public Action<Purchase> Edited { get; set; }
    private int numEdits;
    public void BeginEdit()
    {
        numEdits++;
    }
    public void CancelEdit()
    {
        numEdits--;
    }
    public void EndEdit()
    {
        if (--numEdits == 0)
        {
            if (Edited != null)
                Edited(this);
        }
    }
}

这在SO WPF DataGrid对IEditableObject调用BeginEdit两次中有解释?

并创建Purchases集合:

   ObservableCollection<EditablePurchase> Purchases = new ObservableCollection<EditablePurchase>()
        {
            new EditablePurchase {FieldA = "Field_A_1", FieldB = "Field_B_1", Edited = UpdateAction},
            new EditablePurchase {FieldA = "Field_A_2", FieldB = "Field_B_2", Edited = UpdateAction}
        };
    Purchases.CollectionChanged += Purchases_CollectionChanged;
    private void Purchases_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
            foreach (EditablePurchase item in e.NewItems)
                item.Edited = UpdateAction;
    }
    void UpdateAction(Purchase purchase)
    {
        // Save XML
    }

这提供了对Edited的调用被捕获用于初始化中的所有EditablePurchase元素和新创建的元素请确保在初始值设定项中设置Edited属性

这是WPF的耻辱。没有DataGrid.CellEditEnded事件?很可笑,到目前为止我还不知道。这是一个有趣的问题。

正如Fratyx所提到的,您可以致电

dataGrid.CommitEdit(DataGridEditingUnit.Row, true);

在CellEditEnding方法后面的代码中。虽然它有效,但我发现它很难看。这不仅是因为有代码隐藏(可以使用行为来绕过它),而且ViewModel CellEditEndingMethod将被调用两次,其中一次没有充分的理由,因为编辑尚未提交。

如果你还没有,我可能会选择在Purchase类中实现INotifyPropertyChanged(我建议你使用基类,这样你就可以再次在一行上写属性),并使用PropertyChanged事件:

public MyViewModel()
{
    Purchases = new ObservableCollection<Purchase>();
    Purchases.CollectionChanged += Purchases_CollectionChanged;
}
private void Purchases_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
        foreach (Purchase item in e.NewItems)
            item.PropertyChanged += Purchase_PropertyChanged;
    if (e.OldItems != null)
        foreach (Purchase item in e.OldItems)
            item.PropertyChanged -= Purchase_PropertyChanged;
}
private void Purchase_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    // save the xml...
}

在DataGrid更改集合之前,您将不会得到任何CollectionChanged事件。这在数据集提交之前是不会发生的。如果在单元格中按"Enter",则会更改实际数据集副本中该单元格的值。因此,可以通过回滚跳过更改。只有在完成一行之后,例如,通过更改到另一行或直接提交,更改后的数据才会写回原始数据。然后将更新绑定并更改集合。如果你想一个单元一个单元地更新,你必须按照我建议的代码强制提交。但是,如果你想拥有一个没有代码的纯粹MVVM解决方案,你必须满足于DataGrid的目的。也就是在行结束后更新。