7

Assume we have a Person class:

public class Person
{
  public string FamilyName {get;set;}
  public string GivenName {get;set;}
}

And there's a control to somehow display the information of a list of persons. Here is the pseudocode of the aspx:

<uc1:EmployeesViewer runat="server">
  <Employees>
    <Person>
      <GivenName>John</GivenName>
      <FamilyName>Kerry</GivenName>
    </Person>
    <Person>
      <GivenName>Jack</GivenName>  
      <FamilyName>Lew</GivenName>
    </Person>
  <Employees>
</uc1:EmployeesViewer>

EmployeesViewer.Employees is of type List<Person>, with the attribute [PersistenceMode(PersistenceMode.InnerProperty)].

But Visual Studio doesn't compile this. Is it possible to declare a Person object using markup?

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
Gqqnbig
  • 5,845
  • 10
  • 45
  • 86
  • Why do you want to do this? This seems very strange. – jrummell Apr 08 '16 at 21:27
  • @jrummell We can assign a property in code-behind, right? I just explore the possibility in doing it in markup, searching a lot with no luck. – Gqqnbig Apr 08 '16 at 21:30
  • That's because it's not well supported, and typically discouraged since your logic should not be defined in your view/page. You can, however, embed c# code in your page, but it's ugly. https://msdn.microsoft.com/en-us/library/ms178135.aspx – jrummell Apr 08 '16 at 21:34
  • @jrummell it's not logic; it's my static data. It's up to EmployeesViewer how to process the data, which is written code-behind. In WPF we can do this, such as by static resources. Even in asp.net, do you explain it's ok to write `
    `?
    – Gqqnbig Apr 08 '16 at 21:38
  • @jstreet thanks, but I'm talking about asp.net, not HTML. – Gqqnbig Apr 11 '16 at 20:46

5 Answers5

8

Is it possible to declare a Person object using markup?

Yes, with only some minor changes to the markup (and no changes to your Person class):

<%@ Page Language="C#" CodeBehind="DemoPage.aspx.cs" Inherits="DemoApp.DemoPage" %>
<%@ Register TagPrefix="uc1" Assembly="DemoApp" Namespace="DemoApp" %>

<uc1:EmployeesViewer runat="server">
    <Employees>
        <uc1:Person GivenName="John" FamilyName="Kerry" />
        <uc1:Person GivenName="Jack" FamilyName="Lew" />
    </Employees>
</uc1:EmployeesViewer>

The uc1: tag prefix assumes that your Person class is in the same assembly and namespace as EmployeesViewer. If not, just <%@ Register %> another tag prefix for Person.

Note: You can use the <uc1:Person> syntax even though Person doesn't derive from Control:

namespace DemoApp
{
    public class Person
    {
        public string FamilyName {get;set;}
        public string GivenName {get;set;}
    }
}

Here's a simple EmployeesViewer control that renders the list of employees:

namespace DemoApp
{
    [ParseChildren(true)]
    [PersistChildren(false)]
    public class EmployeesViewer : Control
    {
        private readonly List<Person> _employees = new List<Person>();

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public List<Person> Employees
        {
            get { return _employees; }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            foreach (Person person in this.Employees)
            {
                writer.RenderBeginTag(HtmlTextWriterTag.P);
                writer.WriteEncodedText(string.Format("{0} {1}", person.GivenName, person.FamilyName));
                writer.RenderEndTag();
            }
        }
    }
}

The [ParseChildren(true)] attribute is needed so that at run time ASP.NET interprets the <Employees> tag as a property name rather than as literal markup to be rendered.

