How to access on of the Control in Page from the UserControl? - asp.net

How do I access the Control (dropdownlist) in current Page from the UserControl?
In the UserControl:
String test = ((DropDownList)this.Parent.FindControl("drpdwnlstMainRegion")).SelectedValue;
or
String test = ((DropDownList)this.Page.FindControl("drpdwnlstMainRegion")).SelectedValue;
It return null on ((DropDownList)this.Parent.FindControl("drpdwnlstMainRegion")) for some reason?!?!
BTW ... I am using ASP.NET C# 3.5.
Thanks

Depending upon the structure of your page and the nesting of the controls, you may have to recursively crawl through all of the controls. Something like the following may be helpful: http://stevesmithblog.com/blog/recursive-findcontrol/

Compile these extension methods into your assembly:
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
public static class ControlExtensions
{
/// <summary>
/// Recurses through a control tree and returns an IEnumerable<Control>
/// containing all Controls from the control tree
/// </summary>
/// <returns>an IEnumerable<Control></returns>
public static IEnumerable<Control> FindAllControls(this Control control)
{
yield return control;
foreach (Control child in control.Controls)
foreach (Control all in child.FindAllControls())
yield return all;
}
/// <summary>
/// Recurses through a control tree and finds a control with
/// the ID specified
/// </summary>
/// <param name="control">The current object</param>
/// <param name="id">The ID of the control to locate</param>
/// <returns>A control of null if more than one control is found with a matching ID</returns>
public static Control FindControlRecursive(this Control control, string id)
{
var controls = from c in control.FindAllControls()
where c.ID == id
select c;
if (controls.Count() == 1)
return controls.First();
return null;
}
}
And then use like this:
Control whatYoureLookingFor = Page.Master.FindControlRecursive("theIdYouAreLookingFor");
This is a duplicate of a couple of questions already on SO but I couldn't find them.

Related

ViewModel instance will always be recreated when switching between pages

