我有一个看起来像的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>
Property SelectedPurchase看起来像:
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中的任何值,然后点击 Enter 将触发CellEditEndingCommand并触发CellEditEndingMethod。但是如果我在CellEditEndingMethod中保留一个断点并查看它,那么我可以看到SelectedPurchase的任何属性的值都没有变为新值。
让我举一个例子来更准确地解释上述一行:
当我在CellEditEndingMethod中的任何一行上保留断点并查看像Liter,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)
}
);
答案 0 :(得分:1)
CellEditEnding事件不是为了更新数据行,而是为了验证单个单元格,如果内容无效,则将其保持在编辑模式。提交整行时完成真正的更新。通过将http://codefluff.blogspot.de/2010/05/commiting-bound-cell-changes.html中的HandleMainDataGridCellEditEnding方法中的代码添加到您的CellEditEndingMethod来尝试它。那里很好解释。您可以将if (!isManualEditCommit) {}
替换为if (isManualEditCommit) return;
。
<强>更新强>
您可以通过IEditableObject接口扩展Purchase类。 DataGrid将在提交数据后调用此接口的EndEdit()方法,因此您可以在那里执行XML操作。因此,您不需要任何其他按钮,因为单元格自动进入编辑模式,并且在您离开行时完成提交。 我认为CollectionChanged解决方案不起作用,因为如果编辑数据集,所有更改都发生在单个对象(Purchase)内,而不是集合中。 CollectionChanged将通过向集合添加或删除对象来调用
第二次更新
将所有这些放在一起的另一种尝试:
我简化了您的Purchase类以进行演示:
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 calls BeginEdit on an IEditableObject two times?
中有解释创建“购买”集合:
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
}
这提供了对初始化和新创建的所有EditablePurchase元素的Edited调用。 请确保在初始值设定项中设置了已编辑的属性
答案 1 :(得分:0)
这是WPF的耻辱。没有DataGrid.CellEditEnded
事件?荒谬的,到目前为止我还不知道。这是一个有趣的问题。
正如Fratyx所说,你可以致电
dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
在CellEditEnding方法后面的代码中。虽然它有效,但我发现它很难看。不仅因为后面有代码(可以使用行为来环绕那个代码),而且你的ViewModel CellEditEndingMethod
将被调用两次,因为编辑尚未提交,因此没有任何理由。
我可能会选择在您的Purchase类中实施INotifyPropertyChanged
(我建议使用base class,这样您就可以再次在一行上写属性),如果您还没有,请使用而是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...
}
答案 2 :(得分:0)
在DataGrid更改集合之前,您不会收到任何CollectionChanged事件。并且在发生数据集之前不会发生这种情况。 如果在单元格中按“Enter”,则会在真实数据集的一种副本中更改此单元格的值。因此可以通过回滚跳过更改。只有在完成一行后,例如通过更改为另一行或直接提交,您更改的数据将被写回原始数据。然后更新绑定并更改集合。 如果您希望逐个单元格更新,则必须按照我建议的代码强制提交。 但是如果你想拥有一个没有代码的纯粹MVVM解决方案,那么你必须满足于DataGrid的用途。那就是在行完成后更新。