Form is closing, user can't do anymore corrections

Form is closing, user can't do anymore corrections

Old forum URL: forums.lhotka.net/forums/t/5353.aspx


alef posted on Friday, September 05, 2008

Hello,

I've a problem with the current implementation of the error handling.

Suppose the user is clicking on the OK button.(form ProjectEdit from the example of the book) Suppose the database throws an error because there is some incorrect data. The function RebindUI will catch the error (Csla.DataPortalException) and absorb the error. Then code continues to execute in the procedure OKButton_Click, the statement "this.Close()" will be called which will close the form without the user letting the possibility to correct the data. Is it possible to provide some code where it would be possible for the user to correct some things if the database is throwing an error?

Below follows the code:

Class ProjectEdit.cs

    private void OKButton_Click(object sender, EventArgs e)
    {
      using (StatusBusy busy = new StatusBusy("Saving..."))
      {
        RebindUI(true, false);
      }
      this.Close();
    }

 private void RebindUI(bool saveObject, bool rebind)
    {
      // disable events
      ...
      try
      {
        // unbind the UI
         ...
        // save or cancel changes
        if (saveObject)
        {
          _project.ApplyEdit();
          try
          {
            _project = _project.Save();
          }
          catch (Csla.DataPortalException ex)
          {
            MessageBox.Show(ex.BusinessException.ToString(),
              "Error saving", MessageBoxButtons.OK,
              MessageBoxIcon.Exclamation);

          }
          catch (Exception ex)
          {
            MessageBox.Show(ex.ToString(),
              "Error Saving", MessageBoxButtons.OK,
              MessageBoxIcon.Exclamation);
          }
        }
        else
          _project.CancelEdit();
      }
      finally
      {
        // rebind UI if requested
         ...
        }
      }
    }

 

RockfordLhotka replied on Friday, September 05, 2008

Absolutely! This is just UI code, and is not part of CSLA at all. You can write your UI code however you'd like!

(keeping in mind that data binding imposes some strict rules you have to live with)

alef replied on Saturday, September 06, 2008

I've replaced the code with the following. So I've put the try catch statement in the OKButton_Click event. Apparently you are first trying to see if it is a Csla.DataPortalException. I've seen that this DataPortalException has an interesting property BusinessObject which gives access to the business object at the time of the exception. This is really interesting for a developer to know what data the user entered on the form in case of an exception.

Do you have some generic piece of code to log all the data of the businessobject?

 

    private void OKButton_Click(object sender, EventArgs e)
    {
          try
          {
             using (StatusBusy busy = new StatusBusy("Saving..."))
             {
                  RebindUI(true, false);
             }
             this.Close();
          }
          catch (Csla.DataPortalException ex)
          {
            MessageBox.Show(ex.BusinessException.ToString(),
              "Error saving", MessageBoxButtons.OK,
              MessageBoxIcon.Exclamation);
          }
          catch (Exception ex)
          {
            MessageBox.Show(ex.ToString(),
              "Error Saving", MessageBoxButtons.OK,
              MessageBoxIcon.Exclamation);
          }
    }


 private void RebindUI(bool saveObject, bool rebind)
    {
      // disable events
      ...
      try
      {
        // unbind the UI
         ...
        // save or cancel changes
        if (saveObject)
        {
          _project.ApplyEdit();
          _project = _project.Save();
        }
        else
          _project.CancelEdit();
      }
      finally
      {
        // rebind UI if requested
         ...
        }
      }
    }


 

rsbaker0 replied on Saturday, September 06, 2008

On a related note, we had similar issues with "in grid" editing using the DevExpress XtraGrid. Once the user leaves the row, no further corrections are possible.

Most grid, including this one, provide for the capability to stop the row focus from changing, but we had all sorts of weird issues -- like you couldn't even click on some totally unrelated window. Validation exceptions where flying everywhere...

At least for the time being, we have resolved this problem by discarding the users changes if we ge a validation exception when no further correction is readily feasible. Not optimal, but if the user is ignoring errors displayed to them...

alef replied on Monday, September 08, 2008

Is this for another subject?

alef replied on Tuesday, September 09, 2008

