I am working on a WPF web application with a MainPage which is using ninject to load pages.
Basically I am woundering if there is a way to tell NavigationService that an Id has changed because a user updated a value in a composite key.
I'll do my best to explain a little more. My MultiTagPage has a MultiTagViewModel and it shows a multitagvalue consisting of (MultiTagDef, Key, Value). There is also a DataGrid with other values with the same key and MultiTagDef. If you dubble click on one of those values the corresponding multitagvalue is showed instead.
In the database the multitagvalues are stored with composite keys (Multitagdef.Name, key, value). So if somebody updates a multitagvalue the Id is changed (say from (A, B, C) to (A, B, D)) and saved, then if the user proceeds to another multitagvalue in the datagrid (A, B, E) and perhaps deletes that object then the navigationservice try to load (A, B, C) instead of (A, B, D).
The architecture is designed for objects which has an ID column which, of course, never changes. Unfortunately adding an ID column is not an option in this case. So does anyone have a suggestion of how to solve this? Should I try to reload the page everytime somebody saves or could I tell the NavigationService that the current object now has changed ID?
Now, code:
From MainPage:
public partial class MainPage : IUIService
{
public static readonly DependencyProperty MainModelProperty = PropertyHelper.Register<MainPage>(x => x.MainModel);
public MainModel MainModel
{
get { return (MainModel)GetValue(MainModelProperty); }
set { SetValue(MainModelProperty, value); }
}
private static readonly ILog log = LogManager.GetLogger(typeof(MainPage));
public MainPage()
{
// Make doubly sure...
ShowsNavigationUI = false;
InitializeComponent();
App.Kernel.Rebind<IUIService>().ToConstant(this);
MainModel = App.Kernel.Get<MainModel>();
WindowTitle = MainModel.Title;
ContentFrame.Navigating += Navigating;
ContentFrame.Navigated += Navigated;
ContentFrame.NavigationFailed += NavigationFailed;
}
private void Navigating(object sender, NavigatingCancelEventArgs args)
{
object dataContext = null;
if(ContentFrame.Content is FrameworkElement) {
dataContext = ((FrameworkElement)ContentFrame.Content).DataContext;
} else if(ContentFrame.Content is FrameworkContentElement) {
dataContext = ((FrameworkContentElement)ContentFrame.Content).DataContext;
}
if(dataContext is ISaveable && ((ISaveable)dataContext).NeedsSave) {
if(MessageControl.UnsavedSync() != MessageControl.Button.Yes) {
args.Cancel = true;
}
}
}
private void Navigated(object sender, NavigationEventArgs e)
{
var mi = e.ExtraData as MenuItemModel;
if(mi == null) {
var page = e.Content as IMenuItemPage;
if(page != null) {
mi = page.MenuItem;
}
if(mi == null) {
log.DebugFormat("Navigated to {0} ({1}) without menu item", e.Content, e.Uri);
return;
}
}
MainModel.CurrentMenuItem = mi;
if(mi.Type != MenuItemType.Folder) {
Settings.Default.AddRecentMenuItem(new RecentMenuItem(mi.MenuItem));
}
}
#region Generic Edit command
private void EditCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.Handled = true;
e.CanExecute = false;
var param = Unwrap(e.Parameter);
var paramType = GetEditType(param);
if(paramType == null || !editPages.ContainsKey(paramType)) {
return;
}
e.CanExecute = ToConstructorArgument(param) != null;
}
private void EditExecuted(object sender, ExecutedRoutedEventArgs e)
{
Edit(e.Parameter);
}
private void Edit(object param)
{
var paramType = GetEditType(param);
if(paramType == null || !editPages.ContainsKey(paramType)) {
log.WarnFormat("Page for param {0} (type {1}) not found", param, paramType);
return;
}
if(param is IList) {
var list = (IList)param;
if(list.Count > 1000) {
Show("Too many items selected", "Please select no more than 1000 items at a time", messageTheme: MessageTheme.Warning);
return;
}
}
var arg = ToConstructorArgument(param);
if(arg == null) {
log.Warn("Unexpected parameter " + param + " supplied to EditExecuted");
return;
}
var pageType = editPages[paramType];
try {
log.DebugFormat("Got a parameter of type {0}, navigating to edit page {1}", param.GetType(), pageType);
Navigate(MakePage(pageType, arg));
} catch(Exception ex) {
log.Error("Unable to load edit page for parameter " + param, ex);
}
}
private static Page MakePage(Type pageType, params IParameter[] p)
{
var page = (Page)App.Kernel.Get(pageType, p);
var dp = ClientUtil.GetViewModelProperty(page.GetType());
if(dp != null) {
page.Loaded += (o, args) => {
var skrap = App.Kernel.Get(dp.PropertyType, p);
page.SetValue(dp, App.Kernel.Get(dp.PropertyType, p));
};
page.Unloaded += (o, args) => {
try {
page.ClearValue(dp);
} catch(Exception e) {
// Often happens when datagrid is in edit mode when navigating away from page:
// http://connect.microsoft.com/VisualStudio/feedback/details/571967/wpf-datagrid-causes-crash-with-sorting-is-not-allowed-during-an-addnew-or-edititem-transaction
log.Warn("Error while unloading page", e);
}
};
ViewModelBehavior.SetUpdateUIError(page, true);
}
return page;
}
#endregion
#region Navigate command
private void NavigateCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = e.Parameter is Type;
e.Handled = true;
}
private void NavigateExecuted(object sender, ExecutedRoutedEventArgs e)
{
var type = e.Parameter as Type;
if(type == null) {
return;
}
Navigate(MakePage(type));
e.Handled = true;
}
#endregion
#region IUIService implementation
public void Navigate(Type type, params Tuple<string, object>[] parameters)
{
Navigate(MakePage(type, parameters.Select(x => new ConstructorArgument(x.Item1, x.Item2)).ToArray()));
}
public void Navigate(Type type, MenuItemModel menuItem, params Tuple<string, object>[] parameters)
{
Navigate(MakePage(type, parameters.Select(x => new ConstructorArgument(x.Item1, x.Item2))
.Prepend(new ConstructorArgument("menuItem", menuItem))
.Prepend(new ConstructorArgument("props", menuItem.Params))
.ToArray()),
menuItem);
}
public void Navigate(Page content, object extraData = null)
{
if(ContentFrame != null) {
if(ContentFrame.Content is DependencyObject) {
foreach(var dg in WpfUtil.FindDescendants<DataGrid>((DependencyObject)ContentFrame.Content)) {
while(!dg.CommitEdit()) { /* Keep committing */ }
}
}
ContentFrame.Navigate(content, extraData);
}
}
public bool NavigateBack()
{
if(ContentFrame != null && ContentFrame.CanGoBack) {
ContentFrame.GoBack();
return true;
}
if(NavigationService != null && NavigationService.CanGoBack) {
NavigationService.GoBack();
return true;
}
return false;
}
}
Please tell me if you need more information.