I'm using Prism 6, UWP with Unity.
The ViewModels will be automatically injected into the datacontext of the page. However, when I navigate between the pages the viewmodels will always be recreated. Is this behaviour desired by Prism and Unity?
Imagine the following scenario, a user enter some data into a page therefore the proper properties of the viewmodel will be set. When the user is switching back to another page and revisits the page all entered data are lost, because a new viewmodel instance is created.
At the moment my workaround is to override OnNavigatedTo and OnNavigatingFrom to save all properties of the viewmodel with the SessionStateService manual. I'm not sure if this is the correct way?
You can reproduce this behaviour with the following example:
https://github.com/PrismLibrary/Prism-Samples-Windows/tree/master/SplitViewSample/SplitViewSample
I am not using Prism, I'm using modified version of Template 10.
I just had a quick look at the Prism source code. Looks like Template 10 borrowed a lot of ideas from Prism.
I'll try to answer your question from 2 perspectives:
1) AFAIK, in Prism there is a static class with which you can set how to create/resolve you view model when it is automatically looked up for the corresponding View. The class is ViewModelLocationProvider, in file ViewModelLocationProvider.cs you can use following methods to setup the 'view model factories'
/// <summary>
/// Sets the default view model factory.
/// </summary>
/// <param name="viewModelFactory">The view model factory which provides the ViewModel type as a parameter.</param>
public static void SetDefaultViewModelFactory(Func<Type, object> viewModelFactory)
{
_defaultViewModelFactory = viewModelFactory;
}
/// <summary>
/// Sets the default view model factory.
/// </summary>
/// <param name="viewModelFactory">The view model factory that provides the View instance and ViewModel type as parameters.</param>
public static void SetDefaultViewModelFactory(Func<object, Type, object> viewModelFactory)
{
_defaultViewModelFactoryWithViewParameter = viewModelFactory;
}
/// <summary>
/// Registers the view model factory for the specified view type name.
/// </summary>
/// <param name="viewTypeName">The name of the view type.</param>
/// <param name="factory">The viewmodel factory.</param>
public static void Register(string viewTypeName, Func<object> factory)
{
_factories[viewTypeName] = factory;
}
then all the logic for getting view model instance is in the following, pay attention to the comment summary here, it describes the logic/strategy
/// <summary>
/// Automatically looks up the viewmodel that corresponds to the current view, using two strategies:
/// It first looks to see if there is a mapping registered for that view, if not it will fallback to the convention based approach.
/// </summary>
/// <param name="view">The dependency object, typically a view.</param>
/// <param name="setDataContextCallback">The call back to use to create the binding between the View and ViewModel</param>
public static void AutoWireViewModelChanged(object view, Action<object, object> setDataContextCallback)
{
// Try mappings first
object viewModel = GetViewModelForView(view);
// Fallback to convention based
if (viewModel == null)
{
var viewModelType = _defaultViewTypeToViewModelTypeResolver(view.GetType());
if (viewModelType == null)
return;
viewModel = _defaultViewModelFactoryWithViewParameter != null ? _defaultViewModelFactoryWithViewParameter(view, viewModelType) : _defaultViewModelFactory(viewModelType);
}
setDataContextCallback(view, viewModel);
}
on line 87 and 96 you get you view model instance for the corresponding view.
So that means, if you don't call any of those methods to setup the factories, it will fall back to the default factory which is
/// <summary>
/// The default view model factory whic provides the ViewModel type as a parameter.
/// </summary>
static Func<Type, object> _defaultViewModelFactory = type => Activator.CreateInstance(type);
that is pretty clear you will always get a new instance.
Regarding Unity, I didn't see anything special, the only clue is that in the PrismApplication class in PrismApplication.cs, it sets up the factory like following:
/// <summary>
/// Configures the <see cref="ViewModelLocator"/> used by Prism.
/// </summary>
protected virtual void ConfigureViewModelLocator()
{
ViewModelLocationProvider.SetDefaultViewModelFactory((type) => Resolve(type));
}
that means the factory is now using
/// <summary>
/// Resolves the specified type.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>A concrete instance of the specified type.</returns>
protected virtual object Resolve(Type type)
{
return Activator.CreateInstance(type);
}
which you can override with your own implementation.
In PrismUnityApplication class, PrismUnityApplication.cs, it offers a default implementation to resolve the instance with Unity
/// <summary>
/// Implements the Resolves method to be handled by the Unity Container.
/// Use the container to resolve types (e.g. ViewModels and Flyouts)
/// so their dependencies get injected
/// </summary>
/// <param name="type">The type.</param>
/// <returns>A concrete instance of the specified type.</returns>
protected override object Resolve(Type type)
{
return Container.Resolve(type);
}
and yeah, like other guys mentioned, you can control the lifetime of your view models yourself via Unity.
2) sorry for the long answer,
but I feel it's better to show you some code that will make things clear.
I'll keep the 2nd one short.
In my opinion, you don't need a view model when your view is gone.
I am not sure how the Frame stack is implemented in UWP and how they manage the view/page instances. I would assume once you navigate to a different page, the previous view/page should be released or can be released at GC, and you have the parameter and page type to be able to navigate back, but it will be a new instance and you restore the state of your view by restoring your view model.
so really, i think you're on the right track. and you should save/persist your user data whenever you can, and your solution works when the app is suspended and then is resumed, you can still recover the state of your view.
Thanks for reading.
It should be solved when you register your ViewModels as singletons (ContainerControlledLifetimeManager) in the UnityContainer. The best place for this would be the App.xaml.cs in method OnInitializeAsync
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterType<MyViewModel>(new ContainerControlledLifetimeManager());
// rest of the method
}

Chatjs: how to fetch friends list from sql database

I am using ChatJs in my asp.net webapplication with signalr adapter. How to show the online user from database on page load.As i am new to asp.net please guide me. thanks in advance
/// <summary>
/// This method is STUB. This will SIMULATE a database of users
/// </summary>
private static readonly List<DbUserStub> dbUsersStub = new List<DbUserStub>();
// <summary>
/// This method is STUB. In a normal situation, the user info would come from the database so this method wouldn't be necessary.
/// It's only necessary because this class is simulating the database
/// </summary>
/// <param name="newUser"></param>
public static void RegisterNewUser(DbUserStub newUser)
{
if (newUser == null) throw new ArgumentNullException("newUser");
dbUsersStub.Add(newUser);
}

Is ViewState safe for multiple users in web application

In my ASP.Net app i use viewstate to store data of a grid, i use common class for creating viewstate object, like shown below.
public static PageViewState CurrentViewState
{
get
{
if (_app == null)
{
Initialize();
}
return _app;
}
}
/// <summary>
/// Creates new object for singleton class
/// </summary>
private static void Initialize()
{
PageViewState _viewstate = new PageViewState();
_app = _viewstate;
}
/// <summary>
/// Returns viewstate for specified page name
/// </summary>
/// <param name="_page">string : Name of the page</param>
/// <returns></returns>
public object this[string _page]
{
get
{
if (ViewState[_page] != null)
return ViewState[_page];
else
return null;
}
set
{
ViewState[_page] = value;
}
}
I'm using a static property, is it safe when multiple users access this in aspx.cs.
Datatable _dtable = (Datatable)PageViewState.CurrentViewState["MyPage"];
ViewState stores data in client side in the form of hiddenfields so it should be unique for each user, am i right about this.
ViewState stores data in client side and this is safe.
Yes it is as it stores data on the client side i.e. the page user is viewing on his machine.
So you will be seeing the different version of same page(in the sense of viewstate data) and I will be seeing the different version.
ViewState is encrypted string representing the control state and it is at the page level.
This is rendered for each page with the hidden input field.
<input type="hidden" id="__ViewState" value="uxudhk.." />
Each page requested by a different user will have a different hidden field in a page. So this is safe always. In case if your ViewState is modified or altered you will get an Server side error like InValid ViewState.