I've already a partial solution for the logging of the values of the BO in case of an exception.
I've created a procedure PrintAllProperties which will do the work.
Have somebody an idea to extend the code to print recursively also the properties of child objects, child collections?

    public void PrintAllProperties(object BO )
    {
      // Creates a new collection and assign properties for BO.
      PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(BO);

      foreach (PropertyDescriptor property in properties)
      {
        // Checks IsBrowsable Status, Properties like IsDirty() etc are filtered
        // Also Checks Readonly Status
        // In short, If Property is Found and IsBrowsable is True & Readonly is false
        // then print this BO Property
        System.Type mtype = property.PropertyType;

        if (property.IsBrowsable & !property.IsReadOnly)
        {
          System.Diagnostics.Debug.Print(property.DisplayName + ":" + property.GetValue(BO));
        }
      }

The procedure PrintAllProperties can be called in the catch statement :
   catch (Csla.DataPortalException ex)
      {
        PrintAllProperties(ex.BusinessObject);

alef replied on Wednesday, September 10, 2008

I've found the following two interesting articles

Adding DumpState to objects for debugging
http://www.lhotka.net/Article.aspx?id=3967df85-ee82-4266-a131-7a3e8e08f5be

XML Serialization of BusinessBase objects
http://groups.msn.com/CSLANET/general.msnw?action=get_message&mview=0&ID_Message=20161&LastModified=4675531561287839345


These articles are the solution to the problem.

The following code is been used:
    If field.FieldType.IsSubclassOf(GetType(BusinessCollectionBase)) Then

but in version 3.0.4 BusinessCollectionBase doesn' exist anymore. How do I have to do is in version 3.0.4?

Lkotka do you have some code in C# which works against version 3.0.4?
Can you add it again to your framework?

alef replied on Tuesday, September 16, 2008

Hi Lothka,

I've added the following code and tested it and everything works as expected.

Can you review the code and maybe if you find it usefull at it for your next release?

I've added these new methods to the interface IUndoableObject or would you prefer to put these methods to another interface?

Below you can find the code which is needed:

1) Coded added to the class IUndoableObject

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to the output window

/// in the VS.NET IDE while debugging

/// </summary>

/// <remarks>

/// http://www.lhotka.net/Article.aspx?id=3967df85-ee82-4266-a131-7a3e8e08f5be

/// </remarks>

void DumpState();

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML

/// (for logging purposes).

/// </summary>

/// <remarks>

/// If an exception occurs during the normal data portal processing, a Csla.DataPortalException will be thrown. To get

/// at the original exception thrown by the business code, use the BusinessException property. Remember

/// that you can also use the BusinessObject property to get a reference to the business object as it

/// was when the exception was thrown — a fact that can be very useful for debugging.

/// We can use e.g. the following code in the UI to log the entire state of an object graph when an

/// exception occurs:

/// EnterpriseHelper.Log(((Csla.Core.IUndoableObject)ex.BusinessObject).ToXML(), "Exceptions");

/// </remarks>

string ToXML();

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML

/// (for logging purposes).

/// </summary>

/// <param name="objXmlWriter">

/// The XmlWriter object that you want to use as the underlying writer.

/// </param>

void DumpToXML(ref XmlWriter objXmlWriter);

 

2) Coded added to the class UndoableBase

#region "Alef: Logging/Debugging functions"

void IUndoableObject.DumpState()

{

DumpState();

}

string IUndoableObject.ToXML()

{

return ToXML();

}

void IUndoableObject.DumpToXML(ref XmlWriter objXmlWriter)

{

DumpToXML(ref objXmlWriter);

}

#region " To Output Window "

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to the output window

/// in the VS.NET IDE while debugging

/// </summary>

[EditorBrowsable(EditorBrowsableState.Never)]

protected internal void DumpState()

