/// <inheritdoc/> public virtual void EndTransaction(string name, AggregateActionItemDelegate aggregateActionItems, bool reverseOrderOnUndo = true) { if (TransactionStack.Count == 0) { throw new InvalidOperationException(Properties.ExceptionMessages.CannotEndNoTransactionInProgress); } var currentTransaction = TransactionStack.Pop(); if (currentTransaction.Count > 0) { var result = aggregateActionItems(currentTransaction); var aggregateActionItem = result as IAggregateActionItem; if (aggregateActionItem != null) { aggregateActionItem.ReverseOrderOnUndo = reverseOrderOnUndo; } Add(result); TransactionEnded?.Invoke(this, new ActionItemsEventArgs <IActionItem>(result)); } else { TransactionDiscarded?.Invoke(this, new ActionItemsEventArgs <IActionItem>(new IActionItem[0])); } }
/// <inheritdoc/> public virtual void DiscardTransaction() { if (TransactionStack.Count == 0) { throw new InvalidOperationException(Properties.ExceptionMessages.CannotEndNoTransactionInProgress); } var actions = TransactionStack.Pop(); TransactionDiscarded?.Invoke(this, new ActionItemsEventArgs <IActionItem>(actions.ToArray())); }
/// <summary> /// Purges the stack from the given index (included) to the top of the stack. /// </summary> /// <param name="index">The index from which to purge the stack.</param> private void PurgeFromIndex(int index) { if (index < 0 || index > transactions.Count) { throw new ArgumentOutOfRangeException(nameof(index)); } if (transactions.Count - index > 0) { var discardedTransactions = new IReadOnlyTransaction[transactions.Count - index]; for (var i = index; i < transactions.Count; ++i) { transactions[i].Interface.Freeze(); discardedTransactions[i - index] = transactions[i]; } transactions.RemoveRange(index, transactions.Count - index); TransactionDiscarded?.Invoke(this, new TransactionsDiscardedEventArgs(discardedTransactions, DiscardReason.StackPurged)); } }
/// <inheritdoc/> public virtual void EndTransaction(string name, Func <IReadOnlyCollection <IActionItem>, IActionItem> aggregateActionItems) { if (TransactionStack.Count == 0) { throw new InvalidOperationException(Properties.ExceptionMessages.CannotEndNoTransactionInProgress); } var currentTransaction = TransactionStack.Pop(); if (currentTransaction.Count > 0) { var aggregateActionItem = aggregateActionItems(currentTransaction); Add(aggregateActionItem); TransactionEnded?.Invoke(this, new ActionItemsEventArgs <IActionItem>(aggregateActionItem)); } else { TransactionDiscarded?.Invoke(this, new ActionItemsEventArgs <IActionItem>(new IActionItem[0])); } }
/// <inheritdoc/> public void CompleteTransaction(Transaction transaction) { lock (lockObject) { try { if (transactionsInProgress.Count == 0) { throw new TransactionException("There is not transaction in progress in the transaction stack."); } if (transaction != transactionsInProgress.Pop()) { throw new TransactionException("The transaction being completed is not that last created transaction."); } // Check if we're completing the last transaction TransactionInProgress = transactionsInProgress.Count > 0; // Ignore the transaction if it is empty if (transaction.IsEmpty) { return; } // If this transaction has no effect, discard it. if (transaction.Operations.All(x => !x.HasEffect)) { return; } // If we're not the last transaction, consider this transaction as an operation of its parent transaction if (TransactionInProgress) { // Avoid useless nested transaction if we have a single operation inside. PushOperation(transaction.Operations.Count == 1 ? transaction.Operations.Single() : transaction); return; } // Remove transactions that will be overwritten by this one if (currentPosition < transactions.Count) { PurgeFromIndex(currentPosition); } if (currentPosition == Capacity) { // If the stack has a capacity of 0, immediately freeze the new transaction. var oldestTransaction = Capacity > 0 ? transactions[0] : transaction; oldestTransaction.Interface.Freeze(); for (var i = 1; i < transactions.Count; ++i) { transactions[i - 1] = transactions[i]; } if (Capacity > 0) { transactions[--currentPosition] = null; } TransactionDiscarded?.Invoke(this, new TransactionsDiscardedEventArgs(oldestTransaction, DiscardReason.StackFull)); } if (Capacity > 0) { if (currentPosition == transactions.Count) { transactions.Add(transaction); } else { transactions[currentPosition] = transaction; } ++currentPosition; } } finally { if (!TransactionInProgress) { TransactionCompleted?.Invoke(this, new TransactionEventArgs(transaction)); } } } }