UWP: Conditionally Navigating from OnNaviagted to another page without input - c#

When the application runs, I want to check a condition that will determine where the page navigates to. There isn't any input from the user so there isn't an Object sender. How can I accomplish this?
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
var Albums = await App.DataModel.GetAlbum();
if (Albums != null)
{
Frame.Navigate(typeof(ViewModel.AlbumView));
}
else
{
Frame.Navigate(typeof(ViewModel.AddAlbumView));
}
}

in the App.Xaml.cs there is line in the OnAppLaunchedEvent
rootframe.Navigate(typeof(MainPage));
replace this with your condition
var Albums = await App.DataModel.GetAlbum();
if (Albums != null)
{
Frame.Navigate(typeof(ViewModel.AlbumView));
}
else
{
Frame.Navigate(typeof(ViewModel.AddAlbumView));
}
and don't forget to bring your albums and viewmodel into app.
Just as a good practice make the viewModel static and ref it in
other pages that require it . this ensures u are following a SingleTon
pattern and u dont have multiple copies of ur Viewmodel at the same time.

Related

I need to update a Blazor page to update whenever the query string changes

I have a Blazor component named NavBar.razor that displays a Radzen navigation menu with a list of tags. When the user clicks a tag (RadzenPanelMenuItem), the component OrderBrowser.razor is loaded into the page next to the menu. The query string lets OrderBrowser.razor know what tag was selected. (See the OnInitializedAsync method below.) The component loads the associated orders into a grid.
This works fine the first time the user clicks a tag, but when they click a different tag, the OnInitializedAsync method does not execute, even though the uri changes. So I added an event handler to force a reload when the uri changes. This works, but, for some reason, it seems to reload twice, resulting in an undesirable blink when it reloads the 2nd time.
Does anyone know a better way to do this? Thanks.
Code from NavBar.razor:
#foreach (var item in TagsAndCounts)
{
<Radzen.Blazor.RadzenPanelMenuItem
Text="#(item.Tag + " (" + item.Count + ")")"
Path="#("orders/browse?tag=" + item.Tag)" />
}
Order grid from OrderBrowser.razor:
<OrderGrid Data="#orders" AllowPaging="false" />
Code from OrderBrowser.razor:
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
NavManager.LocationChanged += NavManager_LocationChanged;
var uri = NavManager.ToAbsoluteUri(NavManager.Uri);
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("tag", out var tag))
{
orders = await orderService.GetOrdersForTagAsync(tag);
}
}
private void NavManager_LocationChanged(object sender, LocationChangedEventArgs e)
{
NavManager.NavigateTo(NavManager.Uri, forceLoad: true);
}
Use this event
[Parameter]
[SupplyParameterFromQuery]
public string? Page { get; set; } = "0";
protected override void OnParametersSet()
{
//Fire your Code her
}
When you go from: yourUrl/tag/tagname1 and click to link yourUrl/tag/tagname2 it does not fire OnInitializedAsync, because new page was not created. It is intended and correct behavior. But bit confusing.
You can leverage new, with .net6 introduced, capability of SupplyFromQueryParameter attribute, which will change its value based on query string.
[Parameter, SupplyParameterFromQuery(Name = "tag")] public string Tag { get; set; } = "";
Now you can do the magic inside seeter of the Tag property. Or, if you need to call async method inside setter (which is not a good practice), you can use OnParametersSet method. It is called right after parameters has been changed. So you also need a mechanism to check if tag parameter has been changed (because it is called every time *some* parameter has been changed )
bool tagUpdated = true;
string _tag ="";
[Parameter,SupplyParameterFromQuery] public string Tag { get => _tag; set { if (value != _tag) { _tag = value; tagUpdated = true; } } }
protected override async Task OnParametersSetAsync()
{
if (tagUpdated)
{
tagUpdated = false;
await YourAsyncCallAndOtherMagic();
}
}
Note, that SupplyFromQueryParameter works only on pages (.razor components with #page directive)
(still not the most beautiful solution I guess. Open for suggestions...)
I found a solution. It's still a bit of a hack, but it seems to work. If anybody has any better solutions, please let me know. I just changed the event handler to get the new URL and update the page, instead of forcing a reload.
private async void NavManager_LocationChanged(object sender, LocationChangedEventArgs e)
{
orders = await orderService.GetOrdersForTagAsync(tag);
var index = NavManager.Uri.LastIndexOf("/");
tag = NavManager.Uri.Substring(index + 1);
if (tag != "open_orders")
{
StateHasChanged();
}
}

Navigation Helper SaveState / LoadState works incorrect

First, I read a list of entries from a database and display it in a ListView. When I leave the page to show details of an entry, then go back to the list, everything is ok.
Next, I open another page to add one entry to the database.
Go back to the list, reading from database shows me the correct count.
When I go to display one detail, the correct count is stored in SaveState.
Go back to the list, LoadState give the wrong count. It's the former state.
Display other details and go back now works with the old list and do not show me the added entry.
This is my code:
private void getList()
{
memoList = new List<MemoItem>();
db.loadHistory(ref memoList);
DelButton.IsEnabled = false;
System.Diagnostics.Debug.WriteLine("GetList, memoList[{0}]", memoList.Count);
}
private void NavigationHelper_SaveState(object sender, SaveStateEventArgs e)
{
if (memoList != null)
{
e.PageState["MemoItem"] = memoList;
if (memoSelected > -1)
e.PageState["memoSelected"] = memoSelected;
System.Diagnostics.Debug.WriteLine("SaveState, memoList[{0}]", memoList.Count);
}
}
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
if (e.PageState != null)
{
if (e.PageState.ContainsKey("MemoItem"))
{
memoList = (List<MemoItem>)e.PageState["MemoItem"];
System.Diagnostics.Debug.WriteLine("LoadState, memoList[{0}]", memoList.Count);
if (e.PageState.ContainsKey("memoSelected"))
memoSelected = (int)e.PageState["memoSelected"];
MemoListView.ItemsSource = memoList;
MemoListView.Visibility = Visibility.Visible;
}
else
{
getList();
showList();
}
}
}
Here are the Systems.Diagnostic outputs with comments in ():
GetList, memoList[8] (first time loaded)
SaveState, memoList[8] (leave the list to display details for one entry)
LoadState, memoList[8] (come back to the ListView)
SaveState, memoList[8] (leave the list to another page and add one entry)
GetList, memoList[9] (back to the list, read the correct entry count from database)
SaveState, memoList[9] (leave the ListView to display details for one entry)
LoadState, memoList[8] (come back to the ListView loads the wrong old list)
SaveState, memoList[8] (and works with the old list...)
LoadState, memoList[8] (...)
Remark: I can't call GetList from database every time, because I have to preserve checkmarks in the list which are not contained in the database.
What is wrong in my code? How can I resolve this problem? How to invalidate the StateEvent data after availability of a new list from database?
The problem is solved now. It was actually a consequence of the back stack manipulation.
I have four basic pages in my program, for which a GoBack makes sense. But I do not want to go back deeper. (I do not think anyone wants to go back 20 or more steps, but the seemingly unlimited BackStack would allow that.The required memory consumption is not negligible.)
In order to clear the BackStack at selecting one of the basic pages, I had this:
private void NavButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
if (b != null && b.Tag != null)
{
Type pageType = Type.GetType(b.Tag.ToString());
if (pageType != null)
{
if (rootFrame.CurrentSourcePageType != pageType)
{
rootFrame.Navigate(pageType, rootFrame);
// No goBack for Basic Pages
if (testBasicPage(pageType))
{
while (rootFrame.BackStackDepth > 1)
{
rootFrame.BackStack.RemoveAt(0);
}
}
}
}
}
}
That worked fine until I came across the problem with SaveState / Load State.
Now I first empty the BackStack and then navigate to the target page. That works.
private void NavButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
if (b != null && b.Tag != null)
{
Type pageType = Type.GetType(b.Tag.ToString());
if (pageType != null)
{
if (rootFrame.CurrentSourcePageType != pageType)
{
// No goBack for Basic Pages
if (testBasicPage(pageType))
{
while (rootFrame.BackStackDepth > 0)
{
rootFrame.BackStack.RemoveAt(0);
}
}
rootFrame.Navigate(pageType, rootFrame);
}
}
}
}

