Get data from second subpage to first page - Windows Phone(best approach) - c#

I want to ask you, what do you think, what is the best approach to get from second page to first page? I use something like this.(MVVM)
Second page:
public partial class AddProfilePageView : PhoneApplicationPage
{
public AddProfilePageView()
{
InitializeComponent();
DataContext = new AddProfileViewModel();
}
public AddProfileViewModel ViewModel { get { return DataContext as AddProfileViewModel; } }}
First page:
public partial class ProfilesPageView : PhoneApplicationPage
{
public ProfilesPageView()
{
InitializeComponent();
DataContext = new ProfilesViewModel();
}
public ProfilesViewModel ViewModel
{
get { return DataContext as ProfilesViewModel; }
}}
AddProfileViewModel() class has properties, that are binded to controls in xaml. From this page I need to get data to first page ProfilesPageView.
My solution is:
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
var content = e.Content as ProfilesPageView;
if (content != null && ViewModel.IsOk)
{
content.ViewModel.ProfilesList.Add(ViewModel.ProfileRecord);
}
}
So what do you think? Is it good solution how obtain data?
Thanks

In reality here you're not trying to get data from one page to another.
On the second/detail page you're adding a new item and then when you return to the first/main page you want it to update the displayed data to show the new item.
I'd assume that AddProfileViewModel and ProfilesViewModel are both persisting to disk/IsolatedStorage so you could just refresh the main/list/first page when returning to it.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// ...
if (e.NavigationMode == NavigationMode.Back)
{
(this.DataContext as ProfilesViewModel).Refresh();
}
}

Related

How to Extend Content Page

How can I extend the class Content Page to create a method that executes when it is the user's first time on a page?
(Xamarin.Forms)
This is probably the simplest way to do it:
public abstract class CustomContentPage : ContentPage
{
private bool _appeared;
protected override void OnAppearing()
{
base.OnAppearing();
if (!_appeared)
{
OnFirstAppearing();
_appeared = true;
}
}
protected abstract void OnFirstAppearing();
}
First you will want to use Application.Properties dictionary so you can preserve the state of the application, i.e. whether the user has ever gone to the page.
So in the OnAppearing method override in your ContentPage subclass, check to see if you have set the property in the Application.Current.Properties dictionary to see if the page has been visited. If the key is not present, set it and do whatever you want for this first visit to the page. If the key is present, do whatever you want to do for subsequent visits to the page, e.g.:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
if (!Application.Current.Properties.ContainsKey("main_page_visited"))
{
Application.Current.Properties["main_page_visited"] = true;
label.Text = "First Time visited";
}
else
{
label.Text = "Second+ Time visited";
}
}
}
The Application.Current.Properties gets saved automatically when the app exits.

Using new ViewModel each time I open a page

I didn't know how better to word the title so I went with solution that came to my mind.
Here is the problem. I have a page that has list and each item on the lists opens a detail page (on click). But the VM is reused, which causes me several problems.
Previous data can be seen for split second when opening a the detail page
I need certain properties to be set to specific values when the page open, but since the VM is reused it keeps all the values from the previous detail and this messes up my logic.
This UWP app. I'm using Template10 framework's NavigationService to move between pages.
Main Page ViewModel
public class MainPageViewModel : ViewModelBase {
private List<MangaItem> _mangaList;
public List<MangaItem> mangaList {
get { return _mangaList; }
set { Set(ref _mangaList, value); }
}
private string _mainSearchText;
public string mainSearchText {
get { return _mainSearchText; }
set { Set(ref _mainSearchText, value); }
}
public MainPageViewModel() {
_mangaList = new List<MangaItem>();
mangaList = new List<MangaItem>();
Initialize();
}
private async void Initialize() {
mangaList = await MangaListGet.GetListAsync();
}
public async void MainSearchSubmitted() {
mangaList = await MangaListGet.GetListAsync(_mainSearchText);
}
public void MangaSelected(object sender, ItemClickEventArgs e) {
var mangaItem = (MangaItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.MangaDetail), mangaItem.id);
}
}
And Detail Page ViewModel
class MangaDetailViewModel : ViewModelBase {
private MangaItem _mangaDetail;
public MangaItem mangaDetail {
get { return _mangaDetail; }
set { Set(ref _mangaDetail, value); }
}
private string _mangaId;
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState) {
_mangaId = parameter as string;
Initialize();
await Task.CompletedTask;
}
private async void Initialize() {
mangaDetail = await MangaDetailGet.GetAsync(_mangaId);
}
public void ChapterSelected(object sender, ItemClickEventArgs e) {
var _chapterId = (ChapterListItem)e.ClickedItem;
NavigationService.Navigate(typeof(Views.ChapterPage), _chapterId.id);
}
}
This code only shows the first problem is displaying previously loaded data for a split second. If needed I will add code that showcases the other problem, but I' not sure if it's really relevant right now. I'm thinking that maybe my entire logic is flawed or something.
EDIT:
<Page.DataContext>
<vm:ChapterPageViewModel x:Name="ViewModel" />
</Page.DataContext>
where vm is xmlns:vm="using:MangaReader.ViewModels".
Another solution is to use Bootstrapper.ResolveforPage() which is intended to handle dependency injection but would easily serve your needs. Like this:
[Bindable]
sealed partial class App : BootStrapper
{
static ViewModels.DetailPageViewModel _reusedDetailPageViewModel;
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
if (page.GetType() == typeof(Views.DetailPage))
{
if (_reusedDetailPageViewModel == null)
{
_reusedDetailPageViewModel = new ViewModels.DetailPageViewModel();
}
return _reusedDetailPageViewModel;
}
else
{
return null;
}
}
}
The NavigationService will treat this the same as any other view-model. Meaning it will call OnNavTo() and the other navigation overrides you include.
Best of luck.
While Template10 documentation states the NavigationCacheMode is disabled by default, that isn't the case in it's example templates (as of writing this). This is set in View C# code (.xaml.cs file).
.xaml.cs file
namespace MangaReader.Views {
public sealed partial class MangaDetail : Page {
public MangaDetail() {
InitializeComponent();
//NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled; //this was set by default
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Disabled;
}
}
}
Now, new ViewModel will be created each time you access a this page.

