Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are components of my custom control not initiated?

I'm making a GenericTable as a custom implementation of GridView that will display the values of any list of objects that's inserted.

To use the control on an aspx page it needs to be a UserControl, so the GridView is included as a component in the GenericTable:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="GenericTable.ascx.cs" Inherits="CASH.WebApplication.Controls.GenericTable" %>
<div style="width: 100%; overflow: scroll">
    <asp:GridView ID="grid" runat="server"></asp:GridView>
</div>

This works fine for the first use of my control, it's added on the aspx page. It seems that doing that adds some sort of magic that initiates the control components. When the user clicks on an item that has properties of it's own, the GenericTable should insert a row below the current row and spawn a new GenericTable that will show said properties. table is the DataTable that I use to set the GridView contents:

var data = table.NewRow();
var child = new GenericTable();

data[0] = child;

table.Rows.InsertAt(data, row);
grid.DataSource = table;
grid.DataBind(); // The extra row is displayed now, initialize components in the aspx code?
child.MakeTable(); // Throws exception because it's `grid` property is null.

When I try to activate the newly made GenericTable, after this code, it's grid is null.

Is there a way to initialize the same magic that happens when this control is located in the aspx code?

Update: Maybe the problem lies in how the table is stored between postbacks, currently I'm using the session, maybe there's a better way to remember user input? The whole GenericTable code:

using Project.DomainModel.Models;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CASH.WebApplication.Controls
{
    public partial class GenericTable : UserControl
    {
        private PropertyInfo[] properties;
        //private GridView gridView;
        private DataTable table = new DataTable();
        private Dictionary<int, int> ingedrukt = new Dictionary<int, int>();

        protected void Page_Init(object sender, EventArgs e)
        {
            grid.RowCommand += WeergaveDossiers_RowCommand;
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                for (int i = 0; i < grid.Rows.Count; i++)
                {
                    grid.Rows[i].Cells[0].ColumnSpan = 0;
                }
            }
            else
            {
                properties = (PropertyInfo[])Session["properties"];
                table = (DataTable)Session["table"];
                ingedrukt = (Dictionary<int, int>)Session["ingedrukt"];

                foreach (var knop in ingedrukt)
                {
                    DetailRijToevoegen(knop.Key, knop.Value);
                }
            }

            grid.DataBind();
        }

        protected void SaveInSession()
        {
            Session["properties"] = properties;
            Session["table"] = table;
            Session["ingedrukt"] = ingedrukt;
        }

        protected void WeergaveDossiers_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            int row = int.Parse((string)e.CommandArgument) + 1;
            int col = GetKolomIndex(e.CommandName) + 1;

            if (ingedrukt.ContainsKey(row))
            {
                if (ingedrukt[row] != col)
                {
                    //DetailRijVerwijderen(row);
                    //ingedrukt.Remove(row);
                    //ingedrukt[row] = col;
                }
            }
            else
            {
                ingedrukt[row] = col;
            }

            //DetailRijToevoegen(row, col);
            SaveInSession();
        }

        protected void DetailRijToevoegen(int row, int col)
        {
            var data = table.NewRow();
            var child = new GenericTable();
            child.grid = new GridView();

            data[0] = child;

            table.Rows.InsertAt(data, row);
            grid.DataSource = table;
            grid.DataBind();

            var cells = grid.Rows[row].Cells;
            // Only keep the first cell
            while (cells.Count > 1)
            {
                cells.RemoveAt(1);
            }

            child.MaakTable(new List<object>() { table.Rows[row][col] });

            grid.Columns[0].Visible = true;
            grid.Rows[row].Cells[0].ColumnSpan = table.Columns.Count;
        }

        protected void DetailRijVerwijderen(int row)
        {

        }

        protected int GetKolomIndex(string naam)
        {
            for (int i = 0; i < properties.Length; i++)
            {
                if (properties[i].Name == naam)
                {
                    return i;
                }
            }

            throw new InvalidDataException("Kolom naam " + naam + " niet bekend");
        }

        public void MaakTable(IEnumerable<object> data)
        {
            properties = data.First().GetType().GetProperties().Where(p => p.CanRead).ToArray();

            grid.AutoGenerateColumns = false;
            var details = new BoundField();
            details.DataField = "Details";
            grid.Columns.Add(details);

            table.Columns.Add(new DataColumn("Details", typeof(object)));
            foreach (var veld in properties)
            {
                table.Columns.Add(new DataColumn(veld.Name, (veld.Name == "Id" ? typeof(object) : veld.PropertyType)));
                grid.Columns.Add(MaakKolom(veld));
            }

            foreach (var entry in data)
            {
                var row = table.NewRow();
                int col = 0;

                foreach (var veld in properties)
                {
                    row[++col] = veld.GetValue(entry);
                }

                table.Rows.Add(row);
            }

            grid.DataSource = table;

            SaveInSession();
        }

        protected DataControlField MaakKolom(PropertyInfo veld)
        {
            DataControlField field;

            if (typeof(Entity).IsAssignableFrom(veld.PropertyType))
            {
                field = new ButtonField();
                ((ButtonField)field).DataTextField = veld.Name;
                ((ButtonField)field).ButtonType = ButtonType.Button;
                ((ButtonField)field).CommandName = veld.Name;
            }
            else if (veld.PropertyType == typeof(bool))
            {
                field = new CheckBoxField();
                ((CheckBoxField)field).DataField = veld.Name;
            }
            else if (veld.PropertyType.IsEnum)
            {
                field = new TemplateField();
                //((TemplateField)field).ItemTemplate = (ITemplate)new Label()
                //{
                //  Text = "#DataBinder.Eval(\"" + veld.Name + "\")",
                //};
            }
            else if (veld.PropertyType == typeof(DateTime))
            {
                field = new TemplateField();
                //field.DatePicker = true;
            }
            else
            {
                field = new BoundField();
                ((BoundField)field).DataField = veld.Name;
            }

            field.HeaderText = veld.Name;

            return field;
        }

        protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {

            }
        }
    }
}
like image 535
MrFox Avatar asked Jan 24 '14 16:01

