I was reading a post at VS 2008, ASP.NET: Generate Local Resources.
Mehdi Golchin showed us a beautiful job of StateManagedCollection.
However I was wondered about using multiple classes of IStateManager in one StateManagedCollection.
As you can see below:
public class MenuItemCollection : StateManagedCollection
{
public MenuItem this[int index]
{
get { return (MenuItem)((IList)this)[index]; }
}
public int Add(MenuItem item)
{
return ((IList)this).Add(item);
}
public void Remove(MenuItem item)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((MenuItem)o).SetDirty();
}
}
This MenuItemCollection class can have only one child class("MenuItem").
If I want to use another class as well as MenuItem class, for example MenuItem2 class, how do I have to write the codes?
Anyone can help me?
Thanks in advance.
Write a generic version - for example,
public class GenericStateManagedCollection<T> : StateManagedCollection
where T: IStateManager, new()
{
public T this[int index]
{
get { return (T)((IList)this)[index]; }
}
public int Add(T item)
{
return ((IList)this).Add(item);
}
public void Remove(T item)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((T)o).SetDirty();
}
protected override object CreateKnownType(int index)
{
return Activator.CreateInstance<T>();
}
protected override Type[] GetKnownTypes()
{
return new Type[] { typeof(T) };
}
}
And use it as
public class MenuItemCollection : GenericStateManagedCollection<MenuItem> { }
public class XyzItemCollection : GenericStateManagedCollection<XyzItem> { }
EDIT:
I have most probably mis-understood your question! Assuming now that you want to put two different type of objects into the StateManagedCollection. From usage perspective, it doesn't make sense to have objects of completely unrelated types into the collection - you need to have some base class. For example, consider DataControlFieldCollection which holds instances of (abstract) type 'DataControField. BoundField, ButtonField etc inherits fromDataControField`.
So you need to go via similar route - for example,
public class MenuItemBase : IStateManager
{
// Use implementation from link you quoted (Mehdi Golchin's answer)
...
}
public class MenuItem : MenuItemBase
{
...
}
public class MenuItem2 : MenuItemBase
{
...
}
public class MenuItemCollection : StateManagedCollection
{
public MenuItemBase this[int index]
{
get { return (MenuItemBase)((IList)this)[index]; }
}
public int Add(MenuItemBaseitem)
{
return ((IList)this).Add(item);
}
public void Remove(MenuItemBaseitem)
{
((IList)this).Remove(item);
}
// Write Insert and RemoveAt methods
protected override void SetDirtyObject(object o)
{
((MenuItemBase)o).SetDirty();
}
// important to override CreateKnownType and GetKnownTypes
private static readonly Type[] _knownTypes = new Type[] {typeof(MenuItem), typeof(MenuItem2) }
protected override Type[] GetKnownTypes()
{
return _knownTypes;
}
protected override object CreateKnownType(int index)
{
switch (index)
{
case 0:
return new MenuItem();
case 1:
return new MenuItem2();
default:
throw new Exception("Invalid Index");
}
}
}
Note: Untested code
Related
I was following this example
example code:
public class Global : HttpApplication
{
private Poster _posterDetails;
private Posting _postingDetails;
private Property _propertyDetails;
protected void Application_PostRequestHandlerExecute(object sender, EventArgs e)
{
if (HttpContext.Current.Session == null) return;
_posterDetails = HttpContext.Current.Session["Poster"] as Poster;
_postingDetails = HttpContext.Current.Session["Posting"] as Posting;
_propertyDetails = HttpContext.Current.Session["Property"] as Property;
}
}
these session variables are littered throughout the app and I need to abstract the retrieval of them. Say, later I get them from a db instead of the current session.
Session is baked into the Page or Context. How do I inject that dependency into the concrete implementation of a possible current property getter.
Create an abstraction around HttpContext:
public interface IHttpContextFactory
{
HttpContextBase Create();
}
public class HttpContextFactory
: IHttpContextFactory
{
public HttpContextBase Create()
{
return new HttpContextWrapper(HttpContext.Current);
}
}
Then inject it into a specialized service for these settings.
public interface ISettings
{
T GetValue<T>(string key);
void SetValue<T>(string key, T value);
}
public class ContextSettings
: ISettings
{
private readonly IHttpContextFactory httpContextFactory;
private HttpContextBase context;
public RequestCache(
IHttpContextFactory httpContextFactory
)
{
if (httpContextFactory == null)
throw new ArgumentNullException("httpContextFactory");
this.httpContextFactory = httpContextFactory;
}
protected HttpContextBase Context
{
get
{
if (this.context == null)
{
this.context = this.httpContextFactory.Create();
}
return context;
}
}
public virtual T GetValue<T>(string key)
{
if (this.Context.Session.Contains(key))
{
return (T)this.Context.Session[key];
}
return default(T);
}
public virtual void SetValue<T>(string key, T value)
{
this.Context.Session[key] = value;
}
}
It will later be possible to replace the service with another storage mechanism by implementing ISettings and providing different constructor dependencies. Note that changing the constructor signature does not require a different interface.
That said, you should provide another service (or perhaps more than one) that takes ISettings as a dependency so you can make explicit properties. You should aim to provide focused sets of related properties for specific purposes. Your application also shouldn't have to know the type of property in order to retrieve its value - it should just call a property that hides those details.
public class SomeSettingsService: ISomeSettingsService
{
private readonly ISettings settings;
public SomeSettingsService(ISettings settings)
{
if (settings == null)
throw new ArgumentNullException("settings");
this.settings = settings;
}
public Poster Poster
{
get { return this.settings.GetValue<Poster>("Poster"); }
set { this.settings.SetValue<Poster>("Poster", value); }
}
public Posting Posting
{
get { return this.settings.GetValue<Posting>("Posting"); }
set { this.settings.SetValue<Posting>("Posting", value); }
}
public Property Property
{
get { return this.settings.GetValue<Property>("Property"); }
set { this.settings.SetValue<Property>("Property", value); }
}
}
Not sure if this is what you are asking... What I often do is create a service:
public interface ISessionService
{
object Get(string key);
void Save(string key, object value);
}
And then I implement this, which calls HttpContext.Current.Session[key] and returns the value. It shouldn't be hard to create a Get<T>(string key) to return an object either. Break all of your dependencies to use this (which is the hard part).
There is no seamless way to break the dependency... it has to be through a manual change.
I want to create a custom server control which looks like this:
<cc:MyControl prop1="a" prop2="b">
<cc:MyItem name="xxx">
<cc:MyItem name="yyy">
<cc:MyItem name="zzz">
</cc:MyControl>
MyControl is of course implemented as a server control, however I do not want MyItem to be child controls. Rather they should exist as simple .Net objects. I have a class called MyItem, and the control has a property called Items, and when MyItem elements are declared in the markup, the objects should be instantiated and added to the collection.
The tutorials on MSDN don't actually explain how this happens. See: http://msdn.microsoft.com/en-us/library/9txe1d4x.aspx
I'd like to know:
How is <cc:MyItem> mapped to the MyItem class? Does the element in the markup have to have the same name as the object's class?
Which constructor of MyItem is called when MyItems are added declaratively, and when?
What collection types am I permitted to use to hold MyItem objects? The link above uses ArrayList, but can I use the strongly typed List instead?
Is it possible for a control to contain multiple collections?
It is so common to use class name for markup, but you can assign another name if you want, I do not explain more, if you want please comment
when asp.net compiles markup, it uses default parameter less constructor
you can use any collection type but if you want to use benefits of viewstate your collection type must implement IStateManager interface (below I wrote source of collection that I created for my self with state managing support)
Yes, your control can have multiple collections, just add required attributes as below:
(I used one of my codes, please replace names with your desired name)
if you want to have collection first of all you must define its property in your control.
imagine we have a control named CustomControl that extends Control as below:
[System.Web.UI.ParseChildrenAttribute(true)]
[System.Web.UI.PersistChildrenAttribute(false)]
public class CustomControl : Control{
private GraphCollection m_graphs;
[Bindable(false)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public GraphCollection Graphs
{
get
{
if (this.m_graphs == null) {
this.m_graphs = new GraphCollection();
if (base.IsTrackingViewState) {
this.m_graphs.TrackViewState();
}
}
return this.m_graphs;
}
}
}
as you can see in above code, CustomControl has a field with name "m_graphs" with type of "GraphCollection", also a property that exposes this field
also please please pay attention to its attribute PersistenceMode that says to asp.net property "Graphs" must persisted as InnerProperty
also please pay attention to two attributes applied to CustomControl class
attribute ParseChildrenAttribute says to asp.net that nested markup, must be treated as properties and attribute PersistChildrenAttribute says to asp.net that nested markups are not control's children
at the final, I bring two source codes for state managing components
first of all GraphCollection that extends from StateManagedCollection (both classes was written by me)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
namespace Farayan.Web.Core
{
public class StateManagedCollection<T> : IList, ICollection, IEnumerable, IEnumerable<T>, IStateManager
where T : class, IStateManager, new()
{
// Fields
private List<T> listItems = new List<T>();
private bool marked = false;
private bool saveAll = false;
// Methods
public void Add(T item)
{
this.listItems.Add(item);
if (this.marked) {
//item.Dirty = true;
}
}
public void AddRange(T[] items)
{
if (items == null) {
throw new ArgumentNullException("items");
}
foreach (T item in items) {
this.Add(item);
}
}
public void Clear()
{
this.listItems.Clear();
if (this.marked) {
this.saveAll = true;
}
}
public bool Contains(T item)
{
return this.listItems.Contains(item);
}
public void CopyTo(Array array, int index)
{
this.listItems.CopyTo(array.Cast<T>().ToArray(), index);
}
public IEnumerator GetEnumerator()
{
return this.listItems.GetEnumerator();
}
public int IndexOf(T item)
{
return this.listItems.IndexOf(item);
}
public void Insert(int index, T item)
{
this.listItems.Insert(index, item);
if (this.marked) {
this.saveAll = true;
}
}
public void LoadViewState(object state)
{
object[] states = state as object[];
if (state == null || states.Length == 0)
return;
for (int i = 0; i < states.Length; i++) {
object itemState = states[i];
if (i < Count) {
T day = (T)listItems[i];
((IStateManager)day).LoadViewState(itemState);
} else {
T day = new T();
((IStateManager)day).LoadViewState(itemState);
listItems.Add(day);
}
}
}
public void Remove(T item)
{
int index = this.IndexOf(item);
if (index >= 0)
this.RemoveAt(index);
}
public void RemoveAt(int index)
{
this.listItems.RemoveAt(index);
if (this.marked) {
this.saveAll = true;
}
}
public object SaveViewState()
{
List<object> state = new List<object>(Count);
foreach (T day in listItems)
state.Add(((IStateManager)day).SaveViewState());
return state.ToArray();
}
int IList.Add(object item)
{
T item2 = (T)item;
this.listItems.Add(item2);
return listItems.Count - 1;
}
bool IList.Contains(object item)
{
return this.Contains((T)item);
}
int IList.IndexOf(object item)
{
return this.IndexOf((T)item);
}
void IList.Insert(int index, object item)
{
this.Insert(index, (T)item);
}
void IList.Remove(object item)
{
this.Remove((T)item);
}
void IStateManager.LoadViewState(object state)
{
this.LoadViewState(state);
}
object IStateManager.SaveViewState()
{
return this.SaveViewState();
}
void IStateManager.TrackViewState()
{
this.TrackViewState();
}
public void TrackViewState()
{
this.marked = true;
for (int i = 0; i < this.Count; i++) {
((IStateManager)this[i]).TrackViewState();
}
}
// Properties
public int Capacity
{
get
{
return this.listItems.Capacity;
}
set
{
this.listItems.Capacity = value;
}
}
public int Count
{
get
{
return this.listItems.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public bool IsSynchronized
{
get
{
return false;
}
}
public T this[int index]
{
get
{
return (T)this.listItems[index];
}
}
public object SyncRoot
{
get
{
return this;
}
}
bool IList.IsFixedSize
{
get
{
return false;
}
}
object IList.this[int index]
{
get
{
return this.listItems[index];
}
set
{
this.listItems[index] = (T)value;
}
}
bool IStateManager.IsTrackingViewState
{
get
{
return this.marked;
}
}
#region IEnumerable<T> Members
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return this.listItems.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
}
and GraphCollection
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Farayan.Web.Core;
namespace Farayan.Web.AmCharts
{
public class GraphCollection : StateManagedCollection<Graph>
{
}
}
and finally Graph in our example:
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Web.UI;
using System.ComponentModel;
using Farayan.Web.AmCharts;
using System.Collections.Generic;
using Farayan.Web.Controls;
using System.Runtime;
using Farayan.Web.Core;
namespace Farayan.Web.AmCharts
{
public class Graph : StateManager
{
#region Colorize Property
[Browsable(true)]
[Localizable(false)]
[PersistenceMode(PersistenceMode.Attribute)]
[DefaultValue(false)]
public virtual bool Colorize
{
get { return ViewState["Colorize"] == null ? false : (bool)ViewState["Colorize"]; }
set { ViewState["Colorize"] = value; }
}
#endregion
//==============================
public override void LoadViewState(object state)
{
base.LoadViewState(state);
}
public override object SaveViewState()
{
return base.SaveViewState();
}
}
}
you may noticed that Graph extends StateManager class
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Web.UI;
using Farayan.Web.AmCharts;
namespace Farayan.Web.AmCharts
{
public class StateManager : IStateManager
{
protected StateBag ViewState = new StateBag();
#region IStateManager Members
public virtual bool IsTrackingViewState
{
get { return true; }
}
public virtual void LoadViewState(object state)
{
if (state != null) {
ArrayList arrayList = (ArrayList)state;
for (int i = 0; i < arrayList.Count; i += 2) {
string value = ((IndexedString)arrayList[i]).Value;
object value2 = arrayList[i + 1];
ViewState.Add(value, value2);
}
}
}
public virtual object SaveViewState()
{
ArrayList arrayList = new ArrayList();
if (this.ViewState.Count != 0) {
IDictionaryEnumerator enumerator = this.ViewState.GetEnumerator();
while (enumerator.MoveNext()) {
StateItem stateItem = (StateItem)enumerator.Value;
//if (stateItem.IsDirty) {
if (arrayList == null) {
arrayList = new ArrayList();
}
arrayList.Add(new IndexedString((string)enumerator.Key));
arrayList.Add(stateItem.Value);
//}
}
}
return arrayList;
}
public virtual void TrackViewState()
{
}
#endregion
#region IStateManager Members
bool IStateManager.IsTrackingViewState
{
get { return this.IsTrackingViewState; }
}
void IStateManager.LoadViewState(object state)
{
this.LoadViewState(state);
}
object IStateManager.SaveViewState()
{
return this.SaveViewState();
}
void IStateManager.TrackViewState()
{
this.TrackViewState();
}
#endregion
}
}
I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.
Is there some kind of additional Interface necessary to pass the action to the embedded button-component?
Any help is very appreciated since I am stuck at this issue for some days now
MyClass:
public class MyClass extends UIPanel implements SystemEventListener
{
private UIForm form;
private HtmlCommandButton buttonOk;
public MyClass()
{
FacesContext context = getFacesContext();
UIViewRoot root = context.getViewRoot();
root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}
#Override
public void processEvent(SystemEvent event)
{
this.form = new UIForm();
this.buttonOk = new HtmlCommandButton();
this.buttonOk.setId("okButtonId");
this.buttonOk.setActionExpression(getAction());
this.buttonOk.setValue("OK");
this.form.getChildren().add(this.buttonOk);
getChildren().add(this.form);
}
private enum PropertyKeys
{
action, text, titel
}
public MethodExpression getAction()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}
public void setAction(MethodExpression actionExpression)
{
getStateHelper().put(PropertyKeys.action, actionExpression);
}
public String getText()
{
return (String) getStateHelper().eval(PropertyKeys.text);
}
public void setText(String text)
{
getStateHelper().put(PropertyKeys.text, text);
}
public String getTitel()
{
return (String) getStateHelper().eval(PropertyKeys.titel);
}
public void setTitel(String titel)
{
getStateHelper().put(PropertyKeys.titel, titel);
}
#Override
public void encodeAll(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.DIV_ELEM, this);
writer.writeText(getText(), null);
this.form.encodeAll(context);
writer.endElement(HTML.DIV_ELEM);
}
#Override
public void encodeChildren(FacesContext context) throws IOException
{
}
#Override
public boolean isListenerForSource(Object source)
{
return (source instanceof MyClass);
}
}
MyClassHandler:
public class MyClassHandler extends ComponentHandler
{
public MyClassHandler(ComponentConfig config)
{
super(config);
}
#SuppressWarnings("rawtypes")
#Override
protected MetaRuleset createMetaRuleset(Class type)
{
return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}
}
myView Method:
...
public String myMethod()
{
System.err.println("myMethod");
return "/some/path/yadayada.xhtml";
}
...
MyView.xhtml
<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />
Exdending UICommand is enough, since you only want one action to be executed.
You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.
public void decode(FacesContext contex)
{
Map<String,String> map = context.getExternalContext.getRequestParameterMap();
// your rendered buttons need a name you check for
final boolean okPressed = map.containsKey( getClientId + ":ok" );
final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
if(okPressed || cancelPressed)
{
MethodExpression exp = null;
if(okPressed)
{
exp = getActionOk();
}
else
{
exp = getActionCancel();
}
// redirect to standard action
setActionExpression(exp);
queueEvent(new ActionEvent(this));
}
}
In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.
I use a session wrapper like this:
public interface ISessionWrapper
{
// ...
CultureInfo Culture { get; set; }
}
public class SessionWrapper: ISessionWrapper
{
private T GetFromSession<T>(string key)
{
return (T)HttpContext.Current.Session[key];
}
private void SetInSession(string key, object value)
{
HttpContext.Current.Session[key] = value;
}
// ...
public CultureInfo Culture
{
get { return GetFromSession<CultureInfo>("Culture"); }
set { SetInSession("Culture", value); }
}
}
I can use this interface in my controller like this:
private readonly ISessionWrapper sessionWrapper = new SessionWrapper();
// ...
ci = new CultureInfo(langName);
sessionWrapper.Culture = ci;
But how can I access this wrapper in the view below to replace the (direct call to) session variable?
#switch (Session["Culture"].ToString())
{
case "fr":
// ...
case "uk":
// ...
}
You could make use of the base view:
public abstract class BaseViewPage : WebViewPage
{
public virtual ISessionWrapper SessionWrapper
{
get
{
return new SessionWrapper();
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual ISessionWrapper SessionWrapper
{
get
{
return new SessionWrapper();
}
}
}
your views will have access to SessionWrapper property.
Make sure to add pageBaseType="SessionWrapper" attribute to pages tag in web.config.
I want to update a log file(txt) everytime when methods in a an interface class are called?
Is there any way to do this other than writing code in every method to create log?
Here's my 30 mins. you'll have to implement the logging code somewhere so you have to create another abstraction for your code. thus an abstract class is needed. i think. this is very quick and dirty.
public interface IService<T>
{
List<T> GetAll();
bool Add(T obj);
}
then you'll need the abstract class where you'll need to implement your logging routine
public abstract class Service<T> : IService<T>
{
private void log()
{
/// TODO : do log routine here
}
public bool Add(T obj)
{
try
{
log();
return AddWithLogging(obj);
}
finally
{
log();
}
}
public List<T> GetAll()
{
try
{
log();
return GetAllWithLog();
}
finally
{
log();
}
}
protected abstract List<T> GetAllWithLog();
protected abstract bool AddWithLogging(T obj);
}
as for your concrete classes
public class EmployeeService : Service<Employee>
{
protected override List<Employee> GetAllWithLog()
{
return new List<Employee>() { new Employee() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Employee obj)
{
/// TODO : do add logic here
return true;
}
}
public class CompanyService : Service<Company>
{
protected override List<Company> GetAllWithLog()
{
return new List<Company>() { new Company() { Id = 0, Name = "test" } };
}
protected override bool AddWithLogging(Company obj)
{
/// TODO : do add logic here
return true;
}
}
public class Employee
{
public int Id {get;set;}
public string Name { get; set;}
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
then on your implementation you can just..
static void Main(string[] args)
{
IService<Employee> employee = new EmployeeService();
List<Employee> employees = employee.GetAll();
foreach (var item in employees)
{
Console.WriteLine(item.Name);
}
IService<Company> company = new CompanyService();
List<Company> companies = company.GetAll();
foreach (var item in companies)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
hope this helps!
I think you would have to use Aspect Oriented Programming to achieve that. Read http://www.sharpcrafters.com/aop.net
I think you meant class (instead of interface)
Two options I can think of:
Implementing INotifyPropertyChanged which is in lines of writing code in every method
or
to adopt on of the AOP frameworks in the article http://www.codeproject.com/KB/cs/AOP_Frameworks_Rating.aspx if that is not a major leap