{

Type currentType = this.GetType();

Hashtable state = new Hashtable();

string fieldName;

FieldInfo[] fields;

Debug.IndentSize = 2;

string currentTypeName = currentType.Name;

Debug.WriteLine("OBJECT " + currentType.Name);

do {

// get the list of fields in this type

fields = currentType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (FieldInfo field in fields)

{

// make sure we process only our variables

if (object.ReferenceEquals(field.DeclaringType, currentType))

{

fieldName = field.DeclaringType.Name + "!" + field.Name;

// see if this field is marked as not undoable

if (!NotUndoableField(field))

{

// the field is undoable, so it needs to be processed

if (typeof(Csla.Core.IUndoableObject).IsAssignableFrom(field.FieldType))

{

// this is a child collection or a child object, cascade the call

Debug.Indent();

Debug.WriteLine("CHILD " + fieldName);

object value = field.GetValue(this);

// make sure the variable has a value

if (value != null)

{

// this is a child object, cascade the call

((Core.IUndoableObject)value).DumpState();

}

else

{

// variable has no value - store that fact

Debug.WriteLine("<Nothing>");

}

Debug.Unindent();

}

else

{

// this is a normal field, simply trap the value

if ((field.GetValue(this) != null))

{

Debug.WriteLine(fieldName + ": " + field.GetValue(this).ToString());

}

else

{

Debug.WriteLine(fieldName + ": <Nothing>");

}

}

}

else

{

// field is not undoable

if ((field.GetValue(this) != null))

{

Debug.WriteLine("<NotUndoable()> " + fieldName + ": " + field.GetValue(this).ToString());

}

else

{

Debug.WriteLine("<NotUndoable()> " + fieldName + ": <Nothing>");

}

}

}

}

currentType = currentType.BaseType;

}

while (currentType != typeof(UndoableBase));

Debug.WriteLine("END OBJECT " + currentTypeName);

}

#endregion

#region " ToXML "

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML

/// (for debugging purposes)

/// </summary>

[EditorBrowsable(EditorBrowsableState.Never)]

protected internal string ToXML()

{

return ToXML(true, "CSLA_Object");

}

 

private string ToXML(bool includeDocumentTag, string rootElementName)

{

MemoryStream objMS = new MemoryStream();

XmlWriterSettings settings = new XmlWriterSettings();

// Write individual elements on new lines and indent

settings.Indent = true;

// Indent with Tabs

settings.IndentChars = "\t";

XmlWriter writer = XmlWriter.Create(objMS, settings);

// Write XML data.

if (includeDocumentTag)

{

// Begin with our XML Header

// Standalone: Dit attribuut is een aanwijzing voor de verwerker dat dit document wel of niet

// het gebruik van een extern DTD-bestand of een extern entiteitenbestand vereist. Als je een

// goed opgesteld XML-document maakt, gebruik je geen externe DTD, dus dan wordt de waarde yes.

writer.WriteStartDocument(true);

}

writer.WriteStartElement(rootElementName);

this.DumpToXML(ref writer);

//RootElementName

writer.WriteEndElement();

//Commit everything to our underlying MemoryStream

writer.Flush();

// Set the position to the beginning of the stream

objMS.Position = 0;

//Create a streamreader object based on our Memory Stream

StreamReader objsr = new StreamReader(objMS);

// Return the entire contents of our MemoryStream

return objsr.ReadToEnd();

}

/// <summary>

/// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML

/// </summary>

/// <param name="objXmlWriter">

/// The XmlWriter object that you want to use as the underlying writer.

/// </param>

[EditorBrowsable(EditorBrowsableState.Never)]

protected internal void DumpToXML(ref XmlWriter objXmlWriter)

{

Type currentType = this.GetType();

Hashtable state = new Hashtable();

string fieldName;

FieldInfo[] fields;

objXmlWriter.WriteStartElement(currentType.Name);

do {

// get the list of fields in this type

fields = currentType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

foreach (FieldInfo field in fields)

{

// make sure we process only our variables

if ((field.DeclaringType == currentType))

{

fieldName = field.Name;

// see if this field is marked as not undoable

if (!NotUndoableField(field))

{

// the field is undoable, so it needs to be processed

if (typeof(Csla.Core.IUndoableObject).IsAssignableFrom(field.FieldType))

{

// this is a child collection or a child object, cascade the call

objXmlWriter.WriteStartElement(fieldName);

object value = field.GetValue(this);

// make sure the variable has a value

if (value != null)

{

// this is a child object, cascade the call

((Core.IUndoableObject)value).DumpToXML(ref objXmlWriter);

}

else

{

// variable has no value - store that fact

objXmlWriter.WriteElementString("Value", "Nothing");

}

objXmlWriter.WriteEndElement();

}

else

{

// this is a normal field, simply trap the value

if (!(field.GetValue(this) == null))

{

objXmlWriter.WriteElementString(fieldName, field.GetValue(this).ToString());

}

else

{

objXmlWriter.WriteElementString(fieldName, "Nothing");

}

}

}

else {

// field is not undoable

if (!(field.GetValue(this) == null))

{

objXmlWriter.WriteElementString(fieldName, field.GetValue(this).ToString());

}

else

{

objXmlWriter.WriteElementString(fieldName, "Nothing");

}

}

}

}

currentType = currentType.BaseType;

}

while (currentType != typeof(UndoableBase));

objXmlWriter.WriteEndElement();

}

