Tedious coding sucks. SubSonic saves lots of time. But I want it to save me more. So I've started working on a method that will fill out the controls for me so I don't have to. It's simple really. I just new() up a the model I will use and pass it to my method along with the UserControl I have my form elements in.

   1:  public void SetupForm<T>(Control form, T activeRecord ) 
   2:      where T : AbstractRecord<T> ,new()
   3:  {
   4:      TableSchema.TableColumnSettingCollection settings = activeRecord.GetColumnSettings();
   5:      foreach (TableSchema.TableColumnSetting setting in settings)
   6:      {
   7:          Control settingControl = form.FindControl(setting.ColumnName);
   8:          if (settingControl is TextBox)
   9:              ((TextBox)settingControl).Text = 
  10:                  activeRecord.GetColumnValue<string>(setting.ColumnName);
  11:          if (settingControl is DropDownList)
  12:              ((DropDownList)settingControl).SelectedValue = 
  13:                  activeRecord.GetColumnValue<string>(setting.ColumnName);
  14:      }
  15:  }

The key thing is to make sure you have called EnsureChildControls() before calling this method.

So in my control the code looks like

   1:  int issueId = SubSonic.Sugar.Web.QueryString<int>("issue");
   2:  if (issueId != 0)
   3:  {
   4:      EnsureChildControls();
   5:      Issue issue = new Issue(issueId);
   6:      //BasePage inherits from System.Web.UI.Page
   7:      BasePage.SetupForm(this, issue);
   8:  } 

Now this is pretty rough and more than anything a proof of concept so your mileage may vary. But I figure this is a good place to start

kick it on DotNetKicks.com

I've changed this a bit due to some comments

   1:  enum Directions
   2:  {
   3:      Up,
   4:      Down,
   5:      Left,
   6:      Right
   7:  }
   8:  void DoSomething()
   9:  {
  10:      
  11:      Directions currentState =(Directions) Enum.Parse(typeof(Directions), "Down"); 
  12:  }

Have you ever wanted to grab bulk data on a grid view? It's actually not that hard at all. Set the System.Web.UI.WebControls.GridView.DataKeys Property on the Grid, then on the postback event iterate over the grid like so.

   1:  foreach (GridViewRow row in gridView.Rows) 
   2:  { 
   3:      DataKey data = gridView.DataKeys[row.RowIndex]; 
   4:      int Id = (int)data.Values["Id"]; 
   5:      TextBox myControl = row.FindControl("mycontrol") as TextBox; 
   6:      //do something with the data
   7:  } 

And on your GridView add the DataKeys Property

   1:  <asp:GridView ID="myGrid" runat="server" 
   2:       DataSourceID="myDataSource"
   3:       DataKeyNames="Id" >

I think SubSonic is simply amazing. It has saved me so much time and effort of writing. One little trick that it's got is the LoadFromPost method the Models inherit from the SubSonic.AbstractRecord<T> class.

What LoadFromPost does is iterate over the the Request.Forms collection and matches the name of the form element with the corresponding  property for your Model. So what that means is no more of this.

 1: Product prod = new Product();
 2: prod.Name = name.Text;
 3: ....//More code setting the properties based on the form
 4: prod.Save

So all you now have to do is this

 1: Product prod = new Product
 2: prod.LoadFromPost();//this loads up all the properties.
 3: prod.Save();

Anything that saves time is a good thing, and nothing sucks more than writing tedious databinding code. Another advantage is that you don't need to use Server controls just plain html tags so you don't have to worry about ASP.Net adding all the extra naming container bits as well and that'll play nicely with any JavaScript or CSS that uses the id of an html tag.

kick it on DotNetKicks.com

I recently updated an application to the latest version of the Ajax Control Toolkit and my AutoComplete Extender broke. It was returning the list fine. but in the dropdown the only values showed were undefined. Not cool. But after some research I changed the web service that returns the values to use the new AutoComplete Item

First Version

 

 1: [WebMethod]
 2: public string[] AutoComplete( string prefixText, int count )
 3: {
 4:  List<string> results = new List<string>(); 
 5:  IDataReader reader = DataLayer.AutoCompleteLookup(prefixText).GetReader(); 
 6:  while (reader.Read())
 7:  {
 8:  results.Add(reader[0].ToString());
 9:  }
 10:  
 11:  return results.ToArray();
 12: }

That worked fine, and I have a feeling that if the service wasn't returning numeric values like 01-145 that it might still be fine, but the service was returning values like 01-145 so I had to do this

 1: [WebMethod]
 2: public string[] AutoComplete( string prefixText, int count )
 3: {
 4:  List<string> results = new List<string>(); 
 5:  IDataReader reader = DataLayer.AutoCompleteLookup(prefixText).GetReader(); 
 6:  while (reader.Read())
 7:  {
 8:  results.Add(
 9:  AutoCompleteExtender.CreateAutoCompleteItem
 10:  (
 11:  reader[0].ToString(), 
 12:  reader[0].ToString()
 13:  )
 14:  );
 15:  }
 16:  return results.ToArray();
 17: }

