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.
Related
I'm developing custom controls with security included. When the user does not have access to the control, the control makes itself disable but also go invisible. The control is not rendered and it does not appear on the page. At this point, everything is fine.
My question is how I can secure the control the prevent user to change the value?
I have injected an input in my form with Chrome HTML Inspector because like it should be, the field is not rendered, when I submit the form with the injected input with a new value, the server has the new value in the control value property.
public enum UserRole {
Standard,
Administrator,
[...]
}
//For this example, my custom control is derived from HtmlInputText. [ToolboxData("<{0}:MyCustomControl runat=\"server\"></{0}:MyCustomControl>")]
public class MyCustomControl: System.Web.UI.HtmlControls.HtmlInputText
{
public UserRole? MinimumRoleRequired { get; set; }
protected override void OnLoad(EventArgs e)
{
//Simplified version
if (this.Page.CurrentUser.Role < this.MinimumRoleRequired)
{
this.Visible = false;
this.Disabled = true;
return;
}
[...]
}
protected override void Render(HtmlTextWriter writer)
{
if (!this.Visible || this.Disabled)
{
return;
}
[...]
}
[...]
}
//My page who contain the control:
//HTML (MyPage.aspx)
<Controls:MyCustomControl ID="tbAdminOnly"runat="server"></Controls:MyCustomControl>
//C# (MyPage.aspx.cs)
public partial class UserEdit : Page
{
protected override void OnInit(EventArgs e)
{
this.tbAdminOnly.MinimumRoleRequired = UserRole.Administrator;
[...]
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (this.IsPostBack)
{
string postBackValue = tbAdminOnly.Value;
return;
}
tbAdminOnly.Value = "Hello world!";
}
}
When I load the page as a standard user, the control is not rendered. But if I inject input in the html page
//Note, i need to know the valid name/id but it could be done.
<input type="text" name="tbAdminOnly" id="tbAdminOnly" value="Damn shit">
The postBackValue is now the new value from the injected input. How I can prevent that?
Thanks.
To prevent user from injecting html controls, you need to sanitize the inputs. There are similar posts. How to use C# to sanitize input on an html 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.
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)
{
}
}
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!
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();
}
}