// ToXML_Header and ToXML_Footer are not redundant because

// we had some instances where we were using the methods to log the state of objects

// in the middle of a complex business operation, so we would create a single xml document

// that had a "before" and "after" version of a business object.

// All in one xml doc. So, we needed that ability to write the header and footer seperately.

private string ToXML_Footer(string RootElementName)

{

//</CSLA_Object>

return ("\r\n" + "</" + RootElementName + ">" + "\r\n");

}

private string ToXML_Header(string RootElementName)

{

//<?xml version="1.0" encoding="utf-8" standalone="yes"?>

//<CSLA_Object>

return ("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" +

"\r\n" + "<" + RootElementName + ">" + "\r\n");

}

#endregion

#endregion

3) Coded added to the class BusinessListBase

#region "Alef: Logging/Debugging functions"

void Core.IUndoableObject.DumpState()

{

DumpState();

}

string Core.IUndoableObject.ToXML()

{

return ToXML();

}

void Core.IUndoableObject.DumpToXML(ref XmlWriter objXMLTW)

{

DumpToXML(ref objXMLTW);

}

private void DumpState()

{

// this is a child collection, cascade the call

Debug.Indent();

foreach (C child in this)

((Core.IUndoableObject)child).DumpState();

Debug.Unindent();

}

private string ToXML(bool includeDocumentTag, string rootElementName)

{

MemoryStream objMS = new MemoryStream();

XmlWriterSettings settings = new XmlWriterSettings();

// Write individual elements on new lines and indent

settings.Indent = true;

// Indent with Tabs

settings.IndentChars = "\t";

XmlWriter writer = XmlWriter.Create(objMS, settings);

// Write XML data.

if (includeDocumentTag)

{

// Begin with our XML Header

// Standalone: Dit attribuut is een aanwijzing voor de verwerker dat dit document wel of niet

// het gebruik van een extern DTD-bestand of een extern entiteitenbestand vereist. Als je een

// goed opgesteld XML-document maakt, gebruik je geen externe DTD, dus dan wordt de waarde yes.

writer.WriteStartDocument(true);

}

writer.WriteStartElement(rootElementName);

DumpToXML(ref writer);

//RootElementName

writer.WriteEndElement();

//Commit everything to our underlying MemoryStream

writer.Flush();

// Set the position to the beginning of the stream

objMS.Position = 0;

//Create a streamreader object based on our Memory Stream

StreamReader objsr = new StreamReader(objMS);

// Return the entire contents of our MemoryStream

return objsr.ReadToEnd();

}

private string ToXML()

{

return ToXML(true, "CSLA_Collection");

}

private void DumpToXML(ref XmlWriter objXMLTW)

{

// this is a child collection, cascade the call

foreach (C child in this)

((Core.IUndoableObject)child).DumpToXML(ref objXMLTW);

}

#endregion

3) Code in the UI

try

{

using (StatusBusy busy = new StatusBusy("Saving..."))

{

RebindUI(true, false);

}

this.Close();

}

catch (Csla.DataPortalException ex)

{

EnterpriseHelper.Log(((Csla.Core.IUndoableObject)ex.BusinessObject).ToXML(), "Exceptions");

if (EnterpriseHelper.HandleException(ex.BusinessException))

throw;

//MessageBox.Show(ex.BusinessException.ToString(),

// "Error saving", MessageBoxButtons.OK,

// MessageBoxIcon.Exclamation);

}

