.net - Accessing a pages global variables from a user control - c#

I'd like to know if it's possible to access a page's global variables from a user control that is on that page.
public partial class index : System.Web.UI.Page
{
FormsIdentity id = (FormsIdentity)User.Identity;
string custID = "some guid";
string accID = "some guid";
protected void Page_Load(object sender, EventArgs e)
{
}
}
This is just an example, my actual global variables are more complex custom configuration objects but it should be the same.
I'd like to access the variables from a user control. this would be helpful since I'd need to use the same objects on both the page and the user control and I don;t really want to have retrieve and store in in memory twice.
Anyone know if/how to do this? Thanks!

Yes, you can. It is called passing the variables to the user control. Sure it is stored in memory more than once, but that is a bit of the "price" of keeping a separation of concerns and making reusable controls.
If the items are constants, you can store them as true globals and access via a static method in global.asax. If on a request basis, this won't work in a simple manner, but you can fashion it.
Now, for your not wanting to store in memory twice comment. I would not worry about this, as it is a small amount of information (1), especially since it will only last for the duration of the request (2). Not completely true, as GC does not fire immediately.

If you are depending on this information over multiple controls, its best to have a UserControlBase that inherits from UserControl and then make your controls inherit from UserControlBase. Do the same with the Page classes. Then you can add the property to your BasePage. To reference in your UserControlBase just do...
protected UserInfo UserInfo
{
get
{
return ((BasePage)this.Page).UserInfo;
}
}

Related

Is using hidden fields always better than session when remembering state on a single page?

When you need to remember state across postbacks or callbacks i am led to believe that Session shouldn't be used.
One reason for this is this will cause issues if multiple tabs are open as an action in one tab will result in the session variable being changed so it will effect the other tabs.
For example if i need to persist a collection of objects on a page i can do so via session like so.
protected void Page_Load(object sender, EventArgs e)
{
List<MyClass> myClassList = Session["MyClassList"] as List<MyClass>;
if(myClassLIst == null)
myClassList = new List<MyClass>
//Do something with collection
}
protected void AddButton_Click(object sender, EventArgs e)
{
List<MyClass> myClassList = (List<MyClass>)Session["MyClassList"]
myClassList.Add(new MyClass{ Name = NameTextBox.Text});
Session["MyClassList"] = myClassList;
}
However due to the reason stated above i don't see any reason why i wouldn't just handle this using hiddenfields (Viewstate is another option) like below.
protected void Page_Load(object sender, EventArgs e)
{
//HiddenField.Value will store json data as a string
List<MyClass> myClassList = JsonConvert.DeserializeObject
<List<MyClass>>(HiddenField.Value);
//Do something with collection
}
protected void AddButton_Click(object sender, EventArgs e)
{
List<MyClass> myClassList = JsonConvert.DeserializeObject
<List<MyClass>>(HiddenField.Value);
myClassList.Add(new MyClass{ Name = NameTextBox.Text});
HiddenField.Value = JsonConvert.SerializeObject(myClassList));
}
Here i am storing the collection in a hidden field as Json and serializing/deserializing it using Json.net. This solves the issues with tabs as each page is now responsible for it's on state.
My question is am i right in concluding that Session should only be used when remembering data across multiple pages? And is using hidden fields always better? I see many example where people use session to remember state in a single page so i may be missing something.
Pretty much, yes, page specific data should not be stored in Session, since that can cause issues if multiple tabs/windows are open.
Using ViewState directly is a good alternative. Or using HiddenFields will work fine if you need to access the values in them from the client side. That's really the decision point between using ViewState and HiddenFields.
If data is specific to a single page, use ViewState. Move to a HiddenField if that data needs to be shared with client-side code. If the data needs to persist across multiple page requests, move to Session.
A potential issue around storing data in Session is that it may be be shared across multiple browser windows. If your user opens a new window you may have conflicts between windows sharing the same session. That is fine and maybe even preferable if the data is being used to maintain a set of privileges but if the data contains a selection of a particular page, that will break your application.

Is there a typesafe way of navigating between screens in Windows Phone?

