Single UndoRedoContext across multiple objects

Mar 24, 2010 at 7:20 PM

How would I reuse a single UndoRedoContext across multiple items and multiple instances?

Thanks,

Ilya

Mar 26, 2010 at 7:15 PM
Edited Mar 26, 2010 at 7:15 PM

If anyone is following this project, I made the change referred to in the OP to track actions in the AllActions dictionary as object and passed in the Object instance when registering commands instead of command name. This seems to have worked for now, but I'm doing more testing to make sure.

Here's the code:

 

public UndoRedoContext()
        {
            this.AllActions = new Dictionary<object, WeakReference>();
            this.UndoStack = new Stack<Tuple<object, object>>();
            this.RedoStack = new Stack<Tuple<object, object>>();

I then changed the based classes' Name property to ObjectReference:object. Finally, when declaring my command, I did this:

 

AddElementCommand = new UndoableDelegateCommand<AbstractElementViewModel, AbstractElementViewModel>(
                AddElementCommand_Execute,
                AddElementCommand_CanExecute,
                AddElementCommand_Undo,
                AddElementCommand_Redo,
                this,
                UndoRedoContextService.Instance.Context);


 

Ilya
Apr 12, 2010 at 4:57 PM

Hey,

The reason it was initially done with a string key, rather than a straight object reference was the potential for memory leaks. The way you've suggested might prevent your objects from being properly garbage-collected.

Sanjay

Sep 11, 2010 at 10:52 PM

Hi,


I also try to use only one UndoRedoContext for several ViewModels. But this code does'nt work:

public class MainWindowViewModel : UndoableViewModel
    {
        #region Fields

        private UndoableProperty<PresentationViewModel> m_refPresentation;

        #endregion

        #region Constructor

        public MainWindowViewModel()
            : base()
        {
            Undo = UndoRedoContextService.Instance.GetUndoCommand();
            Redo = UndoRedoContextService.Instance.GetRedoCommand();

            m_refPresentation = new UndoableProperty<PresentationViewModel>(this, "Presentation", UndoRedoContextService.Instance, new PresentationViewModel());

        }

        #endregion

        #region Properties

        public PresentationViewModel Presentation
        {
            get
            {
                return m_refPresentation.GetValue();
            }
            set
            {
                m_refPresentation.SetValue(value);
            }
        }

        #endregion

        #region Commands

        public ICommand Undo { get; set; }
        public ICommand Redo { get; set; }

        #endregion
    }

    public class PresentationViewModel : UndoableViewModel
    {
        #region Fields

        private UndoableProperty<String> m_strPresentationName;

        #endregion

        #region Constructor

        public PresentationViewModel()
            : base()
        {
            m_strPresentationName = new UndoableProperty<string>(this, "PresentationName", UndoRedoContextService.Instance, "Sport");
        }

        #endregion

        #region Properties

        public String Name
        {
            get
            {
                return m_strPresentationName.GetValue();
            }
            set
            {
                m_strPresentationName.SetValue(value);
            }
        }

        #endregion
    }

public class UndoableViewModel : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        /// <summary>
        /// 
        /// </summary>
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add
            {
                UndoRedoContextService.Instance.PropertyChanged += value;
            }
            remove
            {
                UndoRedoContextService.Instance.PropertyChanged -= value;
            }
        }

        #endregion
    }

public class UndoRedoContextService
    {
        #region Singleton

        /// <summary>
        /// 
        /// </summary>
        protected static UndoRedoContext m_refUndoRedoContext = null;

        /// <summary>
        /// Instance
        /// </summary>
        public static UndoRedoContext Instance
        {
            get
            {
                if (m_refUndoRedoContext == null)
                    m_refUndoRedoContext = new UndoRedoContext();

                return m_refUndoRedoContext;
            }
        }

        #endregion
    }

And in MainWindow, I get this code:

<Window x:Class="UndoTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:UndoTest.ViewModels"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <StackPanel Orientation="Horizontal" Height="30" HorizontalAlignment="Center">
                <Button Content="Undo" Command="{Binding Undo}"/>
                <Button Content="Redo" Margin="10,0,0,0" Command="{Binding Redo}"/>
            </StackPanel>
            <TextBox Text="{Binding Presentation.Name, UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="20"/>
        </StackPanel>
    </Grid>
</Window>
But, when I enter "Football" for example instead of Sport in TextBox. When I click on Undo button, I don't see the old value on textbox ("Sport").

Do you have an idea to explain this ? I checkout your UndoFramework.Core code, I put some breakpoints and I don't understand because all actions are well stored.

Thanks.

 

Sep 12, 2010 at 1:46 AM

Hi,

Unfortunately, my desktop just died, so it's going to be a couple of days before I can install Windows and VS2010 on my laptop. If possible, I'd like a copy of your bits so I can debug. It doesn't have to be your full project (if theres a confidentiality issue), all I need I need is a sample demonstrating the bug. 

Although, just from quickly eyeballing your code, I'd say its because in the registration for the Name property, inside the PresentationViewModel class shown here:

 m_strPresentationName = new UndoableProperty<string>(this, "PresentationName", UndoRedoContextService.Instance, "Sport");

The second parameter is meant to the be the name of the property, in order that the INotifyPropertyChanged interface's method can get called properly. Try changing that to just "Name" to see if that will fix your problem. If that doesnt fix it, then send me a copy if possible. I should get VS up and running in a couple of days.

Sep 12, 2010 at 3:55 PM

Hi,

You're right: I changed the second parameter from "PresentationName" to "Name" and now, it's working !

Thanks for your quick and useful answer !

Sep 12, 2010 at 6:42 PM

No problem, glad to help. Let me know if you have any more problems.

Just fyi, this was more of a proof-of-concept of an undo-redo system I was doing as an academic project, so while I didn't do extensive testing, I'm 90 percent sure the code's bug free. So if you run into any more problems, let me know so I can fix it.

Sanjay