Skip to content

Instantly share code, notes, and snippets.

@renestein
Created January 3, 2011 10:08
Show Gist options
  • Save renestein/763320 to your computer and use it in GitHub Desktop.
Save renestein/763320 to your computer and use it in GitHub Desktop.
public class PageBase : PhoneApplicationPage
{
private List<Object> m_viewModels;
public PageBase()
{
IsNewInstance = true;
WasLayoutUpdateCalled = false;
LayoutUpdated += PageBase_LayoutUpdated;
m_viewModels = new List<Object>();
}
static PageBase()
{
ViewModelManager = () => new ViewModelResolver();
}
public bool IsNewInstance
{
get;
set;
}
public bool WasLayoutUpdateCalled
{
get;
set;
}
public static Func<IViewModelResolver> ViewModelManager
{
get;
set;
}
public Dictionary<String, Object> LastSavedTransientState
{
get;
set;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
WasLayoutUpdateCalled = false;
loadSavedTranzientState();
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
SaveState();
handleAllDeactivated();
}
private T tryGetTypedViewModel<T>(object obj)
where T: class
{
return obj as T;
}
protected virtual void SaveState()
{
var statebag = new KnownTypesDictionary();
var rootPair = getRootpair();
saveTransientState(rootPair, statebag, this, null);
string stateKey = GetType().FullName;
State[stateKey] = statebag;
LastSavedTransientState = statebag;
}
protected virtual void LoadState()
{
var rootPair = getRootpair();
restoreTransientState(rootPair, this, null);
}
private ElementIndexPair getRootpair()
{
var rootPair = new ElementIndexPair
{
Element = this,
Parent = null,
Index = 0
};
return rootPair;
}
private void saveTransientState(ElementIndexPair pair, Dictionary<string, Object> stateBag, FrameworkElement root, Object lastDataContext)
{
if (ViewModelManager().IsView(pair.Element))
{
var stateManager = getStateManager(pair.Element.DataContext);
if (stateManager != null && stateManager != lastDataContext)
{
lastDataContext = stateManager;
saveStateInner(pair, stateBag, root, stateManager);
}
}
UIHelper.GetChildren(pair.Element)
.ToList()
.ForEach(elementPair =>
{
elementPair.Parent = pair;
saveTransientState(elementPair, stateBag, root, lastDataContext);
});
}
private void saveStateInner(ElementIndexPair pair, Dictionary<string, Object> stateBag, FrameworkElement root, ITransientStateManager stateManager)
{
var state = stateManager.SaveState();
if (state != null)
{
var stateKey = UIHelper.GetTransientStateKey(pair, root);
stateBag[stateKey] = state;
}
}
private void restoreTransientState(ElementIndexPair pair, FrameworkElement root, Object lastViewModel)
{
if (ViewModelManager().IsView(pair.Element))
{
string stateKey = UIHelper.GetTransientStateKey(pair, root);
bool hasSavedState = LastSavedTransientState != null &&
LastSavedTransientState.ContainsKey(stateKey);
Object currentViewModel = prepareViewModel(pair.Element);
var stateManager = getStateManager(currentViewModel);
var initObject = getInitObjectWithSyncContext(currentViewModel);
lastViewModel = selectViewModel(pair.Element, lastViewModel, currentViewModel);
SendOrPostCallback setDataContext = _ => pair.Element.DataContext = lastViewModel;
if (stateManager != null && hasSavedState)
{
stateManager.LoadState(LastSavedTransientState[stateKey]);
}
else if (initObject != null)
{
initObject.Init();
}
activateAndSetDataContext(currentViewModel, setDataContext);
}
UIHelper.GetChildren(pair.Element)
.ToList()
.ForEach(elementPair =>
{
elementPair.Parent = pair;
restoreTransientState(elementPair, root, lastViewModel);
});
}
private void activateAndSetDataContext(object viewModel, SendOrPostCallback setDataContext)
{
notifyActivated(viewModel);
SynchronizationContext.Current.Send(_ => setDataContext(null), null);
}
private object selectViewModel(FrameworkElement element, Object lastViewModel, Object currentViewModel)
{
if (currentViewModel != null)
{
storeViewModel(currentViewModel);
return currentViewModel;
}
else
{
return lastViewModel;
}
}
private void storeViewModel(Object viewModel)
{
m_viewModels.Add(viewModel);
}
private void loadSavedTranzientState()
{
LastSavedTransientState = null;
Object savedState;
State.TryGetValue(GetType().FullName, out savedState);
LastSavedTransientState = savedState as Dictionary<String, Object>;
}
private IInitialize getInitObjectWithSyncContext(Object obj)
{
var initObject = tryGetTypedViewModel<IInitialize>(obj);
if (initObject != null)
{
initObject.SynchContext = SynchronizationContext.Current;
}
return initObject;
}
private object prepareViewModel(Object obj)
{
return ViewModelManager().ResolveViewModel(obj);
}
private ITransientStateManager getStateManager(Object obj)
{
var stateManager = obj as ITransientStateManager;
return stateManager;
}
private void PageBase_LayoutUpdated(object sender, EventArgs e)
{
handleLayoutUpdated();
}
private void handleLayoutUpdated()
{
if (WasLayoutUpdateCalled)
{
return;
}
if (IsNewInstance)
{
LoadState();
IsNewInstance = false;
}
else
{
handleAllActivated();
}
WasLayoutUpdateCalled = true;
}
private void handleAllActivated()
{
m_viewModels.ForEach(notifyActivated);
}
private void notifyActivated(object obj)
{
var activatedItem = tryGetTypedViewModel<IActivated>(obj);
if (activatedItem != null)
{
activatedItem.Activate();
}
}
private void handleAllDeactivated()
{
m_viewModels.ForEach(notifyDeactivated);
}
private void notifyDeactivated(object obj)
{
var deactivatedItem = tryGetTypedViewModel<IDeactivated>(obj);
if (deactivatedItem != null)
{
deactivatedItem.Deactivate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment