I need to set data for a custom control in my HTML file but I'm experiencing problems with ParseChildren property which doesn't work and my code raises a "Parser Error" for "Object not set to an instance of an object".
Here is my code behind:
namespace UserControls
{
[ParseChildren(true)]
public partial class MainVisit : UserControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public MyCollection MyList { get; private set; }
public MainVisit()
{
this.MyList = new MyCollection();
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.MyList = new MyCollection();
}
}
public class MyCollection : List<SecondaryVisit> { }
public class SecondaryVisit
{
public string Name { get; set; }
public SecondaryVisit() { }
}
}
And this is the HTML file
<%# Register TagPrefix="mscuc" TagName="MainVisitForm" Src="~/UserControls/MainVisit.ascx" %>
<mscuc:MainVisitForm id="MainVisitForm" runat="server">
<VisitList>
<mscuc:SecondaryVisit Name="visit_name" />
</VisitList>
</mscuc:MainVisitForm>
So when I compile and try to execute this code an exception is raised for "Object not set to an instance of an object" at line
Any idea?
Related
I get this error on the setter line
"An unhandled exception of type 'System.StackOverflowException' occurred in WebFormsApplication1.dll"
What is an elegant way to manipulate Property1 which I added getter and setter for in a Master page (please see below),
then I attempted to manipulate it in method1() and finally call method1() inside onInit.
namespace WebFormsApplication1
{
public partial class SiteMaster : MasterPage
{
public string Property1
{
get
{
return System.Configuration.ConfigurationSettings.AppSettings["key1"];
//gets value from config file where pair key-value is stored
}
set
{
Property1 = value;
}
}
public void method1()
{
Property1 = Property1 + "stringToAppend"; // manipulate property here
}
protected void Page_Init(object sender, EventArgs e)
{
method1();
.....
}
}
}
In the Site.Master.aspx I have <%= Property1 %>
If I don't add the setter the property is read only. Maybe I should manipulate it inside the setter?
I was hoping to be able to do it separately for increased modularisation.
Thanks
the problem is here:
set
{
Property1 = value;
}
You can't do it because recursion occurs here and no condition to exit, you can't set this property in his own setter, you should have some field and set it in setter:
public string someValue = System.Configuration.ConfigurationSettings.AppSettings["key1"];
public string Property1
{
get
{
return someValue;
}
set
{
someValue = value;
}
}
Or you can use auto-property:
C# 6:
public string Property1 {get;set;} = System.Configuration.ConfigurationSettings.AppSettings["key1"];
Or lower than C# 6 - you should declare you property and initialize it in contructor or method:
public string Property1 {get;set;}
public void ConstructorOrMethod()
{
Property1 = System.Configuration.ConfigurationSettings.AppSettings["key1"];
}
public partial class TestConrol : UserControl
{
public TestConrol()
{
InitializeComponent();
}
public override string ToString()
{
return "asd";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestConrol tc1 = new TestConrol();
comboBox1.Items.Add(tc1);
TestConrol tc2 = new TestConrol();
comboBox1.Items.Add(tc2);
}
}
When form loaded, I see combobox has two items with empty names, instead of "asd" :/
But this work if I override ToString() in common class, not derived from anything:
public class TestClass
{
public override string ToString()
{
return "bla bla bla";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestClass tcl = new TestClass();
comboBox1.Items.Add(tcl);
}
}
After that I see in combobox "bla bla bla"
Create a property in you control and map the DisplayMember of the combobox to that property, it should work.
I tried to understand the source code(!). This is not a simple call to ToString().
There's an internal class System.Windows.Forms.Formatter doing some stuff. It eventually creates a converter. This is roughly equivalent to saying:
var conv = System.ComponentModel.TypeDescriptor.GetConverter(tc1.GetType());
where tc1 is the TestContol from your question. Now, had we used the TestClass tcl which doesn't implement any interfaces, this would have given us a converter which would eventually call ToString().
But in this example we use tc1, and it is a System.ComponentModel.IComponent. Our conv therefore becomes a System.ComponentModel.ComponentConverter. It uses the Site of the IComponent. When we say:
string result = conv.ConvertTo(tc1, typeof(string));
and the Site is null, we get the empty string "" you saw in your combo box. Had there been a Site it would have used its Name instead.
To demonstrate that, put the following into your TestControl instance constructor:
public TestConrol()
{
InitializeComponent();
Site = new DummySite(); // note: Site is public, so you can also
// write to it from outside the class.
// It is also virtual, so you can override
// its getter and setter.
}
where DummySite is something like:
class DummySite : ISite
{
public IComponent Component
{
get { throw new NotImplementedException(); }
}
public IContainer Container
{
get { throw new NotImplementedException(); }
}
public bool DesignMode
{
get { throw new NotImplementedException(); }
}
public string Name
{
get
{
return "asd"; // HERE'S YOUR TEXT
}
set
{
throw new NotImplementedException();
}
}
public object GetService(Type serviceType)
{
return null;
}
}
Use comboBox1.Items.Add(tc1.ToString()); instead of comboBox1.Items.Add(tcl);
This worked for me:
comboBox1.FormattingEnabled = false
In your UserControl, add a property, and call it FriendlyName for example, as such
namespace project
{
public partial class CustomUserControl : UserControl
{
public CustomUserControl()
{
InitializeComponent();
}
public String FriendlyName { get => "Custom name"; }
}
}
And then set the DisplayMember property of your ComboBox to "FriendlyName", as such
myComboBox.DisplayMember = "FriendlyName";
And to me, this was a very clean solution and gut tells me it is the intended way to do it.
this is what i had research in this few day, but i still cannot show the controls in design time... its keep show Type " System.Web.UI.UserControl does not have a public property name. The control is not showing while i have a inner property
Example .aspx code:
<XF:XFButton ID="XFButton1" runat="server" >
<Button1 ClientSideEvents-Click = "function(s,e){ApplyJavascript();}"></Button1>
</XF:XFButton>
Example behind .ascx code:
namespace XESControlsTestApp.WebControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
[ParseChildren(true)]
[Browsable(true)]
public class ContentContainer : Control, INamingContainer {
private ITemplate _content;
public ITemplate ContentTemplate
{
get { return this._content; }
set { this._content = value; }
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (this._content != null)
{
ContentContainer container = new ContentContainer();
this._content.InstantiateIn(container);
this._content.InstantiateIn(this);
}
}
}
If I pass the derived class testA a PlaceHolder that contains a Hyperlink, with a url that starts with
a tilde, it resolves it correctly.
However, when I pass testB
(identical apart from it inherits from
System.Web.UI.UserControl) the same PlaceHolder It
renders it literally (doesn't
transform / resolve the '~')
Any ideas?
public class testA : System.Web.UI.Control
{
public System.Web.UI.WebControls.PlaceHolder plc { get; set; }
protected override void OnLoad(EventArgs e)
{
if (plc != null)
this.Controls.Add(plc);
base.OnLoad(e);
}
}
public class testB : System.Web.UI.UserControl
{
public System.Web.UI.WebControls.PlaceHolder plc { get; set; }
protected override void OnLoad(EventArgs e)
{
if (plc != null)
this.Controls.Add(plc);
base.OnLoad(e);
}
}
This is ASP.NET
When you inherit from System.Web.UI.UserControl and do not associate your control with an ascx file then your control TemplateSourceVirtualDirectory will not be set, this is required by the ResolveClientUrl method - if its null or empty the url will be returned AS IS.
To solve your problem, just set AppRelativeTemplateSourceDirectory :
public class testB : System.Web.UI.UserControl
{
public System.Web.UI.WebControls.PlaceHolder plc { get; set; }
protected override void OnLoad(EventArgs e)
{
if (plc != null)
{
this.AppRelativeTemplateSourceDirectory =
plc.AppRelativeTemplateSourceDirectory;
this.Controls.Add(plc);
}
base.OnLoad(e);
}
}
A UserControl is normally associated with an ascx file that defines its markup. Such controls should be instantiated using TemplateControl.LoadControl() before they're added to the page, in order to perform event catch-up.
I suspect that event catch-up does not take place since you don't call LoadControl(), so the Hyperlink's NavigateUrl never gets a chance to be properly resolved.
Here is what I want:
I want a control to put on a page, which other developers can place form elements inside of to display the entities that my control is searching. I have the Searching logic all working. The control builds custom search fields and performs searches based on declarative C# classes implementing my SearchSpec interface.
Here is what I've been trying:
I've tried using ITemplate on a WebControl which implements INamingContainer
I've tried implementing a CompositeControl
The closest I can get to working is below.
OK I have a custom WebControl
[
AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
DefaultProperty("SearchSpecName"),
ParseChildren(true),
ToolboxData("<{0}:SearchPage runat=\"server\"> </{0}:SearchPage>")
]
public class SearchPage : WebControl, INamingContainer
{
[Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty),
DefaultValue(typeof(ITemplate), ""),
Description("Form template"),
TemplateInstance(TemplateInstance.Single),
TemplateContainer(typeof(FormContainer))]
public ITemplate FormTemplate { get; set; }
public class FormContainer : Control, INamingContainer{ }
public Control MyTemplateContainer { get; private set; }
[Bindable(true), Category("Behavior"), DefaultValue(""),
Description("The class name of the SearchSpec to use."), Localizable(false)]
public virtual string SearchSpecName
{
get;
set;
}
[Bindable(true), Category("Behavior"), DefaultValue(true),
Description("True if this is query mode."), Localizable(false)]
public virtual bool QueryMode
{
get;
set;
}
private SearchSpec _spec;
private SearchSpec Spec
{
get
{
if (_spec == null)
{
Type type = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Name == SearchSpecName).First();
_spec = (SearchSpec)Assembly.GetExecutingAssembly().CreateInstance(type.Namespace + "." + type.Name);
}
return _spec;
}
}
protected override void CreateChildControls()
{
if (FormTemplate != null)
{
MyTemplateContainer = new FormTemplateContainer(this);
FormTemplate.InstantiateIn(MyTemplateContainer);
Controls.Add(MyTemplateContainer);
}
else
{
Controls.Add(new LiteralControl("blah"));
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
// <snip>
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
}
public class FormTemplateContainer : Control, INamingContainer
{
private SearchPage parent;
public FormTemplateContainer(SearchPage parent)
{
this.parent = parent;
}
}
then the usage:
<tster:SearchPage ID="sp1" runat="server" SearchSpecName="TestSearchSpec" QueryMode="False">
<FormTemplate>
<br />
Test Name:
<asp:TextBox ID="testNameBox" runat="server" Width="432px"></asp:TextBox>
<br />
Owner:
<asp:TextBox ID="ownerBox" runat="server" Width="427px"></asp:TextBox>
<br />
Description:
<asp:TextBox ID="descriptionBox" runat="server" Height="123px" Width="432px"
TextMode="MultiLine" Wrap="true"></asp:TextBox>
</FormTemplate>
</tster:SearchPage>
The problem is that in the CodeBehind, the page has members descriptionBox, ownerBox and testNameBox. However, they are all null. Furthermore, FindControl("ownerBox") returns null as does this.sp1.FindControl("ownerBox"). I have to do this.sp1.MyTemplateContainer.FindControl("ownerBox") to get the control.
How can I make it so that the C# Code Behind will have the controls generated and not null in my Page_Load event so that developers can just do this:
testNameBox.Text = "foo";
ownerBox.Text = "bar";
descriptionBox.Text = "baz";
OK, I fixed this by making SearchPage extend Panel and removing ParseChildren(true).