I'm looking for a way to navigate between screens in my app. Basically what I've seen so far consists of passing a string URI to the NavigationService, complete with query string parameters., e.g.
NavigationService.Navigate(new Uri("/MainPage.xaml?selectedItem=" +bookName.Id, UriKind.Relative));
I'm not really keen on this though ultimately because it requires magic strings, and they can lead to problems down the road.
Ideally I'd just create an instance of the class I want to navigate to, passing the parameters as arguments to the constructor. Is this possible? If so, how?
While the actual navigation will have to use strings eventually, you can create or use a wrapper that is type safe.
I would suggest looking at Caliburn Micro even if you only used it for the type safe navigation. Here is a snippet from a tutorial on using it in WP8:
The NavigationService that comes with the toolkit supports a view model first approach: instead of declaring which is the URL of the page where we want to take the user (that is the standard approach), we declare which is the ViewModel we want to display. The service will take care of creating the correct URL and display the view that is associated with the view model.
Alternatively you could look at Windows Phone MVC which also has some type safe navigation. You might even just be able to pull the navigation code out to use on your own since it's licensed under MS-PL.
Basically, no, not that's built-in. Complex parameters like IRepository instances are, unfortunately, beyond the ability of the navigation facilities in Silverlight; I usually use some form of IoC container to handle those. Simpler POCO parameters are easily serialized to a string, but that still requires magic strings and manual query-string parsing.
You can, however, easily build something typesafe yourself. For example, here's my approach.
For parameter data, I have a class that I call 'Extras', which wraps a Dictionary<string, object> with methods like GetBool(string), GetInt32(string), etc., and has a static factory method CreateFromUri(Uri); this is good enough for my purposes.
I use this in conjunction with type-safe navigation. I really like the MVVM pattern, and each of my pages has a ViewModel encapsulating nearly all logic. The one-to-one relationship of page to ViewModel makes the latter an ideal navigation key. That, combined with attributes and reflection, gives us a simple solution:
public class NavigationTargetAttribute : Attribute
{
private readonly Type target;
public ViewModelBase Target
{
get { return target; }
}
public NavigationTargetAttribute(Type target)
{
this.target = target;
}
}
Put one of these on each of your pages, with the proper ViewModel type.
[NavigationTarget(typeof(LoginViewModel))]
public class LoginPage : PhoneApplicationPage
{ ... }
Then, in a singleton NavigationManager-esque class, you can do:
GetType().Assembly
.GetTypes()
.Select(t => new { Type = t, Attr = t.GetCustomAttributes(false).FirstOrDefault(attr => attr is NavigationTargetAttribute) })
.Where(t => t.Attr != null);
And just like that, you have a collection of every navigable type in your app. From there, it's not much more work to put them in a dictionary, for example. If you follow a convention for where you put your pages, you can (for example) translate between type and Uri quite easily... for example, new Uri("/Pages/" + myPageType.Name + ".xaml", UriKind.Relative). It's not much more to add support for query parameters. Finally, you'll end up with a method, like so:
public void Navigate(Type target, Extras extras)
{
Type pageType;
if (navigationTargets.TryGetValue(target, out pageType))
{
var uri = CreateUri(pageType, extras);
navigationService.NavigateTo(uri);
}
// error handling here
}
Finally, in the page's OnNavigatedTo method, I do something like:
var extras = Extras.CreateFromUri(e.Uri);
((ViewModelBase) DataContext).OnNavigatedTo(extras);
This, finally, gives a semblance of strongly-typed navigation. This is a bare-bones approach; off the top of my head, this could be improved by adding required parameters in the navigation attribute and validating them at navigation-time. It also doesn't support more complex types of navigation, where the value of nav arguments would determine the ultimate destination. Nevertheless, this suits my 90% use case - maybe it will work for you, too.
There are definitely some details omitted here, like how exactly to get an instance of NavigationService - I can work up a more complete sample later tonight, but this should be enough to get started.
You can use PhoneApplicationService.State
It is a Dictionary<String,Object>
PhoneApplicationService.State is commonly used in tombstoning to store the current state of the application. However, it can be used to conveniently pass data between pages.
MSDN documentation
Windows Phone applications are deactivated when the user navigates to
another application. When the user returns to the application, by
using the Back button or by completing a Launcher or Chooser task, the
application is reactivated. An application can store transient
application state in the State dictionary in the handler for the
Deactivated event. In the Activated event handler, an application can
use the values stored in the State dictionary to transient application
state.
Basically what you would do is
PhoneApplicationService.State.add(selectedName,yourobjectInstance);
NavigationService.Navigate((new Uri("/MainPage.xaml?selectedItem="+selectedName,UriKind.Relative));
Then in your navigated too method you can retrieve it
YourObject yourObjectInstance;
var yourObj = PhoneApplicationService.State["yourObjectName"];
yourObjectInstance = yourObj is YourObject ? (yourObj as YourObject) : null;
Here is a more indepth look into how to use this feature
WPF supports navigating to an already created object, but WP8 lacks that Navigate overload.
If you don't want to hard-code XAML page URIs, you can you can use the following (a bit dirty) helper function to get the .xaml resource URI of some class.
static Uri GetComponentUri<T>() where T : DependencyObject, new() {
return BaseUriHelper.GetBaseUri(new T());
}
Then you can modify that URL and navigate to it:
var baseUri = GetComponentUri<SomePage>(); //Uri="pack://application:,,,/MyProject;component/MainWindow.xaml"
var pageUri = new UriBuilder(baseUri) { Query = "selectedItem=" + bookName.Id };
NavigationService.Navigate(pageUri);
Our solution that works just fine:
1. Do not use query strings in Page Uris, this is just completely agains MVVM where view should just display stuff, but the actual logic for loading and selecting items is in ViewModel.
2. Create class with const Page names and whenever you want to navigate, just use this:
public static class P
{
public const string ArticlePage = "/Pages/ArticlePage.xaml";
public const string OnlineSectionPage = "/Pages/OnlineSectionPage.xaml";
public const string GalleryPage = "/Pages/GalleryPage.xaml";
...
}
// in our viewModel
NavigationService.Navigate(P.ArticlePage);
// In navigation service
public void Navigate(string pagePath)
{
if (EnsureMainFrame())
{
mainFrame.Navigate(new Uri(pagePath, UriKind.RelativeOrAbsolute));
}
}

ASP.NET Control visibility dilemma

I've got a Page, a GridView using an ObjectDataSource with a SelectMethod and a DropDownList. The SelectMethod, among other things, gets a string-array containing several IDs (to filter the Data) - but I also need it as DataSource for the DropDownList.
Alas, I cannot DataBind the DropDownList inside the SelectMethod since it's null.
An Idea would be to bind this string[] to a Session-Variable, but then I'd have to either re-set it upon every Page_Load or remove it from Session on every other page if I want it to update in case something on the Database changed.
What I'm looking for is some kind of variable that is available both in Page_Load and my ObjectDataSources SelectMethod, but that removes itself upon leaving the page (i.e. navigating to any other page on my Web-Application (preferably without having to call a method on EVERY other Page).
I hope you could understand my problem.
Thanks,
Dennis
As I understand the need to fetch the string array arises from the performance hit that a separate roundtrip will cause. To work around this you may create a separate object to feed your object data source. This object will have two methods one for getting the string array and another for getting the data for the grid (i.e. the select method)
You may then put an object like this in your page and fetch the data in it in a lazy manner. If the object makes a call for any of the data it stores the other part in a field. You can then use the ObjectDataSource ObjectCreating event to pass this object on your page to the ObjectDataSource
protected void odsSomething_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
e.ObjectInstance = YourInsntanceAlreadyInThePage;
}
This way you will avoid the roundtrip.
Also consider making two web service calls at the same time using the asynchronous client calls so that you can make both calls for the same time. If this is viable depends on the flow of your logic.
What I'm looking for is some kind of variable that is available both in Page_Load and my ObjectDataSource's SelectMethod, but that removes itself upon leaving the page (i.e. navigating to any other page on my Web-Application (preferably without having to call a method on EVERY other Page).
In a similar situation, I've used the Items property of the current HttpContext. It's an IDictionary (non-generic), so can hold arbitrary objects keyed by arbitrary objects, and its lifetime is precisely the duration of the current request, so will go away as soon as the request is ended. To use:
// Where you first get the data
HttpContext.Current.Items["SomeKey"] = new [] { "string1", "string2" };
// Where you want to to use the data
var strings = (string[])HttpContext.Current.Items["SomeKey"];

Ways to share common functions betweed few pages in ASP.NET

This is possibly very lame question and I lack knowledge about ASP.Net. In this case a link to an article explaining would be very welcome.
I'm working on web-site on ASP.NET with C# as codebehind. My current project involves developing few pages with very similar functionality and a many functions are the same. For example
private void PermissionCheck()
{
if (null == Session["UserID"] ||
null == Session["ProjectID"] ||
null == Session["AssetID"] ||
null == Session["ProjectName"])
{
Response.Redirect("~/Login.aspx");
}
}
Would be the same on 2 pages. Some other things are the same as well. I would like to put this into common base class. But there are other functions that don't really belong to pages at all:
private string GetAttachmentTempPath()
{
return Request.PhysicalApplicationPath + WebConfigurationManager.AppSettings.Get("AttachmentsTempFolder");
}
I would like to move this into Attachment class, but to get the physical path of the application, I need to pass in Request object into that method, which is not really nice, as it couples Attachment class with Page.Request object.
Is there any way to move these functions somewhere else without needing to pass Page.Request objects around??
p.s. The appliction is huge, and there is no scope to change the entire architecture.
For your permission thing you could make a base page class:
class BasePage : Page
{
...
protected override OnInit() {
// do check here
}
}
Now you can implement your page like this class MyOtherPage : BasePage { ... }
The OnInit gets executed everytime your MyOtherPage gets loaded.
You can see a complete page lifecycle here: Link
For your other problem: Consider to implement a global available static tool class
Update
A good approach for making things like web.config easier to access is a Singleton. In asp.net a singleton is only created once and lives until the asp worker process gets stopped . So this values are shared across the whole asp.net application. This is also good for storing data in a global context that you dont want to get out of your database or file anytime a user makes a page request (for example a version number or things like that)
Update 2
To access the request without passing it to every function, use this:
HttpContext.Current.Request
Base page is a good option for reusable code in Page level. Other than that for things like configuration values it's good to come up with utility classes specifically for those methods base don there type of operations.
If you need to avoid passing in Page object into these types of utility methods,
HttpContext
class would be handy because you can access many of the ASP.Net object through static members through this class. HttpConext # MSDN
If you have similar functions behind the page, you can use ASP.NET WEb User Control.
You can create the functions in the usercontrol and use the control in the pages where necessary.
Have look at this article about Web User Control in Asp.Net

How to access the current [web] page (outside of the current page)?

The situation is this:
I have an abstract class used globally that used to reference a Session variable via a public property. We now want to change its behavior to return a property on a master page.
(By simply changing the guts of this particular property, we hope to avoid doing a lot of rewrites)
This is just a snippet of the class:
public abstract class AppSession
{
public static CaseNumber CurrentCaseNo
{
/* OLD METHOD DELETED */
get
{
if (CurrentPage.Master != null)
// property on the master page
return CurrentPage.Master.CurrentCaseNo;
else
throw new Exception("This page has no master page");
}
}
}
Above, "CurrentPage" is not real/valid. I just wrote that there to show context.
Is this even possible?
Thanks!
J
Look at the HttpContext.Current object. I believe it's Handler property will return the currently executing page. It would be easier to read a value stored in the Session that pulling it out of a property since the Session is available off of HttpContext.Current.
Building on David's answer, this can be used statically throughout your application:
Page myPage = System.Web.HttpContext.Current.CurrentHandler as Page;
if( myPage != null )
return ((MyMaster)myPage.Master).CurrentCaseNo;
I think that you would need to work with something that took a "page" object in as a parameter, and from there, you could determine if the page that was passed is your master page. And do what you need from there....
But that adds quite a bit of overhead.
The real question here is what are you trying to avoid? Trying to get rid of session and move to viewstate?

Categories