alef replied on Wednesday, October 22, 2008

I've moved to version 3.5.2 of the csla framework.

I need again this possibility to dump the object state.

But in version 3.5.2 I now get an error:

type Csla.Core.FieldManager.FieldDataManager can't be casted to Csla.Core.IUndoableObject

Apparently FieldDataManager is something new in this version. Lhotka, can you tell what I have to change so that the code works again?

Below an extract of the code where it fails: (see in previous reply for the whole code)

protected internal void DumpToXML(ref XmlWriter objXmlWriter)

{

....

  object value = field.GetValue(this);

  // make sure the variable has a value

  if (value != null)

  {

  // this is a child object, cascade the call

  ((Core.IUndoableObject).DumpToXML(ref objXmlWriter);  >>>>>>> it is failing here

 }

else

{

}

 

RockfordLhotka replied on Wednesday, October 22, 2008

The field manager is new in 3.5, and offers an alternative to storing your field data in private fields. I added this to simplify the creation of CSLA .NET for Silverlight, which doesn’t allow reflection against private fields (and so doesn’t have the BinaryFormatter or NetDataContractSerializer).

 

You are basically writing a serializer. And that’s fine in .NET. But in CSLA 3.5 and higher you’ll need to serialize private fields like you are doing, AND you’ll need to serialize the field manager in a different manner. Look at Csla\Serialization\Mobile\MobileFormatter to get some ideas.

 

Rocky

alef replied on Thursday, October 23, 2008

Thanks for the clarification.

So to have a complete view of the object I have to manage two things:
1) the private fields in the old fashion way
2) fields stored in the field manager

The MobileFormatter does exist only in CSLA.NET for SilverLight and not in CSLA.NET for Windows. What is the reason you didn't implement this in CSLA.NET for Windows?