Revalidate Model When Using WebAPI (TryValidateModel equivalent)

Using vanilla MVC I can revalidate my model with TryValidateModel. The TryValidateModel method doesn't seem to be applicable to WebAPI. How can I revalidate my model when using WebAPI?
I know it has been a while since this has been asked, but the problem is still valid. Thus i thought i should share my solution to this problem.
I decided to implement the TryValidateModel(object model) myself, based on the implementation in the System.Web.Mvc.Controller.cs
The problem is that the mvc's TryValidateModel internally used their own HttpContext and ModelState. If you go and compaire the two, they are very similar....
The be able to use our own HttpContext there exists a HttpContextWrapper that can be used for that.
And Since we have to clear our model state, it doesn't really matter that we use a different type of ModelState , as long as we get the desired result, thus i create a new ModelState object from the correct type...
I did add the error to the ModelState of the controller and not to the model state to the newly created ModelState , This seems to work just fine for me :)
Here is my code, that i just added to the controller...
do not forget to import the library...
using System.Web.ModelBinding;
protected internal bool TryValidateModel(object model)
{
return TryValidateModel(model, null /* prefix */);
}
protected internal bool TryValidateModel(object model, string prefix)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
var t = new ModelBindingExecutionContext(new HttpContextWrapper(HttpContext.Current), new System.Web.ModelBinding.ModelStateDictionary());
foreach (ModelValidationResult validationResult in ModelValidator.GetModelValidator(metadata, t).Validate(null))
{
ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
}
return ModelState.IsValid;
}
I don't know when was it added but now there is Validate method on api controller.
ApiController.Validate Method (TEntity)
https://msdn.microsoft.com/en-us/library/dn573258%28v=vs.118%29.aspx
Based from rik-vanmechelen original answer, here is my version that relies on the services container exposed by Web API.
/// <summary>
/// Tries to validate the model.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>Whether the model is valid or not.</returns>
protected internal bool TryValidateModel(object model)
{
if (model == null)
{
throw new ArgumentNullException("model");
}
var metadataProvider = Configuration.Services.GetService<System.Web.Http.Metadata.ModelMetadataProvider>();
var validatorProviders = Configuration.Services.GetServices<System.Web.Http.Validation.ModelValidatorProvider>();
var metadata = metadataProvider.GetMetadataForType(() => model, model.GetType());
ModelState.Clear();
var modelValidators = metadata.GetValidators(validatorProviders);
foreach (var validationResult in modelValidators.SelectMany(v => v.Validate(metadata, null)))
{
ModelState.AddModelError(validationResult.MemberName, validationResult.Message);
}
return ModelState.IsValid;
}
This uses the following simple extension methods to access the services :
/// <summary>
/// Services container extension methods.
/// </summary>
public static class ServicesContainerExtensions
{
/// <summary>
/// Gets the service.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="services">The services.</param>
/// <returns>The service.</returns>
/// <exception cref="System.ArgumentNullException">services</exception>
public static TService GetService<TService>(this ServicesContainer services)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
return (TService)((object)services.GetService(typeof(TService)));
}
/// <summary>
/// Gets the services.
/// </summary>
/// <typeparam name="TService">The type of the service.</typeparam>
/// <param name="services">The services.</param>
/// <returns>The services.</returns>
/// <exception cref="System.ArgumentNullException">services</exception>
public static IEnumerable<TService> GetServices<TService>(this ServicesContainer services)
{
if (services == null)
{
throw new ArgumentNullException("services");
}
return services.GetServices(typeof(TService)).Cast<TService>();
}
}
The advantage of using this method is that it reuses the MetadataProvider and ValidatorProvider(s) you have configured for your Web API application while the previous answer is retrieving the one configured in ASP.NET MVC.
ASP.NET MVC and WebAPI run through different pipelines.
Turns out TryValidateModel is not supported in WebAPI. There's a feature request over on CodePlex.

Custom PageHandlerFactory for .aspx

