Windows-XAML / Template10

Making Windows 10 apps great again
Apache License 2.0
1.41k stars 388 forks source link

OnNavigatedToAsync receives empty parameter #789

Closed Gh0s7 closed 8 years ago

Gh0s7 commented 8 years ago

I'm adding dynamically buttons to the MyHamburger inside Shell.xaml, based on user's input. Basically the user inserts the name of a category and hits the add button, having the new category shown on the hamburger. This new button should navigate to the detail page, unfortunately the OnNavigatedToAsync receives a new and dempty category as parameter and I don't understant why.

Here's some code:

Shell.xaml - addNewCategory method

var sp = new StackPanel
{
    Orientation = Orientation.Horizontal
};
sp.Children.Add(
    new SymbolIcon
    {
        Width = 48,
        Height = 48,
        Symbol = Symbol.Clock
    }
    );
sp.Children.Add(
    new TextBlock
    {
        Margin = new Thickness(12, 0, 0, 0),
        VerticalAlignment = VerticalAlignment.Center,
        Text = e.Name
    }
    );
var newButton = new HamburgerButtonInfo
{
    ClearHistory = false,
    PageType = typeof (CategoryPage),
    // Here e is 100% fine, category name is a valid name
    PageParameter = e,
    Content = sp
};
MyHamburgerMenu.IsOpen = true;
MyHamburgerMenu.PrimaryButtons.Add(newButton);

CategoryPageViewModel

public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
{
    // Here parametere is a Category with Name = null as if it was a newly created object    
    Category = suspensionState.ContainsKey(nameof(Category))
        ? (OtpCategory) suspensionState[nameof(Category)]
        : (OtpCategory) parameter;
    // Close the hamburger pane
    Shell.HamburgerMenu.IsOpen = false;
    await Task.CompletedTask;
}

Category model

public class Category
{
    #region Equals

    protected bool Equals(Category other)
    {
        return string.Equals(Name, other.Name);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        return obj.GetType() == GetType() && Equals((Category) obj);
    }

    public override int GetHashCode()
    {
        return Name?.GetHashCode() ?? 0;
    }

    public static bool operator ==(Category left, Category right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(Category left, Category right)
    {
        return !Equals(left, right);
    }

    #endregion

    #region Properties

    /// <summary>
    ///     Category name
    /// </summary>
    public string Name { get; }

    /// <summary>
    ///     List of all the entries associated to this category.
    /// </summary>
    public List<Entry> Entries;

    #endregion

    #region ctor

    /// <summary>
    ///     Empty ctor for serialization
    /// </summary>
    public Category()
    {
    }

    /// <summary>
    ///     Basic ctor
    /// </summary>
    /// <param name="name"></param>
    public Category(string name)
    {
        Name = name;
        Entries = new List<Entry>();
    }

    #endregion
}

What am I doing wrong?

EDIT:

For some reasons I tried removing the empty constructor and it now works, is this the correct behavior?

EDIT 2:

After removing that constructor it works, but the other constructor gets called a huge number of times during navigation, and everytime it resets the Entries list which will always be empty when I load the page. I feel like I'm missing the reason why the ctor is called so many times during navigation.

JerryNixon commented 8 years ago

Sub-classing anything should have a constructor like:

class MyClass: MyBase {
    public MyClass() : base() { }
}
Gh0s7 commented 8 years ago

I'm sorry but I don't know why you're talking about sub-classing since Category doesn't extend anything.

By the way I've added that kind of constructor and now both Name and Entries are null after navigation, as if instead of passing the object instance as reference it calls the empty constructor everytime the page loads.

So I removed the empty constructor from both Category and Entry to see what happens, and it leads to

System.Exception: Your parameter must be serializable. If it isn't, then use SessionState.

Next step was to add DataContract to both Category and Entry to make them serializable, but now it won't even navigate from the hamburger and I really have no clue on what's going on :(

EDIT:

To make things easier, since I think I'm having issues explaining my problem, here's a quick stripped-out version of my project: Repro.zip on Dropbox