save and load Listbox Items locally and pass them to other pages

I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..

saving state between pages when creating a windows 8 app

I've been following the Create your first Windows Store app using C# or Visual Basic tutorials provided by Microsoft but am having some problems saving state when navigating between pages.
Create your first Windows Store app using C# or Visual Basic
Part 3: Navigation, layout, and views
Basically I've noticed that if I navigate from the main page to the photo page select a photo, navigate back to the main page and then go to the photo page again it doesn't remember the photo that was selected. I'm using the following code to navigate to the photo page from the main page.
private void photoPageButton_Click(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(PhotoPage));
}
In the photo page the loadstate method is
protected async override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
if (pageState != null && pageState.ContainsKey("mruToken"))
{
object value = null;
if (pageState.TryGetValue("mruToken", out value))
{
if (value != null)
{
mruToken = value.ToString();
// Open the file via the token that you stored when adding this file into the MRU list.
Windows.Storage.StorageFile file =
await Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken);
if (file != null)
{
// Open a stream for the selected file.
Windows.Storage.Streams.IRandomAccessStream fileStream =
await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Set the image source to a bitmap.
Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
new Windows.UI.Xaml.Media.Imaging.BitmapImage();
bitmapImage.SetSource(fileStream);
displayImage.Source = bitmapImage;
// Set the data context for the page.
this.DataContext = file;
}
}
}
}
}
The photo page save state is
protected override void SaveState(Dictionary<String, Object> pageState)
{
if (!String.IsNullOrEmpty(mruToken))
{
pageState["mruToken"] = mruToken;
}
}
I've noticed that the pagestate is always null when navigated to. Any ideas?
Enable NavigationCacheMode property of the page and add NavigationCacheMode="Enabled"
OR
Enable it by properties panel.
I did this tutorial too and I found one solution to save the state across pages navigation.
First, override the OnNavigatedFrom in order to save the file token into State Frame:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var state = SuspensionManager.SessionStateForFrame(this.Frame);
state["mruToken"] = mruToken;
}
Override the OnNavigatedTo in order to load the token from the state:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var state = SuspensionManager.SessionStateForFrame(this.Frame);
if (state != null && state.ContainsKey("mruToken"))
{
object value = null;
if (state.TryGetValue("mruToken", out value))
{
// the same code as LoadState to retrieve the image
}
}
}
In fact, I wrote another function to retrieve the image so it can be used in both LoadState and OnNavigatedTo methods.
private async void restoreImage(object value)
{
if (value != null)
{
mruToken = value.ToString();
// Open the file via the token that you stored when adding this file into the MRU list.
Windows.Storage.StorageFile file =
await Windows.Storage.AccessCache.StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(mruToken);
if (file != null)
{
// Open a stream for the selected file.
Windows.Storage.Streams.IRandomAccessStream fileStream =
await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Set the image source to a bitmap.
Windows.UI.Xaml.Media.Imaging.BitmapImage bitmapImage =
new Windows.UI.Xaml.Media.Imaging.BitmapImage();
bitmapImage.SetSource(fileStream);
displayImage.Source = bitmapImage;
// Set the data context for the page.
this.DataContext = file;
}
}
}
The problem is coming from the NavigationHelper OnNavigateTo method
public void OnNavigatedTo(NavigationEventArgs e)
{
var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
this._pageKey = "Page-" + this.Frame.BackStackDepth;
if (e.NavigationMode == NavigationMode.New)
{
// Clear existing state for forward navigation when adding a new page to the
// navigation stack
var nextPageKey = this._pageKey;
int nextPageIndex = this.Frame.BackStackDepth;
while (frameState.Remove(nextPageKey))
{
nextPageIndex++;
nextPageKey = "Page-" + nextPageIndex;
}
// Pass the navigation parameter to the new page
if (this.LoadState != null)
{
this.LoadState(this, new LoadStateEventArgs(e.Parameter, null));
}
}
else
{
// Pass the navigation parameter and preserved page state to the page, using
// the same strategy for loading suspended state and recreating pages discarded
// from cache
if (this.LoadState != null)
{
this.LoadState(this, new LoadStateEventArgs(e.Parameter, (Dictionary<String, Object>)frameState[this._pageKey]));
}
}
}
if (e.NavigationMode == NavigationMode.New) if always true because Frame by default creates a new instance of the Page. See Frame Class Remarks. So The LoadState event handler is always called with a null state parameter
if (this.LoadState != null)
{
this.LoadState(this, new LoadStateEventArgs(e.Parameter, null));
}
Now if you look at the complete code for PhotoPage.xaml very closely you will notice that in the page header there is this NavigationCacheMode="Enabled" that is what makes it PhotoPage works.
There no need for all that code about saving states in the Page. The Frame class does that for you when the Page sets its NavigationCacheMode.

