Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Thursday, January 18, 2018 1:32 PM
Hi everyone,
I'm trying to add a Undo/Redo functionality on my program. Like so
private List<OrderItem> _orderItems = new List<OrderItem>();
public List<OrderItem> AddToOrder(PRODUCT product)
{
var item = _orderItems.FirstOrDefault(x =>
{
//Check if the item exist in the list of orders
return product != null && x.Model == product.MODEL;
});
if (item == null)
{
//add the order
var items = new OrderItem
{
Id = product.ID,
Quantity = 1,
Description = product.DESCRIPTION,
Model = product.MODEL,
UnitPrice = product.UNITPRICE,
CurrentCount = product.CURRENTCOUNT,
IsTaxable = product.TAXABLE
};
_orderItems.Add(items);
}
else
{ //increase the quantity
_orderItems.Where(x => x.Id == product.ID).ToList().ForEach(x=>x.Quantity=x.Quantity +1);
}
return _orderItems;
}
The problem lies when the item is already on the list, i am not sure how to track it for undo/redo operation.
Any idea how to better implement this will be really appreciated.
Regards
All replies (8)
Thursday, January 18, 2018 3:10 PM
You're not going to be able to implement undo/redo simply by using a list. Undo/redo can be difficult (or even impossible) to implement depending upon your architecture. I recommend that you look into the memento pattern for supporting undo at an object level.
For larger levels of undo a command/message architecture may be needed. For example, adding an order would consist of sending an "add" command. To undo you'd send the corresponding "remove" command. Of course if that order has already started processing then undo may not even be possible. Again, completely depends upon your architecture.
Given your code, it seems like add to order will add an item or update the quantity. You aren't going to be able to undo that unless you also implement a remove from order logic that does the inverse. But without the original order you aren't going to be able to know that. An undo/redo buffer comes into play here. I would recommend that you read up on how UIs implement undo/redo to get some ideas.
Michael Taylor http://www.michaeltaylorp3.net
Thursday, January 18, 2018 4:14 PM
Hi, thanks for taking time to read my post.
I've just read about the mamento pattern but how about using a Stack<T>.
I guess i can store the list of orders in a stack i just have some difficulty debugging my code.
void Main()
{
Stack<List<Customer>> clist=new Stack<List<Customer>>();
List<Customer> c = new List<Customer>();
c.Add(new Customer {FirstName="A", LastName="B"});
clist.Push(c);
c.Add(new Customer {FirstName="C", LastName="D"});
clist.Push(c);
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set;}
}
I am expecting the code to fill the stack with a single customer on the first push, and then two customers on the second push but when trying to see the content of my stack, I received a two customers on the first push and also on the second push. It seems like it was duplicated. Will you be so kind to pinpoint what i'm doing wrong here?
Regards
Thursday, January 18, 2018 4:43 PM
You're combining a list and stack. Not sure why you'd be doing that. Your first push is adding the c list you created to the stack. You then push it again in the next call. Since this is a ref type you're pushing the same list twice. Both entries in the stack point to the same list. I suspect you just want to push/pop customers.
var clist = new Stack<Customer>();
c.Push(new Customer { });
c.Push(new Customer {});
//Stack now has 2 customers on it
A stack still isn't going to get any undo/redo implementation working but it could be the start of the infrastructure need to support undo. But pushing customer doesn't really do anything useful here. You aren't specifying what is changing about each customer. Undo is going to require a lot more information that just what a customer would provide. You need to know what operation was done (customer added, updated, etc), what customer(s) were impacted, etc. At a minimum you're going to need some sort of undo type(s) to represent the action(s) in your system and the data need to track it. Here's a very, very simple example.
public interface IAction
{
void Undo ();
}
public class AddCustomerAction : IAction
{
public AddCustomerAction ( int customerId )
{ }
public void Undo ()
{
//Logic to remove a customer given the ID passed to the constructor
}
}
public class UpdateCustomerAction : IAction
{
//Add any parameters need to track what was updated
public UpdateCustomerAction ( int customerId, string actionPerformed )
{
}
public void Undo ()
{
//Logic to undo the update to the customer based upon
//the parameters passed to constructor
}
}
//Stack to track the actions
Stack<IAction> undoStack = new Stack<IAction>();
//Example usage in UI call that adds a new customer
void AddCustomer ( Customer customer )
{
//Update your system with new customer
...
//Add the "add" to the stack so it can be undone
undoStack.Add(new AddCustomerAction(customer.Id));
}
void UpdateCustomerQuantity ( Customer customer )
{
//Update system with new quantity
//Record in undo stack
undoStack.Add(new UpdateCustomerAction(customer.Id, "UpdateQuantity")
}
//Implementing the "undo" button
void OnUndo ()
{
if (undostack.Count == 0)
return;
//Get the last action performed
var action = undoStack.Pop();
action.Undo();
}
This is a very simplistic approach. There are many different ways to implement it. You might even find a third party undo library that might be helpful. Undo is not a simple process so if you're not already comfortable with data structures, collections and ref types you might want to bone up on them first.
Michael Taylor http://www.michaeltaylorp3.net
Thursday, January 18, 2018 5:04 PM
Oh, i really don't know it was a ref type. Basically what i'm thinking was saving the whole list in every pop, maybe requires a lot of memory but this is just a simple operation. So when the users adds something on the list, i will create another list, and so on.
I'll try your suggestion.
Many thanks
Thursday, January 18, 2018 5:11 PM
Of course is a stack collection the class which you need for do and undo.
However, read for the rest all Michael wrote.
As addition, it can be a memory eater in the way you do it, because you lock all objects from releasing by the GC. So think twice if you do it with an X86 environment.
(Somehow I get the idea Michael was first starting to write about the stack mechanism before he switched to the collection)
:-)
Success Cor
Friday, January 19, 2018 12:38 AM
Hello,
After reading the other replies, did you get a solution? If was my project for an Undo/Redo
function then I would use List to hold Customer data, and Stack to hold changes per
Customer Entry, like address or phone numbers. There limits as the other have already
explained. You could use a Dynamic Circular Que for undo/redo and use the current index
as a reference to the most recent update and back track from there for Undo. There would
be a lot of overhead in code to maintain a large buffer with any mechanism implemented.
Hope this helps :)
Friday, January 19, 2018 1:20 PM
"If was my project for an Undo/Redo
function then I would use List to hold* Customer* data, and Stack to hold changes per
Customer Entry, like address or phone numbers. "
Yes, this is exactly what i am trying to do right now, but i do have a different problem.
Lets say i have a list of order for storing current order, and an order stack for my undo operation. When the user add a new order/entity, i will push that to the stack, when the user add again the same product/order, i will push it again to the stack but i will just update my list showing just showing a single order but will update the order quantity.
public class Order
{
public int Id { get; set; }
public string OrderModel { get; set; }
public int Quantity { get; set;}
}
Example
OrderId=1
OrderModel="Product A"
ProductQuantity=10
ProductId = 1
ProductModel = "Product A"
ProductQuantity = 5
Expected Output
ProductId = 1
ProductModel = "Product A"
ProductQuantity = 5
Since i have two items on my stack, undoing my operation is a bit tricky to me. I need now to merge the objects/entity on my stack and show it as a list. Since stack is a collection as well, i can just cast it to a list. My problem is when i need to undo and merge similar items on my stack.
Monday, January 29, 2018 4:26 PM
Hi Dikong42,
What is the similar standard?
**>>My problem is when i need to undo and merge similar items on my stack.
You could try to foreach the stack collection and compare all of them. If you similar means the same, after compare, delete one of the same one.
If you want to undo, try to use Stack.Pop method.
https://msdn.microsoft.com/en-us/library/system.collections.stack.pop.aspx
Best Regards,
Wendy
MSDN Community Support
Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact [email protected].