I am building a fairly simple CMS. I need to intercept requests for the majority of .aspx pages in my web application, in order to gain complete control over the output. In most cases, the output will be pulled from cache and will just be plain HTML.
However, there still are a couple of pages that I am going to need to use asp: controls on. I assume the best way for me to bypass a few particular requests would be to inherit System.Web.UI.PageHandlerFactory and just call the MyBase implementation when I need to (please correct me if I am wrong here). But how do I transfer all other requests to my custom handler?
When I wrote a simple CMS, I had a difficult time using the PageHandlerFactory to get it to do what I wanted. In the end I switched to a IHttpModule.
My module would first check to see if there was an .aspx file in the requested path. I'd only do that if the page has user controls on it or didn't fit into the CMS for some reason. So if the file existed, it would return out of the module. After that it would look at the requested path and condense it into a "navigation tag." Thus ~/aboutus/default.aspx would become page.aspx?nt=aboutusdefault. page.aspx would load the proper content form the CMS. Of course, the redirect occurs server-side so the users/spiders never know anything different happened.
using System;
using System.Data;
using System.Collections.Generic;
using System.Configuration;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
namespace MyCMS.Handlers {
/// <summary>
/// Checks to see if we should display a virutal page to replace the current request.
/// Code adapted from:
/// Rewrite.NET -- A URL Rewriting Engine for .NET
/// By Robert Chartier
/// http://www.15seconds.com/issue/030522.htm
/// </summary>
public class VirtualPageModule : IHttpModule {
/// <summary>
/// Init is required from the IHttpModule interface
/// </summary>
/// <param name="Appl"></param>
public void Init(System.Web.HttpApplication Appl) {
// make sure to wire up to BeginRequest
Appl.BeginRequest += new System.EventHandler(Rewrite_BeginRequest);
}
/// <summary>
/// Dispose is required from the IHttpModule interface
/// </summary>
public void Dispose() {
// make sure you clean up after yourself
}
/// <summary>
/// To handle the starting of the incoming request
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void Rewrite_BeginRequest(object sender, System.EventArgs args) {
// Cast the sender to an HttpApplication object
HttpApplication httpApp = (HttpApplication)sender;
// See if the requested file already exists
if (System.IO.File.Exists(httpApp.Request.PhysicalPath)) {
// Do nothing, process the request as usual
return;
}
string requestPath = VirtualPathUtility.ToAppRelative(httpApp.Request.Path);
// Organic navigation tag (~/aboutus/default.aspx = nt "aboutusdefault")
Regex regex = new Regex("[~/\\!##$%^&*()+=-]");
requestPath = regex.Replace(requestPath, string.Empty).Replace(".aspx", string.Empty);
string pageName = "~/page.aspx";
string destinationUrl = VirtualPathUtility.ToAbsolute(pageName) + "?nt=" + requestPath;
SendToNewUrl(destinationUrl, httpApp);
}
public void SendToNewUrl(string url, HttpApplication httpApp) {
applyTrailingSlashHack(httpApp);
httpApp.Context.RewritePath(
url,
false // RebaseClientPath must be false for ~/ to continue working in subdirectories.
);
}
/// <summary>
/// Applies the trailing slash hack. To circumvent an ASP.NET bug related to dynamically
/// generated virtual directories ending in a trailing slash (/).
/// As described by BuddyDvd:
/// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=105061
/// </summary>
/// <param name="httpApp">The HttpApplication.</param>
/// <remarks>
/// Execute this function before calling RewritePath.
/// </remarks>
private void applyTrailingSlashHack(HttpApplication httpApp) {
if (httpApp.Request.Url.AbsoluteUri.EndsWith("/") && !httpApp.Request.Url.AbsolutePath.Equals("/")) {
Type requestType = httpApp.Context.Request.GetType();
object clientFilePath = requestType.InvokeMember("ClientFilePath", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty, null, httpApp.Context.Request, null);
string virtualPathString = (string)clientFilePath.GetType().InvokeMember("_virtualPath", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField, null, clientFilePath, null);
clientFilePath.GetType().InvokeMember("_virtualPath", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField, null, clientFilePath, new object[] { virtualPathString });
requestType.InvokeMember("_clientFilePath", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField, null, HttpContext.Current.Request, new object[] { clientFilePath });
object clientBaseDir = requestType.InvokeMember("ClientBaseDir", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty, null, httpApp.Context.Request, null);
clientBaseDir.GetType().InvokeMember("_virtualPath", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField, null, clientBaseDir, new object[] { virtualPathString });
requestType.InvokeMember("_clientBaseDir", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.SetField, null, HttpContext.Current.Request, new object[] { clientBaseDir });
}
}
}
}
Do you mean that you are going to inject controls? If that is the case, you might want to consider a required base class instead of the Page class. Page implements IHttpHandler, so you can create a derived class and then change your pages to derive from your derived class. You will have much more control over your page and be able to hook into it and its rendering.

Resources