By adding the following code (see further) in FieldDataManager.cs it is working again:
(I've created a seperate interface ILogging in place of using the interface IUndoableObject)

Lhotka : would it be possible to add this maybe in your latest version?
I'm putting some error trapping into my WinForms application and I think a helpful thing to record in a log file would be an XML version of the object so I can get an idea of what the user was working with when then application crashes.
So I'd like a way to cause an object and all of it's children/collections to be converted to XML so it may be saved to a log file.  This way I can examine the object in instances of hard to crack bugs.

Code:
    #region  Alef: ILogging
    void Core.ILogging.DumpState()
    {
      DumpState();
    }

    string Core.ILogging.ToXML()
    {
      return ToXML();
    }

    void Core.ILogging.DumpToXML(ref XmlWriter objXmlWriter)
    {
      DumpToXML(ref  objXmlWriter);
    }
    #endregion

    #region "Alef: Logging/Debugging functions"

    #region " To Output Window "
    /// <summary>
    /// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to the output window
    /// in the VS.NET IDE while debugging
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected internal void DumpState()
    {
      Debug.IndentSize = 2;

      IFieldData[] state = new IFieldData[_propertyList.Count];

      for (var index = 0; index < _fieldData.Length; index++)
      {
        var item = _fieldData[index];
        if (item != null)
        {
          var child = item.Value as ILogging;
          if (child != null)
          {
            Debug.Indent();
            Debug.WriteLine("CHILD " + item.Name);           

            // cascade call to child
            child.DumpState();

            Debug.Unindent();
          }
          else
          {
            Debug.WriteLine(item.Name + ": " + item.Value.ToString());
          }
        }
      }

    }
    #endregion

    #region " ToXML "
    /// <summary>
    /// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML
    /// (for debugging purposes)
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected internal string ToXML()
    {
      return ToXML(true, "CSLA_Object");
    }


    private string ToXML(bool includeDocumentTag, string rootElementName)
    {
      MemoryStream objMS = new MemoryStream();
      XmlWriterSettings settings = new XmlWriterSettings();
      // Write individual elements on new lines and indent
      settings.Indent = true;
      // Indent with Tabs
      settings.IndentChars = "\t";
      XmlWriter writer = XmlWriter.Create(objMS, settings);
      // Write XML data.
      if (includeDocumentTag)
      {
        // Begin with our XML Header
        // Standalone: Dit attribuut is een aanwijzing voor de verwerker dat dit document wel of niet
        // het gebruik van een extern DTD-bestand of een extern entiteitenbestand vereist. Als je een
        // goed opgesteld XML-document maakt, gebruik je geen externe DTD, dus dan wordt de waarde yes. 
        writer.WriteStartDocument(true);
      }

      writer.WriteStartElement(rootElementName);
      this.DumpToXML(ref writer);
      //RootElementName
      writer.WriteEndElement();
      //Commit everything to our underlying MemoryStream
      writer.Flush();
      // Set the position to the beginning of the stream
      objMS.Position = 0;
      //Create a streamreader object based on our Memory Stream
      StreamReader objsr = new StreamReader(objMS);
      // Return the entire contents of our MemoryStream
      return objsr.ReadToEnd();
    }

    /// <summary>
    /// Alef:Dump the entire state of an object graph (root object, its child objects, etc) to human readable XML
    /// </summary>
    /// <param name="objXmlWriter">
    /// The XmlWriter object that you want to use as the underlying writer.
    /// </param>
    [EditorBrowsable(EditorBrowsableState.Never)]
    protected internal void DumpToXML(ref XmlWriter objXmlWriter)
    {
      IFieldData[] state = new IFieldData[_propertyList.Count];

      for (var index = 0; index < _fieldData.Length; index++)
      {
        var item = _fieldData[index];
        if (item != null)
        {
          var child = item.Value as ILogging;
          if (child != null)
          {
            objXmlWriter.WriteStartElement(item.Name);

            // cascade call to child
            child.DumpToXML(ref objXmlWriter);

            objXmlWriter.WriteEndElement();
          }
          else
          {
            objXmlWriter.WriteElementString(item.Name, item.Value.ToString());
          }
        }
      }

    }

    // ToXML_Header and ToXML_Footer are not redundant because
    // we had some instances where we were using the methods to log the state of objects
    // in the middle of a complex business operation, so we would create a single xml document
    // that had a "before" and "after" version of a business object.
    // All in one xml doc.  So, we needed that ability to write the header and footer seperately.
    private string ToXML_Footer(string RootElementName)
    {
      //</CSLA_Object>
      return ("\r\n" + "</" + RootElementName + ">" + "\r\n");
    }

    private string ToXML_Header(string RootElementName)
    {
      //<?xml version="1.0" encoding="utf-8" standalone="yes"?>
      //<CSLA_Object>
      return ("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" +
              "\r\n" + "<" + RootElementName + ">" + "\r\n");
    }

    #endregion

    #endregion



RockfordLhotka replied on Thursday, October 23, 2008

The MobileFormatter exists in both Silverlight and Windows. The reason for MobileFormatter is to allow the data portal to communicate between Silverlight and Windows, and it is necessary because Silverlight doesn’t have a powerful enough serializer to meet the data portal’s needs. This blog post may help

 

http://www.lhotka.net/weblog/CSLALightSerializationImplementation.aspx

 

I do not plan to add a DumpState() concept to the framework. This could (and should) be constructed as an external tool (DLL), and isn’t something that I think belongs in the framework itself.

 

Rocky

alef replied on Saturday, March 07, 2009

Can you show me how to externalize this code? 

Geeky replied on Friday, October 24, 2008

rsbaker0:

On a related note, we had similar issues with "in grid" editing using the DevExpress XtraGrid. Once the user leaves the row, no further corrections are possible.

Most grid, including this one, provide for the capability to stop the row focus from changing, but we had all sorts of weird issues -- like you couldn't even click on some totally unrelated window. Validation exceptions where flying everywhere...

At least for the time being, we have resolved this problem by discarding the users changes if we ge a validation exception when no further correction is readily feasible. Not optimal, but if the user is ignoring errors displayed to them...

Sorry that this is a bit off topic and late ;-)

I'm working on a Dev Express based project atm and they seem to think that it is ok to just swallow every error :-(  I have found that often you just need to handle BaseEdit.InvalidValue and throw the error yourself (or change the default behaviour to throw instead of swallow).

Copyright (c) Marimer LLC