After that changed the AutoComplete worked again.

Now that got me wondering what I could do with the AutoComplete Item that comes back. So I found a post by Phani Raj. The Extender Control has a property OnClientItemSelected (actually the extender has 9 OnClient events) so you could do something like this.

 1: <ajax:AutoCompleteExtender ID="AutoComlete" runat="server"
 2:  TargetControlID="lookupBox"
 3:  ServicePath="~/services/Lookup.asmx" 
 4:  ServiceMethod="AutoComplete" 
 5:  OnClientItemSelected="AutoComlete_Selected" />
 6:  
 7: <script language="javascript">
 8:  function AutoComlete_Selected( source, eventArgs ) {
 9:  $get("hiddenField").value = eventArgs.get_value();
 10:  //eventArgs also has .get_text() 
 11:  }
 12: </script>

Hope this helps someone

While using a cookie parameter for an ObjectDataSource I discovered that you get the whole cookie, not the value from a key....So instead of my method getting passed the value, it was getting passed key=value. Needless to say I was a little miffed. But I quickly discovered how easy it is to create my own custom data parameter.

    [ToolboxData("<{0}:CookieParameter ></{0}:CookieParameter>")]

    [System.Drawing.ToolboxBitmap(typeof(Parameter))]

    public class CookieParameter : Parameter

    {

        private string _cookieKey;

        public string Key

        {

            get { return _cookieKey; }

            set { _cookieKey = value; }

        }

        private string _cookie;

        public string Cookie

        {

            get { return _cookie; }

            set { _cookie = value; }

        }   

        public CookieParameter() : base()

        {

        }

        public CookieParameter(string name, object value) : base(name)

        {

        }

        public CookieParameter(string name, TypeCode type, object value) : base(name, type)

        {

        }

        protected CookieParameter(CookieParameter original)

            : base(original)

        {

        }

        protected override object Evaluate(HttpContext context, Control control)

        {

            if ((context != null) && (context.Session != null))

            {

                return context .Request.Cookies[Cookie].Values.Get(Key);

            }

            return null;

        }


        protected override Parameter Clone()

        {

            return new CookieParameter(this);

        }

    }


Then in the data source control just do this

<asp:ObjectDataSource ID="MyDataSource" runat="server">

    <SelectParameters>

        <Data:CookieParameter Cookie="cookie" Name="myParameter" Type="Object" Key="cookieKey"  />

    </SelectParameters>

 

</asp:ObjectDataSource>

One of the nice features of  System.Collection.Generics.List is it is able to sort on the go with a IComparable, and you can do that simply with an anonomous method
This is simply done like so.

   1:  List<Attribute> attributes = Entity.Get();
   2:  attributes.Sort
   3:  (
   4:      delegate( Attribute firstAttribute, Attribute secondAttribute )
   5:      {
   6:          return firstAttribute.Index.CompareTo( secondAttribute.Index );
   7:      }
   8:  );

SubSonic is such a time saver. Fresh out of the box it gives you so much goodness that it makes you wonder how you developed without it. However one feature I find lacking is that it doesn't have a paging method from the get go. However it's easy enough to implement paging with SubSonic.

The trick is you have to created two methods to get the paging to work. The drawback to using the ObjectDataSource for paging is that the GridView doesn't take an output parameter as the record count. So what do you do to take advantage of the SubSonic goodness? First create the select method and the select count in the controller class.

   1:  [DataObjectMethod( DataObjectMethodType.Select, false )]
   2:  public MyCollection FetchAllPaged(int start, int pageLength)
   3:  {
   4:      int startIndex;
   5:      if(start ==0)
   6:      {
   7:          startIndex = 1;
   8:      }
   9:      else
  10:      {
  11:          startIndex = start / pageLength + 1;
  12:      }
  13:      MyCollection coll = new MyCollection();
  14:      Query qry = new Query( My.Schema );
  15:      qry.PageSize = pageLength;
  16:      qry.PageIndex = startIndex;
  17:      coll.LoadAndCloseReader( qry.ExecuteReader() );
  18:      return coll;
  19:  }
  20:  public int FetchAllCount()
  21:  {
  22:      Query qry = new Query( My.Schema );
  23:      return qry.GetCount( My.Columns.MyId );
  24:  }
The trick to remember is that the ObjectDataSource passes passes in the count of the items but the paging method exposed by SubSonic expects the actual page number. This means that if your PageSize is 10 changing to the second page the start is 20, but SubSonic expects 2.
   1:  <asp:GridView ID="TheGrid" runat="server" 
   2:      AllowPaging="True" 
   3:      DataSourceID="MyDataSource"
   4:      PageSize="10" />
   5:  <asp:ObjectDataSource ID="MyDataSource" runat="server" 
   6:      SelectMethod="FetchAllPaged" 
   7:      TypeName="MyController" 
   8:      SelectCountMethod="FetchAllCount"
   9:      StartRowIndexParameterName="start" 
  10:      MaximumRowsParameterName="pageLength" 
  11:      EnablePaging="True" 
  12:  />
More Posts « Previous page