The [PersistChildren(false)] and [PersistenceMode(PersistenceMode.InnerProperty)] attributes are needed so that, if at design time you modify the Employees collection using the designer's Properties window, Visual Studio correctly persists the collection to markup.

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
  • As I asked in another comment, all solutions tends to create a control or user control. **Is it true that all tags written in the markup must be of type `Control` or its derived classes?** – Gqqnbig Apr 13 '16 at 00:53
  • No. With this approach, there's no need for Person to derive from Control. (You still need to use a tag prefix in the markup so that ASP.NET can locate the assembly and namespace of the Person class.) – Michael Liu Apr 13 '16 at 02:17
  • Can you give me the full markup, especially Register directives? And can I place Person and EmployeesViewer in one file? – Gqqnbig Apr 14 '16 at 23:36
  • 1
    I've updated this answer with my complete .aspx page, plus sample namespaces for the Person and EmployeesViewer classes. (The .aspx.cs code-behind contains only the DemoApp namespace and an empty `public partial class DemoPage : Page` declaration.) It's up to you whether or not to put Person and EmployeesViewer in the same file. Are you having trouble getting this to work? – Michael Liu Apr 15 '16 at 04:07
  • Person class works. Now I added `<%@ register TagPrefix="sys" Assembly="System" Namespace="System" %>` and put `` inside , and changed list type to System.Version, but the compiler says "Unknown server tag 'sys:Version'.". Do you know why? – Gqqnbig Apr 16 '16 at 00:28
  • The correct assembly is `mscorlib`, not `System`. See the [documentation for the Version class](https://msdn.microsoft.com/en-us/library/system.version(v=vs.110).aspx). – Michael Liu Apr 16 '16 at 03:00
2

I'm not sure exactly why you'd want to do this aside from academic reasons. Since some of the other posters have focused mainly on using controls to create your markup, why not use something like a DataSet to achieve what you're trying to achieve?

You would create a dataset by selecting DataSet from Add > New Item > Data >DataSet from the context menu. DataSets can be Strongly-Typed and provide you full Intellisense features, but this might not be ideal for your scenario. Just thought I'd throw this idea out there.

Using DataSets:

Process: Create a DataSet, create a DataTable, create columns (DataColumns) for the DataTable, instantiate and bind to a given .aspx control.

Standard DataSet/DataTable/DataColumn:

DataSet ds = new DataSet("MyDataSetNameSpace");
DataTable person = new DataTable();
//add columns to the datatable
person.Columns.Add(new DataColumn(typeof(string)), "FamilyName");
person.Columns.Add(new DataColumn(typeof(string)), "GivenName");

Strongly-Typed DataSet:

PersonDataSet personDs = new PersonDataSet("PersonDataSetNameSpace");
//DataTable should already exist in your strongly-typed DataSet because you define the datatables in the vs designer
//accessing the datatable
personDs.Person person = new PersonDataSet.Person();
person.GivenName = "John Eisenhower Smith";
person.FamilyName = "Johnny";//nickname?  I'm guessing this might actually be Smith

If you wanted to use the DataSet in Markup, you would bind it to the specific list control you are trying to create a list with.

//you can define the DataSource in Markup or in CodeBehind.  You'd have to define a protected or public variable so that you can access the item in your markup.
//In markup, you would set DataSource = <%# Person%>
myGridViewControl.DataBind();//bind the dataset/datatable to the control

MSDN Reference:

https://msdn.microsoft.com/en-us/library/ss7fbaez(v=vs.110).aspx

EDIT:

To accomplish what you accomplish with XAML, you'd have to inject XAML through the use of a Silverlight control. See this post for more information on how to do that:

Is there a way to insert Silverlight XAML into my page using ASP.NET code-behind?

The short answer is in WebForms, it can't be done at the moment. You'd have to accomplish this with hacks because the framework doesn't support it. You could use a C# code block to do what you're trying to do, but you still won't be able to accomplish what you are trying to accomplish correctly.

Community
  • 1
  • 1
cr1pto
  • 539
  • 3
  • 13
  • So the academic reason is that WPF can create object in XAML, plenty of articles available, eg. [How XAML works - Creating Objects With XAML](http://www.i-programmer.info/programming/wpf-workings/446-how-xaml-works.html). I believe you guys won't tell me to create user control if in WPF context. – Gqqnbig Apr 14 '16 at 23:58
  • Ahhhh...tbh I'm not super familiar with XAML...more of a WinForms/WebForms guys. I can't think of anything to help you accomplish what you can in XAML in ASP.NET or even in Java EE (although my knowledge is limited here as well - Java EE). Let me do some more research and see what I can come up with. – cr1pto Apr 15 '16 at 15:32
  • 1
    This post injects XAML into ASP.NET through the use of a silverlight control. I know this isn't exactly what you are looking for, but this is the best I could do. Most of the other posters just suggest using ASP.NET WebForms with a custom control. I've added the post and the edits in my original post. – cr1pto Apr 15 '16 at 15:39
1

Can't you make Person as a user control? And add it as such:

<uc1:EmployeesViewer runat="server">
    <Employees>
        <uc1:Person GivenName="John"/>
    </Employees>
<uc1:EmployeesViewer/>

EDIT

Made a test example. Add the following to employeesView to be able to access persons in markup:

Private _emptyDataTemplate As New List(Of Person)
    <Browsable(False)>
    <PersistenceMode(PersistenceMode.InnerProperty)>
    <TemplateContainer(GetType(Person))>
    Public ReadOnly Property Persons() As List(Of Person)
        Get
            Return _emptyDataTemplate
        End Get
    End Property

Made a Person class:

Public Class Person
    Inherits System.Web.UI.UserControl

    Private msFirstName As String
    Public Property FirstName() As String
        Get
            Return msFirstName
        End Get
        Set(ByVal value As String)
            msFirstName = value
        End Set
    End Property
End Class

And in main.aspx I added this to markup:

<!-- top of page -->
<%@ Register TagPrefix="pd" TagName="Person" Src="Person.ascx" %>
<%@ Register TagPrefix="pd" TagName="pView" Src="WebUserControl1.ascx" %>

<!-- wherever you use employeesView -->
<pd:EmployeesView ID="PView1" runat="server">
    <Persons>
        <pd:Person ID="Person2" FirstName="blabal" runat="server"></pd:Person>
        <pd:Person ID="Person3" FirstName="bobba" runat="server"></pd:Person>
    </Persons>
</pd:EmployeesView>

Hope it helps : )

user3532232
  • 257
  • 8
  • 19
  • Yes, I can. But it's my business object. Is it required to create one more PersonUserControl? – Gqqnbig Apr 11 '16 at 06:52
  • well if it's possible you could always derive your Person-class to a new class and also derive from Usercontrol so that you can keep your current Person-class features – user3532232 Apr 12 '16 at 11:14
  • is it true that all tags written in the markup must be of type Control or its derived classes? – Gqqnbig Apr 12 '16 at 11:23
  • Don't have more experience than you on this to be honest. I tried to register a xxx.vb file but then it couldn't register it althoug the class in the .vb-file was of type UserControl. So I'd guess there must be something with the .ascx-files. – user3532232 Apr 12 '16 at 13:48
  • At least I'm unable to solve it if I can't use a UserControl. But Someone else might know a way. – user3532232 Apr 12 '16 at 15:55
1

Yes, it is possible.

<% Person person1 = new Person();
   person1.FamiliName="Brown";
   person1.GivenName="John";
   List<Person> persons = new List<Person>();
   persons.Add(person1);
   EmployeesViewer.DataSource = persons;
%>
Alberto León
  • 2,879
  • 2
  • 25
  • 24
-1

This is possible. However, it is entirely its own animal. While the approach is all Microsoft, it's surprising well adopted.

You can use xsd style sheets to format the data. Then you plug in an object, and it renders that object. In some versions of visual studio, there are even methods of generating the object models from the style sheet.

GL

Community
  • 1
  • 1
Stickman
  • 31
  • 6
  • Do you understand my question? In code-behind, we know we can write `EmployeesViewer.Employees.Add(new Employee())` to achieve the same function, but I want to write in markup. – Gqqnbig Apr 07 '16 at 17:18