StartTransaction

Hi Walter,

What would cause a call to StartTransaction to not set IsInTransaction to true? What is the boolean return value for StartTransaction mean?

The online documentation is cryptic :

<h4 ="dxh4">Return Value</h4>the value of the call to <span ="InnovasysSearchHighlight" style=": rgb(255, 255, 102); color: black;">StartTransaction

Should I be verifying that after a call to StartTransaction, IsInTransaction is true and take appropriate action if this is not the case (dont modify model)

<span =“InnovasysSearchHighlight” style=": rgb(255, 255, 102); color: black;">

You need to look at the documentation for UndoManager.StartTransaction. For some reason that documentation link isn’t right.

Basically only a top-level call to StartTransaction will return true. If it’s a nested call, it will return false.

Thanks, I’ll take a look at the UndoManager.StartTransaction documentation.

Should I be verifying that after a call to StartTransaction,
IsInTransaction is true and take appropriate action if this is not the
case (dont modify model) ?

No, don’t bother.

Just make sure that there’s always either a CommitTransaction or a RollbackTransaction for every StartTransaction. Watch out for exceptional code paths.

Hmm… Well my only concern is, I am running into what appears to be a race condition involving external drag and drop operations and calls to StartTransaction not firing a IDiagramModel Changed event.

Do you see any issues with calling the extension method below instead of StartTransaction?

public static void StartTransactionEx(this IDiagramModel idm, string str)
{
    do
    {
        idm.StartTransaction(str);
        if (idm.IsInTransaction == false)
        {
            Debug.WriteLine(String.Format("[Extensions] Attempt to StartTransaction failed!"));
        }
    }
    while (idm.IsInTransaction == false);
}

I also have this code that for the IDiagramModel eventhandler

void GoXamModel_Changed(object sender, ModelChangedEventArgs e)
{
    Debug.WriteLine(string.Format("GoXamModel Change : {0}", e.Change));
}

Debug Output :

[Extensions] Attempt to StartTransaction failed!
GoXamModel Change : StartedTransaction
GoXamModel Change : Property
GoXamModel Change : ChangedGroupNodeKey
GoXamModel Change : Property
GoXamModel Change : CommittedTransaction

Without the extension method, some of my model changes are not within a transaction, which is bad.

No, that seems a bad idea, because you’re calling StartTransaction more than once. (Unless you are also calling CommitTransaction an equal number of times, which seems unlikely. And if you tried to implement that, that would be really messy and inelegant.)

Exactly what model change is happening outside of a transaction, and why? Where is the drag-and-drop coming from, and what mechanism(s) are you using?

The model changes that are outside of a transaction are :

GoXamModel Change : Property
GoXamModel Change : ChangedGroupNodeKey
GoXamModel Change : Property
These are related to the updating of SubGraphKey

Why is it happening out of a transaction? I call StartTransaction, and it does not fire a Model Changed event (and IsInTransaction remains false). I can’t answer why this is the case, but I speculate that I’m attempting to modify the model when it’s in some internal lock state (during a drag and drop operation??)

My application is multithreaded and there are several threads that can initiate changes to the Model. In all cases these threads call an interface which will marshall back to the UI thread. These interfaces will wrap model changes in transactions.

In my custom dragging tool, I override the DropOnto method. This method can call asynchronous methods on a different thread to process business logic. This business logic may call the interfaces in place to modify the model (using the strategy mentioned above)

It’s good that you are changing the model only on the UI thread.

So are you saying that those three model changes outside of a transaction are happening due to a dragging operation, before the drop occurs?

Where is the drag source? Is it a Diagram or another control or outside of your app? WPF or Silverlight?

The three model changes that sometimes execute outside of a transaction are contained in a method that either implement node re-parenting (modify the SubGraphKey) or node creation.

These methods are called in a variety of scenarios :

  1. Business Tier calls interface to “add/move” an object
  2. Communication Tier calls interface to “add/move” an object
  3. External drag and drop asks the Business Tier to process a “add/move”
  4. Internal (node to node) drag and drop asks the Business Tier to process a “move”

1,2, and 4 always work. 3 executes outside transaction.

The drag source is another control in the app.
I override the DraggingTool::DoDragEnter method to convert the drag and drop data to a format GoXam recognizes.
I override the DraggingTool::DropOnto method and it will make calls to the business tier (asynchronously)

I set some breakpoints, flow looks something like :
DropOnto --> Call Business Tier to perform work on data (async)
ExternalObjectsDropped --> executes
(async)BTier --> Calls Move Interface which executes move/add in transactions

DraggingTool.DropOnto is called by DraggingTool.DoDrop.

DoDrop both starts and finishes a transaction (although it could either commit or rollback the transaction). Within that transaction it calls DropOnto and raises the ExternalObjectsDropped event.

Although you say that your BusinessTier executes a transaction after the ExternalObjectsDropped event, I wonder if it could execute either completely after the DraggingTool transaction or during it. If it happens during the transaction, it’s performing a nested transaction, which is OK. However that is exactly why IsInTransaction is true and why your call to StartTransaction returns false.

I think I may have further debugged the issue.

When I am dragging an object into a GoXam diagram, it seems that after I exit DraggingTool::DoDragEnter I get a model change outside of a transaction

Step into: Stepping over method without symbols 'Northwoods.GoXam.DiagramPanel.OnDragEnter'
Step into: Stepping over method without symbols 'System.Windows.RoutedEventArgs.InvokeHandler'
Step into: Stepping over method without symbols 'System.Windows.EventRoute.InvokeHandlersImpl'
Step into: Stepping over method without symbols 'System.Windows.UIElement.RaiseEventImpl'
Step into: Stepping over method without symbols 'System.Windows.OleDropTarget.RaiseDragEvent'
Step into: Stepping over method without symbols 'System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDragOver'
Step into: Stepping over method without symbols 'Northwoods.GoXam.DiagramPanel.OnDragEnter'
Step into: Stepping over method without symbols 'System.Windows.RoutedEventArgs.InvokeHandler'
Step into: Stepping over method without symbols 'System.Windows.EventRoute.InvokeHandlersImpl'
Step into: Stepping over method without symbols 'System.Windows.UIElement.RaiseEventImpl'
Step into: Stepping over method without symbols 'System.Windows.OleDropTarget.RaiseDragEvent'
Step into: Stepping over method without symbols 'System.Windows.OleDropTarget.MS.Win32.UnsafeNativeMethods.IOleDropTarget.OleDragEnter'
GoXamModel Change : AddedNode

Looking at the ModelChangedEventArgs (int the Model Changed event handler) the data property points to the object I created in DoDragEnter.

How do I wrap this inside of a transaction?

By default DraggingTool.DoDragEnter does absolutely nothing.

So it must be your code that is modifying the model. So it should be easy enough for you to either wrap a transaction around that modification, or set a flag to not take the model change too seriously in the code that you care about. One such flag is DiagramModel.SkipsUndoManager.

I think something like the latter case is preferable to the former case, because actually executing a committed transaction just when the user happens to drag something into the diagram will result in “garbage” being part of the undo/redo history.