I have a DevExpress GridView control bound to a binding list. I'm trying to emulate SQL Server Management Studio, where if you delete a row and it fails due to a constraint, the row remains in the grid. I handle this by having a subclass of BindingList which overrides RemoveAt and tells the object to delete itself from the DB before base.RemoveAt is called. However, this seems to cause an exception which crashes the application. I also haven't found any events from the grid to handle the error to prevent this. Here's the code:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Windows.Forms;
namespace DXGridRemoveItem {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public sealed class CustomBindingList : BindingList<BusinessObject> {
public CustomBindingList() {
AllowEdit = true;
AllowRemove = true;
AllowNew = true;
}
protected override void RemoveItem(int index) {
var item = this[index];
item.Save();
base.RemoveItem(index);
}
}
[Serializable]
public sealed class ObjectInUseException : Exception {
public ObjectInUseException() { }
public ObjectInUseException(string message) : base(message) { }
public ObjectInUseException(string message, Exception innerException) : base(message, innerException) { }
private ObjectInUseException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
public sealed class BusinessObject : IEditableObject, INotifyPropertyChanged {
private static int nextId;
private DataStorage currentData;
private DataStorage savedData;
private bool editing;
public BusinessObject() {
currentData = new DataStorage(nextId++);
}
public int Id => currentData.Id;
public string Name {
get => currentData.Name;
set {
if (currentData.Name != value) {
currentData.Name = value;
OnPropertyChanged(nameof(Name));
}
}
}
public void Save() {
if (editing) { throw new InvalidOperationException("Cannot save object while it is being edited."); }
// This happens because we tried to remove a row and a SQL constraint is preventing it
throw new ObjectInUseException();
}
public void BeginEdit() {
if (!editing) {
editing = true;
savedData = currentData;
}
}
public void EndEdit() {
if (editing) {
savedData = null;
editing = false;
}
}
public void CancelEdit() {
if (editing) {
currentData = savedData;
editing = false;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private class DataStorage {
public DataStorage(int id) {
Id = id;
}
public int Id { get; set; }
public string Name { get; set; }
}
}
}
And the code for Form1; a ToolStrip
with a Delete button, a DX GridControl
are added, and a BindingSource
have been added. The BindingSource
's DataSource
is the CustomBindingList
, and the grid's DataSource
is the BindingSource
.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DXGridRemoveItem {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
protected override void OnShown(EventArgs e) {
var list = new CustomBindingList {
new BusinessObject {Name = "Name 1"},
new BusinessObject {Name = "Name 2"},
new BusinessObject {Name = "Name 3"},
new BusinessObject {Name = "Name 4"}
};
bindingSource1.DataSource = list;
base.OnShown(e);
}
private void toolStripButton1_Click(object sender, EventArgs e) {
gridView1.DeleteSelectedRows();
}
}
}