I have several custom user controls (100+) that all have a common formatting involved which basically requires us to wrap the user control in a panel control on the destination page. For simplicity, I wanted to create a base control that handles this, as almost all of the base features are the same for each of the custom controls.
What I have done to accomplish this is to create a base class, inside that base class I create a private panel control, and then I override the Render to generate the panel pre/post tags around the base.Render.
Now this works great as all of the user controls that we care about that are inheriting this and the few formatting items that we have exposed to the inherited controls work as expected (Width, CssClass, etc).
What I would really like is to expose all of the panel control items to the inherited control through the base class, but without having to right a property/method to expose each element.
Any ideas on what the best approach is for this? I just don't want to implement each and every panel property/method manually. We use the design time attributes as well (number one is CssClass and Width) but we have been entending the user of design time attributes...
What we have works, just looking for a easier/better solution.
using System;
using System.Collections;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public class BaseUserControl : System.Web.UI.UserControl
{
private Panel _panel;
private bool _isPanelLoaded;
public Panel Panel
{
get
{
if (_panel == null)
{
_panel = new Panel();
_isPanelLoaded = true;
}
return _panel;
}
}
public BaseUserControl()
{
}
protected override void Render(HtmlTextWriter writer)
{
if (_isPanelLoaded)
{
Panel.RenderBeginTag(writer);
base.Render(writer);
Panel.RenderEndTag(writer);
}
else
{
base.Render(writer);
}
}
public Unit Width
{
get
{
if (Panel.Width.IsEmpty)
{
return 0;
}
return Panel.Width;
}
set
{
Panel.Width = value;
}
}
public string CssClass
{
get
{
return Panel.CssClass;
}
set
{
Panel.CssClass = value;
}
}
}
In order to expose all the properties, you have to inherit from the Panel control. Since you have a panel property, you could set the properties on that property. To define them in markup is the challenge... with most simple objects, if you give the property an attribute of <PersistenceMode(PersistenceMode.Attribute)>, you would be able to access the properties in the sytnax of Panel-CssClass, but I don't know if that will work for you, since Panel is a control. Good to try.
You're essentially talking about writing properties once that wrap the attributes you want, so it may be best just to do that, if the former solution doesn't work.
Related
Problem: I currently have a class that takes in an object of type Control and does some work. I'm attempting to create a class that can take in either a Control, Button or a Label object. I can make this work however it would involve that I copy and paste the class two more times. One to work with Buttons and another to work with Labels. The logic and the members being called are exactly the same with the exception of the Type. I have simplified the concept I'm wishing to convey below:
// This class currently only does work on a Control object
public class takeControlType
{
public takeControlType(Control control)
{
string objectName = control.Name.ToString();
}
}
I could copy paste the code above and make it work by overloading the class Constructor like this:
public class takeAnyType
{
public takeAnyType(Control control)
{
string objectName = control.Name.ToString();
}
public takeAnyType(Button button)
{
string objectName = button.Name.ToString();
}
public takeAnyType(Label label)
{
string objectName = label.Name.ToString();
}
}
Am I correct in thinking that this just seems like a drag in productivity? I'm hoping I can reuse the same logic despite the Type being different as the only item that I would need to replace is the Type. The logic and properties being implemented in my class are exactly the same for Controls, Buttons and Labels. I've researched generics but due to the fact that I'm pulling back properties and methods specific to either a Control, Button or Label I can't seem to get generics to work with the object properties such as .Name or .Width or .Capture for example. The only methods the generic Type provides me with are
Equals()
GetHashCode()
GetType()
ToString()
I need access to a few of the properties I mentioned previously. How does one accomplish this in order that I might avoid having to copy/paste 266 lines of code that make up my class that currently is only able to work with Control objects?
Aside from attempting to make use of Generics I also tried to see if I could use base class type object as opposed to Control but that led me to the same issue I'm currently having with Generics. I no longer have access to the members that are associated with Controls, Buttons and Labels.
To clear up any confusion the example (non-working) code below is what I'm attempting to accomplish.
public class takeAnyType
{
public takeAnyType(anyType obj)
{
string objectName = obj.Name.ToString();
obj.Cursor = Cursors.SizeNESW;
obj.Capture = true;
obj.Width = 20;
obj.Top = 100;
}
}
Button and Label classes inherit from Control (indirectly). This means that if you only create a class for Control, you can still use it for objects of type Button or Label. You don't have to create special classes for those.
In C# (and OO languages in general), you can assign an instance of a derived class to a variable of a super class. For example, this is valid C# code:
Control control = new Button();
An answer addresses your example, but your problem seems to describe something more - doing some more convoluted login on Labels and Buttons. One way to do this is the following:
1) declare a base class to handle common issue (e.g. your name example)
2) declare a class for each Label and Button to handle specific logic
public class ControlHelper
{
public virtual String GetControlName(Control control)
{
return control.Name.ToString();
}
// it is not possible to do the logic on a generic control, so force derived classes to provide the logic
public abstract void DoSomeFancyStuffWithControl(Control control);
// other common functions may come here
}
public class LabelHelper : ControlHelper
{
// you may override virtual methods from ControlHelper. For GetControlName, it should not be the case
public override DoSomeFancyStuffWithControl(Control control)
{
var button = control as Label;
// ...
}
// does not have to be virtual, but allow further inheritance
public virtual String GetText(Label l)
{
return l.Text;
}
// other label specific methods come here
}
public class ButtonHelper : ControlHelper
{
public override DoSomeFancyStuffWithControl(Control control)
{
var button = control as Button;
// ...
}
public virtual bool GetEnabled(Button b)
{
return b.Enabled;
}
// other button specific functions may come here
}
I have an application that can display items in two different ways, in rows using a StackPanel or icons using a WrapPanel. Which way it displays these items depends on a config setting. To populate these panels I have two seperate classes one inherits from the WrapPanel the other from the StackPanel. I was able to cut down on duplicated code using an Inferface. However I still had a lot of duplicated code the only difference between the code is the references to StackPanel or WrapPanel.
What I would really like to do is create a class that inherits from either the StackPanel or WrapPanel depending on the config setting.
public class ContainerBase : <wrap or stack>
{
//Do stuff!
}
Is this possible? Am I approaching this incorrectly?
When I said "composition but not inheritance" in first comment, I meant smth like the following:
public class PanelPresentatinLogic
{
public Panel Panel{get;set;}
public void DoSomeDuplicatingStuff()
{
//Do stuff! with Panel
}
}
public class SortOfStackPanel : StackPanel
{
private readonly PanelPresentatinLogic _panelPresentatinLogic;
public SortOfStackPanel(PanelPresentatinLogic presentationLogic)
{
_panelPresentatinLogic = presentationLogic;
_panelPresentatinLogic.Panel = this;
}
public void DoSomeDuplicatingStuff()
{
_panelPresentatinLogic.DoSomeDuplicatingStuff();
}
}
public class SortOfWrapPanel : WrapPanel
{
private readonly PanelPresentatinLogic _panelPresentatinLogic;
public SortOfWrapPanel(PanelPresentatinLogic presentationLogic)
{
_panelPresentatinLogic = presentationLogic;
_panelPresentatinLogic.Panel = this;
}
public void DoSomeDuplicatingStuff()
{
_panelPresentatinLogic.DoSomeDuplicatingStuff();
}
}
public class UsageSample
{
public void PopulateCollectionOfItemsDependingOnConfigHopeYouveGotTheIdea()
{
string configValue = configuration["PanelKind"];
PanelPresentatinLogic panelPresentatinLogic = new PanelPresentatinLogic();
Panel panel = configValue == "Wrap"
? new SortOfWrapPanel(panelPresentatinLogic)
: new SortOfStackPanel(panelPresentatinLogic);
// TODO: add panel to GUI
}
}
You can use Generics for this;
public class ContainerBase<T> where T : Panel
You can then use the config setting to initialize the right type.
In WPF you are intended to not use controls to populate itself. Instead, use MVVM pattern.
You can achieve your goal with only one class providing the data (tipically an ObservableCollection) and loading that "view mode variable" into a property at MVVM.
Then you can use a an ItemsPanel with an DataTemplateSelector to select the view.
May be you can use Panel?
public class ContainerBase : <wrap or stack>
{
//Do stuff!
}
TextBox, Label, Panel, ... all inherits from Control.
Is there a way to inherit from Control and make ASP.NET controls inherit from my new control?
For example, I have a control
public class SpecialControl : Control
{
public string Something { get; set; }
}
Now I want all controls to inherit from it, so
<asp:TextBox ID="tb" runat="server" Something="hello" />
Would be valid.
You can't change the inheritance chain of the controls that are part of the BCL.
One thing you can do is to create an extension method as below.
<asp:TextBox id="tst" runat="server" Something="TestValue"></asp:TextBox>
public static string GetSomething(this WebControl value)
{
return value.Attributes["Something"].ToString();
}
I only tested this for a TextBox control. May not be the ideal solution as it is not strongly typed but will work.
As Oded mentioned, you can't modify the inheritance chain of packaged controls.
What you can do is create wrappers for the packaged controls, and implement custom properties and methods in the wrappers. Here's a simple example of how to extend the TextBox:
[DefaultProperty("Text")]
[ToolboxData("<{0}:CustomTextBox runat=server></{0}:CustomTextBox>")]
public class CustomTextBox: System.Web.UI.WebControls.TextBox
{
[Bindable(true)]
[DefaultValue("")]
public string Something
{
get
{
string something = (string)ViewState["Something"];
return (something == null) ? String.Empty : something ;
}
set
{
ViewState["Something"] = value;
}
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
}
I'm designing a simple expander control.
I've derived from UserControl, drawn inner controls, built, run; all ok.
Since an inner Control is a Panel, I'd like to use it as container at design time. Indeed I've used the attributes:
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
Great I say. But it isn't...
The result is that I can use it as container at design time but:
The added controls go back the inner controls already embedded in the user control
Even if I push to top a control added at design time, at runtime it is back again on controls embedded to the user control
I cannot restrict the container area at design time into a Panel area
What am I missing? Here is the code for completeness... why this snippet of code is not working?
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ExpanderControl : UserControl
{
public ExpanderControl()
{
InitializeComponent();
....
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class ExpanderControlDesigner : ControlDesigner
{
private ExpanderControl MyControl;
public override void Initialize(IComponent component)
{
base.Initialize(component);
MyControl = (ExpanderControl)component;
// Hook up events
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
s.SelectionChanged += new EventHandler(OnSelectionChanged);
c.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
}
private void OnSelectionChanged(object sender, System.EventArgs e)
{
}
private void OnComponentRemoving(object sender, ComponentEventArgs e)
{
}
protected override void Dispose(bool disposing)
{
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
// Unhook events
s.SelectionChanged -= new EventHandler(OnSelectionChanged);
c.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving);
base.Dispose(disposing);
}
public override System.ComponentModel.Design.DesignerVerbCollection Verbs
{
get
{
DesignerVerbCollection v = new DesignerVerbCollection();
v.Add(new DesignerVerb("&asd", new EventHandler(null)));
return v;
}
}
}
I've found many resources (Interaction, designed, limited area), but nothing was usefull for being operative...
Actually there is a trick, since System.Windows.Forms classes can be designed (as usual) and have a correct behavior at runtime (TabControl, for example).
ParentControlDesigner doesn't know what you want do. It only knows you want your UserControl to be a container.
What you need to do is implement your own designer which enables design mode on the panel:
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MyCtrlLib
{
// specify my custom designer
[Designer(typeof(MyCtrlLib.UserControlDesigner))]
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
// define a property called "DropZone"
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel DropZone
{
get { return panel1; }
}
}
// my designer
public class UserControlDesigner : ParentControlDesigner
{
public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
if (this.Control is UserControl1)
{
this.EnableDesignMode(
(UserControl1)this.Control).DropZone, "DropZone");
}
}
}
}
I learned this from Henry Minute on CodeProject. See the link for some improvements on the technique.
In addition to the answer above. It is mentioned in the comments, that the user is able to drag the WorkingArea. My fix for that is to include the WorkingArea panel in another panel, setting it to Dock.Fill. To disallow the user to change it back, I have created a class ContentPanel that overrides and hides the Dock property:
class ContentPanel : Panel
{
[Browsable(false)]
public override DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = DockStyle.Fill; }
}
}
For me, this makes it sufficiently safe. We are only using the control internally, so we mainly want to prevent developers from accidently dragging things around. There are certainly ways to mess it up anyway.
To prevent the working area from being moved/resized in the designer you have to create a class for that working area that hides the Location, Height, Width, Size properties from the designer:
public class WorkingArea : Panel
{
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Point Location
{
get
{
return base.Location;
}
set
{
base.Location = value;
}
}
...
}
i am working on a project, with a set of usercontrols beeing loaded dynamically think about sort of a portal page with a bunch of widgets (blocks) and all of them are a usercontrol.
they have certain things in common, so i made them all derive from a masterBlock usercontrol
now is there a way to also have some output (in the .ascx) file in common?
anything i put in the ascx of the masterBlock is not rendered or overwritten by the derived block.
i was wondering if anyone has any hints to get this to work.
The *.ascx files can not be derived (maybe with some "magic" can). Derived can be only classes, sou you can create a MyUserControlBase class, which can create some common controls/output and provide it by protected/public properties to derived class (MyWeatherUserControl for example) which can common controls/output modify.
Sample code:
public class MyUserControlBase : UserControl {
private Panel mainPanel;
protected Panel MainPanel {
get { return this.mainPanel; }
}
public MyUserControlBase() {
this.mainPanel = new Panel();
this.Controls.Add( this.mainPanel );
this.CreateMainPanelContent();
}
protected virtual void CreateMainPanelContent() {
// create default content
Label lblInfo = new Label();
lblInfo.Text = "This is common user control.";
this.MainPanel.Controls.Add( lblInfo );
}
}
public class MyWeatherUserControl : MyUserControlBase {
protected override void CreateMainPanelContent() {
// the base method is not called,
// because I want create custom content
Image imgInfo = new Image();
imgInfo.ImageUrl = "http://some_weather_providing_server.com/current_weather_in_new_york.gif";
this.MainPanel.Controls.Add ( imgInfo );
}
}
public class MyExtendedWeatherUserControl : MyWeatherUserControl {
protected override void CreateMainPanelContent() {
// the base method is called,
// because I want only extend content
base.CoreateMainPanelContent();
HyperLink lnkSomewhere = new Hyperlink();
lnkSomewhere.NavigationUrl = "http://somewhere.com";
this.MainPanel.Controls.Add ( lnkSomewhere );
}
}
You could override the Render method in the base class to give you custom rendering, and then the subclasses could work with that.