Properties of Webusercontrols

I have a question and I can not find the right terms to do a reasoned search and solve the question.
Let's see, when I'm creating a page, at some point I need to create a WebUserControl and defer something like state = "true" (like the text of the lables) inside the html tag so that as soon as the page loads , Whether or not that control is subsequently edited in code.
<MyControls:Teste Id="aaa" runat="server" state="false"/>
The test control code is as follows: (The HTML page of this control is blank, it only has the header)
public partial class WebUserControls_WUC_Tect : System.Web.UI.UserControl
{
private static bool state ;
public bool State
{
get { return state ; }
set { state = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
Problem:
Whenever the page returns to the server and is reloaded, the state variable is always set to false or true depending on the initial state I passed, what I intended was for this variable to be loaded only once at the beginning of the page and then Could only be changed by codebeind.
I am grateful for your suggestions.
greetings
Patrick Veiga
You need to use the ViewState to store the property value to keep the persistent value saved.
public partial class WebUserControls_WUC_Tect : System.Web.UI.UserControl
{
private static bool state ;
public bool State
{
get
{
if (ViewState["MyState"] == null)
{
ViewState["MyState"] = false;
}
return (bool)ViewState["MyState"];
}
set
{
ViewState["MyState"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
}
}

WP8 determining page navigation history

In my windows phone 8 app, I am navigating among pages. say
From MainPage(some user control a click to) -> Page1 then with a key press it will take me to main page back again
From MainPage(some user control b click to) -> Page2 and then back with the key press to main page again and so on to page3, page4 etc.
Here I want to identify from which page the main page is loaded from?
Say page1, page2 or page3 based on that I want to load the user controls according to navigation history.
I could not found any built-in property or function in a library.
Any help, I am looking for best practice as well to achieve such functionality.
You can use back stack for this kind of purposes.
How to navigate using the back stack for Windows Phone
See also:
https://stackoverflow.com/a/7000977/942286
make hashtable global.
Hashtable ht = new Hashtable();
ht.Add("page1");
NavigationService.Navigate(new Uri("Page1.xaml", UriKind.Relative));
You can create an
enum PageType
{
NONE,
PAGE_1,
PAGE_2,
PAGE_3,
PAGE_4
}
Then in your MainPage you can have a ivar as
private PageType pageType = PageType.NONE;
Then when you move to a page you can set it
pageType = PageType.PAGE_!;
NavigationService.Navigate(new Uri("Page1.xaml", UriKind.Relative));
and when you come back to MainPage then you can have this
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode == NavigationMode.Back)
{
switch (pageType)
{
case PageType.NONE:
//Your code
break;
case PageType.PAGE_1:
//Your code
break;
case PageType.PAGE_2:
//Your code
break;
case PageType.PAGE_3:
//Your code
break;
case PageType.PAGE_4:
//Your code
break;
}
}
}
EDIT
You can then use navigation context.
In your user control
NavigationService.Navigate(new Uri("Page1.xaml?PAGE_TYPE=Page1", UriKind.Relative));
In your MainPage
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (e.NavigationMode == NavigationMode.Back)
{
String pageType = null;
NavigationContext.QueryString.TryGetValue("PAGE_TYPE", out pageType);
if (pageType == "Page1")
{
//Your code
}
}
}
If you must know what page you came from, you can use PhoneApplicationService.Current.State. Within your secondary pages, override OnBackKeyPress and set the value
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
base.OnBackKeyPress(e);
PhoneApplicationService.Current.State["LastPage"] = this;
}
Then in the MainPage, you would override OnNavigatedTo and check the value of the state
protected override void OnNavigatedTo(NavigationEventArgs e)
{
object lastPage;
if((e.NavigationMode == NavigationMode.Back) &&
(PhoneApplicationService.Current.State.TryGetValue("LastPage", out lastPage)))
{
// we navigated back and we know what the last page was!
var pageName = lastPage.GetType().Name;
if (pageName == "Page1")
{
// do something!
}
}
}
Just pass parameters when you are navigating back to main page, maybe an enum type contains 'page1' 'page2' ...
It seems that the NavigationService doesn't contain the information you need, as well as the NavigatingCancelEventArgs parameter coming with OnNavigatingFrom.
So unfortunately, it seems there's no builtin feature addressing your need.
However , what you could do is to persist the last page your user has been (to handle tombstoning scenarios). For instance it could be in AppSettings, here is a pseudo code :
public class WhateverYouWantLastPagePersister
{
private const string LastPageID = "LastPage";
public string GetLastPage()
{
string lastPage = string.Empty;
IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>(LastPageID, out lastPage);
return lastPage;
}
public void PersistLastPage(string lastPage)
{
IsolatedStorageSettings.ApplicationSettings[LastPageID] = lastPage;
}
}
To persist "automatically" your last visited page, you can derive from PhoneApplicationPage and override OnNavigatingFrom to create a your own base page :
public class BasePage : PhoneApplicationPage
{
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
Uri currentUri = ((App)Application.Current).RootFrame.CurrentSource;
WhateverYouWantLastPagePersister.PersistLastPage(CustomMethodToExtractPageNameFromUri(currentUri));
}
}
All pages needing to persist the last visited pages have to derived from it (so Page1, Page2...)
Finally you can use WhateverYouWantLastPagePersister.GetLastPage() to get what you need.
Of course, in these pseudo code samples :
lastPage can be of any type you want (string, enum , int and so
on...), you just need an appropriate custom converter from string to anything you want
CustomMethodToExtractPageNameFromUri is a simple string parser
WhateverYouWantLastPagePersister can be static class, a singleton, injected with a DI framework, it's up to you!