Not able to navigate to pages on Windows Metro App using c#

When my UserLogin page loads, i want to check for user database, and if it doesn't exist, or can't be read, i want to direct it to NewUser page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
CheckForUser();
if (UserExists == false)
this.Frame.Navigate(typeof(NewUser));
}
The problem is that it never navigates to NewUser, even when i comment out the if condition.
Navigate can't be called directly form OnNavigatedTo method. You should invoke your code through Dispatcher and it will work:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
CheckForUser();
if (UserExists == false)
Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() => this.Frame.Navigate(typeof(NewUser)));
}
This happens because your app tries to navigate before the current frame completely loaded. Dispatcher could be a nice solution, but you have to follow the syntax bellow.
using Windows.UI.Core;
private async void to_navigate()
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.Frame.Navigate(typeof(MainPage)));
}
Replace MainPage with your desired page name.
Call this to_navigate() function.
you can try this and see if this works
frame.Navigate(typeof(myPage)); // the name of your page replace with myPage
full example
var cntnt = Window.Current.Content;
var frame = cntnt as Frame;
if (frame != null)
{
frame.Navigate(typeof(myPage));
}
Window.Current.Activate();
or
if you want to use a 3rd party tool like Telerik try this link as well
Classic Windows Forms, Stunning User Interface
I see you override OnNavigatedTo method but do not call base method. It may be the source of problem.
Try calling base method before any logic:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
CheckForUser();
if (UserExists == false)
this.Frame.Navigate(typeof(NewUser));
}
Use Dispatcher.RunIdleAsync to postpone your navigation to another page until UserLogin page is completely loaded.
The others are correct, but since Dispatcher doesn't work from the view model, here's how to do it there:
SynchronizationContext.Current.Post((o) =>
{
// navigate here
}, null);

Categories

Resources