MrFox


1 Answers

I'm not exactly sure what you're trying to acheive, I just know that what you're doing is fundamentally wrong (well, in terms of the ASP.NET view of the world...).

  1. You're adding your control to the input data, rather than adding it as a sub-control of the grid.
  2. You're not instantiating the GridView control in the consructor of your control.
  3. Session is absolutely the wrong place to store lots of page-related data.

I'll start with the last point first: If this is data that needs to persist between visits to the site, then you must put it into a database. If this is data that only exists from the time someone logs in, to the time they logout, then yes, Session may be the right place for it. Otherwise, if it's specific to the page and should be discarded when the user visits a different one, then you should either reload it from the database every time or maybe store it in the ViewState.

Next, are all the objects of the same type / do they have the same fields? If so, then the default behaviour (controlled explicitly by AutoGenerateColumns will do the job for you, no extra work required:

<asp:GridView runat="server"
    ID="grid"
    AutoGenerateColumns="true" />

If the do not have the same columns, then they should be in seperate grids; a GridView is a way of creating an HTTP <table> element. A table element should only contain related data; you wouldn't use a single table for displaying prices of fish and colours of cars. It follows from this, that if you have different tables, with unrelated data, then you should have different data sources... a more simple solution that means you don't need to implement the control you're trying to implement.

Finally, for completeness, when you define a control, you're just creating a class. If you want to be able to instantiate a control in the way you are trying, then you need to make sure that all its data members are a instantiated in the constructor or that any references are guarded by null-reference checks:

if (grid != null)
{
    // Do stuff with grid
}
like image 85
The Real Edward Cullen Avatar answered Dec 07 '22 21:12

The Real Edward Cullen