Accessing usercontrols in code-behind in ASP.NET

This question is for an ASP.NET guru. Its driving me nuts.
I have inherited an ASP.NET Web Forms application. This application uses a complex
structure of nested user controls. While complex, it does seem necessary in this case.
Regardless, I have a page that uses a single UserControl. We will call this UserControl
root control. This UserControl is defined as follows:
widget.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeFile="widget.ascx.cs" Inherits="resources_userControls_widget" %>
<div>
<asp:Panel ID="bodyPanel" runat="server" />
</div>
widget.ascx.cs
public partial class resources_userControls_widget : System.Web.UI.UserControl
{
private string source = string.Empty;
public string Source
{
get { return source; }
set { source = value; }
}
private string parameter1 = string.Empty;
public string Parameter1
{
get { return parameter1; }
set { parameter1 = value; }
}
private DataTable records = new DataTable();
public DataTable Records
{
get { return records; }
set { records = value; }
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
UserControl userControl = LoadControl(source) as UserControl;
if (parameter1.Length > 0)
userControl.Attributes.Add("parameter1", parameter1);
bodyPanel.Controls.Add(userControl);
}
private void InsertUserControl(string filename)
{
}
}
In my application, I am using widget.ascx in the following way:
page.aspx
<uc:Widget ID="myWidget" runat="server" Source="/userControls/widgets/info.ascx" />
page.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
DataTable table = GetData();
myWidget.Records = table;
}
Please notice how info.ascx is set as the UserControl we want to load in this case. This approach is necessary in this case. I've removed the extraneous code that justifies it to focus on the problem. Regardless, in info.ascx.cs I have the following:
info.ascx.cs
protected void Page_Load(object sender, EventArgs e)
{
// Here's the problem
// this.Parent.Parent is a widget.ascx instance.
// However, I cannot access the Widget class. I want to be able to do this
// Widget widget = (Widget)(this.Parent.Parent);
// DataTable table = widget.Records;
}
I really need to get the value of the "Records" property from the Parent user control. Unfortunately, I can't seem to access the Widget class from my code-behind. Are there some rules about UserControl visibility at compile time that I'm not aware of? How do I access the Widget class from the code-behind of info.ascx.cs?
Thank you!
Firstly you need to create an interface and implement it to the Widget user control class.
For instance,
public interface IRecord
{
DataTable Records {get;set;}
}
public partial class resources_userControls_widget : System.Web.UI.UserControl, IRecord
{
...
}
And in code behind of Info.ascx.cs,
protected void Page_Load(object sender, EventArgs e)
{
// Here's the problem
// this.Parent.Parent is a widget.ascx instance.
// However, I cannot access the Widget class. I want to be able to do this
// Widget widget = (Widget)(this.Parent.Parent);
// DataTable table = widget.Records;
IRecord record=this.Parent.Parent;
DataTable table = widget.Records;
}
In your case, maybe better to use some server object's like ViewState or Session. Fill it within DataTable on your page and get it in Page_load event handler on info.ascx user control.

Categories