Sunday, September 20, 2009

Understanding Coolite ComboBox Mode

This week I came across a very valuable piece of information concerning Coolite combo boxes.  A combobox has attribute called Mode. What this is set to greatly affects the overall behavior of the control, and can create extra queries where you may not expect them.  I find that having an understanding of this attribute can help you develop more efficient pages using the Coolite ComboBox.

  • Default – The default behavior for this control is based on how it initially receives its data.  If it is connected to a Store of any kind, it will act according to the rules of Remote if no store is provided it will act according to the rules of Local
  • Remote – whenever the dropdown is expanded make a query to the data store to get the latest data and bind the data according to the DisplayField and ValueField.  This is where the second query comes in.  Even setting AutoLoad to true on the Store will cause this to happen. Be wary of this second query, especially if the query to get the data is heavy, this could easily create a bottleneck
  • Local – once the data is loaded do not go out and get new data unless load is called on the Store.  This has the greatest chance of working with potentially stale data (depending on your model) but is also the most efficient as it allows you to control when loads happen.  Remember, you can easily tell your Store to load by calling the load() method on the store via JavaScript

So what is the best approach, well as my college mentor Dr. George Nezlek used to say “it depends”. I would say for the majority of these calls you are better off explicitly defining the the Mode as Local and controlling the loading of the store through JavaScript and event handlers.  However, if the user is going to spend a lot of time on the page and the data is critical and very likely to change, Remote may be the better option.

I would assume there is a way to define poll time for the DropDown query, however, I have not explored that, but something I would look at for the later case.

Hope this was helpful, I discovered this and, as usual, found no documentation from Coolite explaining this, so I thought it would be useful to talk about it here.

Wednesday, September 16, 2009

Experiments with RIA Services

Unfortunately I have been so busy as of late I have not had the time I would like to dedicate to exploring the .NET RIA services.  I have been quite impressed from what I have seen on video and heard from colleagues (one colleague is already planning to use it in an enterprise project he is writing).

This weekend I decided to sit down and at least understand the mechanics of transmitting data to and from the server and how to extend existing classes to increase functionality.  What I found is that RIA Services have a bit of a learning curve while you get your head around the model, but once you do, it works very nicely.  My plan was to demonstrate basic binding with an entity generated by the Entity Framework, then move on to include a single related entity, then finally a Many to Many relationships.  To start I brought in 4 classes from my AnimeManager database.  As expected EF picked up the Many-to-Many and hide the relational table, leaving me with the following:image

Let me state that THIS DOES NOT WORK!!  The RIA services DO NOT support a many to many relationship defined this way.  It took me a bit of scouring but I finally came across this blog entry which explains things: Creating Apps with RIA Services (part 3)

Essentially the key is HOW you add the entities, because you WANT the relational tables.  So in this case I deleted the existing EDMX and added this as follows:

  1. Added Studios, Series, SeriesGenres
  2. Allowed the generation of the models
  3. Used Update Model from Database to add the Genres table
  4. Removed the generated relationship between Genres and Series
  5. Recreated this relationship manually by relating Genres to SeriesGenres via this dialogimage
  6. IMPORTANT!!!! THE NAME OF THE RELATIONSHIP MUST MATCH THE FOREIGN KEY NAME FROM THE DATABASE
  7. After adding this you will set get an error about Association type, use Mapping Details to ensure that this relationships maps to your Many type (SeriesGenres in this case)
  8. Build, everything should go through without fail, once it does you can proceed to the next portion, creating the domain service.

RIA Services creates Services that handle the interaction between the underlying WCF web services and the data extracted via the model layer. To create this access the Add New Item dialog and select Domain Service Classimage

The following dialog is subsequently displayed:image

A service class can support multiple entities, though convention generally dictates that each service represent one entity.  For the sake of this I am going to have all three, you will see why in a moment, it has to do with the many to many relationship.  

The important thing here is that you click "”Generate associated classes for metadata”.

Following this screen VS will update your solution and add two files: SeriesServices.cs and SeriesService.metadata.cs.  It is alright to modify these files as they are only generated once.

By using the SilverLight Business Application template, there is something else going on behind the scenes.  VS is looking for these metadata files and is using them to project your classes out to your SilverLight application so they can be used transparently there, with the same namespace I might add.  I find it helpful to include this generated file in the solution, otherwise your code looks like its broken until you compile:image

Notice the file AnimeManager6.Web.g.cs, this is a generated file which contains a representation of the classes in Silverlight.  The one key thing to remember is that your service class (SeriesService in this case) will get generated as a context class instead (SeriesContext in this case).  So the code to bind a datagrid would look something like this:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
     SeriesContext sc = new SeriesContext();
     sc.Load(sc.GetSeriesQuery());
     myDataGrid.ItemsSource = sc.Series;
}

Note that while this doesn’t appear as such, this is an asynchronous call.  You will find the definition of GetSeriesQuery as GetSeries in your SeriesService method as it is auto generated.  The result is returned, in this case, the Series property of the context, if it where a list of a different type it would likely go there.  This means that attempting to step through this in real time is useless, more often then note you will get an empty set from Series.

As this object is right now, attempting to bind to either our Studio reference or Genres collection would not yield anything. You have to include these data points via the Include when you query the context, and in the case of Studio, mark it with the Include attribute to get VS to project it to the SilverLight class representations.

So lets first take care of the Studio reference, open up your metadata file and add the [Include] attribute to the reference:image

You will note that we are also applying it to the SeriesGenres property, more on this in one second.  With this change in place, update your XAML to look for the Studio navigation property and access its Name property: ie {Binding Studio.Name}

Now this is all good and fine and you may be thinking about extending the projected version of Series in the SilverLight project to include custom properties; don’t, this doesn’t work. I am not entirely sure why, but I will show you how to extend the class and in my mind its a better way anyway.

The best way to add these custom properties is by extending the generated models themselves.  In this case, this is the code I used to create a property for Series which lists out its Genres in a comma delimited list:

public partial class Series
{
    [DataMember]
    public string GenreDisplay
    {
        get
        {
            if (SeriesGenres.Count > 0)
            {
                var sb = new StringBuilder();
                foreach (var genre in SeriesGenres)
                {
                    sb.Append(genre.Genre.Name);
                    sb.Append(", ");
                }

                return sb.ToString().Substring(0, sb.ToString().Length - 2);
            }

            return "No Genres";
        }
    }
}

The key thing here is the [DataMember] attribute, which causes it to be included in the serialization down to the client.  By doing this, it is automatically available to use in the XAML.  Bare in mind that as this being done purely on the server before the object is sent down, it is ONLY reliant on either the EntityContext being open or Include being used when making the query.  It doesn’t not require the objects themselves to use the [Include] attribute.

This gives you flexibility in the design of the class.  It is my opinion, that the generated code is sealed for a reason and modifying the metadata file is likely not a good idea, as this may be something you want to regenerate over time.  However, the other effect of using the [DataMember] attribute will cause VS to project the property out to the SilverLight project automatically.

I hope this helps out, getting this information took me some time and drove me nuts in getting it to work.

Wednesday, September 09, 2009

Performing ASP .NET Submits with Coolite Form Panel Events

As with anything in programming there are a number of ways to do any operation, sending data to server side code in Coolite is no exception.  One of the ways that I have found very useful is via the OnEvent configuration for an AjaxEvent.

To begin with one needs to understand that a FormPanel is not a representation of the <form> tag from HTML, but rather similar to the UpdatePanel control from ASP .NET Ajax.  AjaxEvents are then like triggers.  However, since this trigger could reside anywhere on the page we need a way to “link” the panel viewstate to the trigger so the ViewState is transmitted and we get the desired information that we need.  This is done via the FormID attribute on both the AjaxEvent and the FormPanel.  In addition, fields that are to be transmitted seem to require a Layout, meaning they cannot simply be placed adhoc on the page.  Thus the initial HTML fragment looks something like this:

<ext:FormPanel ID="fpMain" runat="server" FormID="mainForm"
    ButtonAlign="Right" Title="My Form">
    <Body>
    </Body>
</ext:FormPanel>

I am only defining a few of the essential properties here.  As I said earlier, FormID helps Coolite identify which ViewState set to submit as part of the AjaxEvent.  All Panels in Coolite have a section dedicated for buttons, we can use the ButtonAlign property align those buttons as we wish.

For the next segment I will define a could fields using the FormLayout control. Understand that this Layout in particular has a lot of extra functionality and uses, I am not going to show case all of them here.  I will talk about it later on, for now I would advise visiting http://examples.coolite.com and looking under the ‘Layouts’ section.

<ext:FormPanel ID="fpMain" runat="server" FormID="mainForm"
    ButtonAlign="Right" Title="My Form">
    <Body>
        <ext:FormLayout runat="server" LabelSeparator=":"
            LabelAlign="Left">
            <ext:Anchor Horizontal="95%">
                 <ext:TextField ID="txtName" runat="server"
                     FieldLabel="Name" />
            </ext:Anchor>
            <ext:Anchor Horizontal="95%">
                 <ext:NumberField ID="nbrAge" runat="server"
                    FieldLabel="Age" MaxLength="3" />
            </ext:Anchor>
        </ext:FormLayout>
    </Body>
    <Buttons>
        <ext:Button ID="btnSubmit" runat="server" Text="Submit"
            Icon="Accept">
            <AjaxEvents>
                <Click OnEvent="btnSubmit_Click" FormID="mainForm">
                    <EventMask ShowMask="true" Msg="Submitting Data"
                        CustomTarget="fpMain" Target="CustomTarget" />
                </Click>
            </AjaxEvents>
        </ext:Button>
    </Buttons>
</ext:FormPanel>

So I have defined here two form fields within FormLayout one holds a normal textfield and the other a numbers only field.  To finish this implementation we need to define the btnSubmit_Click event handler in the code behind file.  The implementation I will use is:

protected void btnSubmit_Click(object sender, AjaxEventArgs ev)
{
    Ext.Msg.Alert("Alert",
        string.Format("Welcome {0} (Age {1}). Today is {2:d}",
            txtName.Text, nbrAge.Text, DateTime.Now))
    .Show();
}

Admitting, this is a very simple concept of what a server side event could do; it simply shows a Coolite style modal that is designed to substitute the standard JavaScript alert window.  Here we are basically telling the user the information they entered as well as attaching a server side piece of data (the current date).  You can image business logic being here for adding this data to a persistent store, such as a database. I will save another blog entry to talk about validating this data since Coolite can take care of a great deal of that for you.

One other interesting thing to note, and is something that I have observed. When you start getting into these sorts of scenarios where you are using these events, you will want to be conscious of the fact that even though Coolite states the <form runat=”server”> tag is not needed in the little docs they have, this is false.  You will find this very true when you start trying to submit changes to a grid or pick up what rows of a grid where selected. I will have an example of this next.

The key thing here is to understand the connection between FormPanel and how to send data from the client to the server is a way that is more familiar to ASP .NET web programmers.  Remember to mindful of the view state you are including and you can always validate things are working by checking the state of form fields on the server, I still do this at the onset, just to be sure I know what I am submitting.

Monday, September 07, 2009

Using .NET Extension Methods for Type Conversion Libraries

One of my favorite features in Microsoft .NET is extension methods, primarily because of the clean syntax they provide for formatting and perform special operations against types.  I never really never did like static helper methods like this:

DecimalHelper.FormatAsMoney(decimalValue);

I just felt that this was not clean and felt more procedural then it should in an object oriented language like .NET.  Nonetheless, it satisfied the need of being able to centralize logic for dealing with certain common conditions, least of all Type conversion (ala the Convert class).  With the advent of the LINQ syntax in .NET 3.5 Microsoft provided support for using extension methods.  These superficially attached methods are my favorite feature in .NET as they are able to achieve the same result as the helper methods above, except with a much cleaner syntax in my opinion:

decimalVaue.AsMoney();

So I started thinking, this would be very useful for creating “smart parsers” for converting types, in particular strings to various data types, a common and often tedious exercise in web applications.  Smart parsers encapsulate the calls to TryParse and allow the method call to return a standard value on fail or, with an overload, return a default value.  This is an example of my AsInt smart parser function:

public static int AsInt(this string s)
{
    int d;
    return !int.TryParse(s, out d)
        ? int.MinValue : d;
}

public static int AsInt(this string s, int defValue)
{
    int d = s.AsInt();
    return d == int.MinValue
        ? defValue : d;
}

I always did hate calls to Convert and TryParse cause I felt the code was dirty and parsing in general sometimes needs to aware of Globalization settings.  As a result of the success I have had using this on many enterprise projects, I have made it a standard that I implement.  I decided recently, at the advisement of a co-worker to publish something. Its not much, but its a few of the standard methods that I work with.  A lot of these sort of methods are born out of a need to create consistency in the application, mostly related to formatting.

Below is the link to download what I currently have developed (along with some unit tests).  I hope to have this grow into a full fledged type conversion and formatting library.

Friday, September 04, 2009

Grouping with Coolite GridPanel

One of the nicest things about Coolite is that you can easily get a very modern looking website in short order, provided you take the time to understand the examples and ExtJS documentation, which maps pretty closely to the controls.  But there are still little niches you need to know to get the controls to work how you want.  One of the most finicky controls, I have found is the GridPanel.

So much interactivity can be achieved these days via JSON and grids it really is breathtaking when you pull it off.  Coolite is no exception, in fact, when configured properly, it has one of the must full featured grids I have seen on the web, once you get there that is. I will talk about the grid a lot in posts to come, primarily about databinding, and updating.  But today we will focus on grouping.

Grouping is an effect where by a top level is rendered based on data groups, these can be expanded to show the children of that group along with aggregation data.  Today I am going to explain how to take a simple array of data and generate a grid panel which supports grouping.  To begin, here is some sample data we will feed our grid:

var aList = new List<Order>()
{
    new Order() { OrderId = 100, ProductId = 3,
        ProductName="Product 3", UnitPrice = 5.00m,
        Quantity = 21 },
    new Order() { OrderId = 100, ProductId = 6,
        ProductName="Product 6", UnitPrice = 1.00m,
        Quantity = 22 },
    new Order() { OrderId = 100, ProductId = 7,
        ProductName="Product 7", UnitPrice = 2.00m,
        Quantity = 23 },
    new Order() { OrderId = 200, ProductId = 3,
        ProductName="Product 3", UnitPrice = 3.00m,
        Quantity = 24 },
    new Order() { OrderId = 300, ProductId = 4,
        ProductName="Product 4", UnitPrice = 4.00m,
        Quantity = 25 },
    new Order() { OrderId = 400, ProductId = 5,
        ProductName="Product 5", UnitPrice = 5.00m,
        Quantity = 26 },
    new Order() { OrderId = 500, ProductId = 8,
        ProductName="Product 8", UnitPrice = 6.00m,
        Quantity = 27 },
    new Order() { OrderId = 500, ProductId = 9,
        ProductName="Product 9", UnitPrice = 7.00m,
        Quantity = 28 },
    new Order() { OrderId = 500, ProductId = 1,
        ProductName="Product 1", UnitPrice = 8.00m,
        Quantity = 29 },
    new Order() { OrderId = 200, ProductId = 2,
        ProductName="Product 2", UnitPrice = 9.00m,
        Quantity = 20 },
    new Order() { OrderId = 700, ProductId = 3,
        ProductName="Product 3", UnitPrice = 1.00m,
        Quantity = 21 },
    new Order() { OrderId = 700, ProductId = 4,
        ProductName="Product 4", UnitPrice = 2.00m,
        Quantity = 22 },
    new Order() { OrderId = 800, ProductId = 4,
        ProductName="Product 4", UnitPrice = 3.00m,
        Quantity = 23 }
};

Ok, maybe that was a bit excessive, but moving on.  Coolite emphasis a UI pattern I like to call “late loading”. The basics of this pattern is that the main frame of the page should come in first and then the data. This gives the user the impression of a page load and lightens the load. This technique is commonly employed with data heavy applications which utilize a single page architecture and simply move data (usually in JSON) to and from the server using asynchronous calls.

My favorite way of getting data with Coolite are ASP .NET Generic Handlers (.ashx), mainly because they are extremely lightweight and have some very nice uses with GridPanels. This is the code for the ASHX, notice the use of the NewtonSoft JSON Library (this is included with the Coolite download) to serialize the List into JSON and pass it back, also notice the content type definition, to text/json.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class DataHandler : IHttpHandler
{
        public void ProcessRequest(HttpContext context)
        {
            List<Order> list = Order.GetData();

            context.Response.ContentType = "text/json";
            StoreResponseData data = new StoreResponseData();
            data.Data = JSON.Serialize(list);
            data.Return();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
    }
}

What this will do is return a JSON object to be read by a Coolite ExtJS data store and then bound to a control.  In this case, the data that is being returned can easily be grouped by its OrderId property. We can have Coolite take care of most the heavy lifting for us.  So we will first define our data store:

   1:  <ext:Store ID="store" runat="server" AutoLoad="true"
   2:      AutoDataBind="true" GroupField="OrderId">
   3:      <Proxy>
   4:          <ext:HttpProxy DisableCaching="true" Json="true"
   5:              Method="GET" Url="/DataHandler.ashx" />
   6:      </Proxy>
   7:      <Reader>
   8:          <ext:JsonReader>
   9:              <Fields>
  10:                  <ext:RecordField Name="OrderId" />
  11:                  <ext:RecordField Name="ProductId" />
  12:                  <ext:RecordField Name="ProductName" />
  13:                  <ext:RecordField Name="UnitPrice" />
  14:                  <ext:RecordField Name="Quantity" />
  15:                  <ext:RecordField Name="Total" />
  16:              </Fields>
  17:          </ext:JsonReader>
  18:      </Reader>
  19:  </ext:Store>

Ok there is a lot here so we will go line by line.  First we are defining the Store and saying that we want it to AutoLoad which means a request will be made as the page becomes ready.  The key here is the GroupField definition which is what will format the outgoing result in a way that the GridPanel will use a grouping format to output it.

The connections to the data sources are initiated through Proxies.  The interesting thing here is that you can also define a WriteProxy to handle changes to Store via the Grids inline editing feature.  In this tutorial we are only going to be covering using a ReadProxy:

  • DisableCaching: This prohibits the browser from caching a request.
  • Json: This just ensures that what we get back from the request is JSON, I define this just to be explicit with what is coming back
  • Method: Once again this overloads the default value, and just explicitly states that we wish this request to be carried out over GET
  • Url: The URL of where to make the request to, in this case we are specifying the URL to our custom ASHX

The next part is the Reader definition; this is defining what the JSON object is going to look like on the client side as it is worked with on the client side.  Make sure the values of the Name attributes match their respective property values of on the JSON object coming over.  I mistakenly thought this was the order of serialization, but it is not; they match one to one.  You can optionally define the Type attribute, but for the most part Coolite will infer the types.

With this defined, we are now ready to move onto defining our GridPanel which will actually provide the UI for the user to see.  The definition is rather long, so I will be breaking this apart based on the internal nodes for definition, to start here is the outer structure which has the base property definitions:

<ext:GridPanel ID="gpPanel" runat="server" AutoHeight="true"
    StripeRows="true" StoreID="store" AutoDataBind="true">
</ext:GridPanel>

The important attributes here StoreID (which defines which store this control is associated with), also AutoDataBind which will automatically rebind the grid whenever a change is detected in the associated store.  The other attributes are defined for aesthetic purposes.

The next thing to do is to define how the grid will look without Grouping and what columns will be included, to do this we define a ColumnModel node within the GridPanel:

<ColumnModel runat="server">
    <Columns>
        <ext:Column DataIndex="OrderId" />
        <ext:Column DataIndex="ProductName"
            Header="Product" />
        <ext:Column DataIndex="UnitPrice"
            Header="Price">
            <Renderer Format="UsMoney" />
        </ext:Column>
        <ext:Column DataIndex="Quantity"
            Header="Qty" />
        <ext:Column DataIndex="Total">
            <Renderer Format="UsMoney" />
        </ext:Column>
    </Columns>
</ColumnModel>

You notice the DataIndex matches the value of the Name attribute from the JsonReader RecordFields.  Understand that the column model defines ONLY what fields from the JSON object you want to display.  The row will STILL contain a reference to the whole JSON object, but this way you can choose to hide fields like PKs (We are doing this with ProductId in this case), but they are still usable when a GridCommand is invoked.

Notice also that we are using the built in Render formatting that is provided by Coolite.  There are quite a few of them that come out of the box, in addition you can define your own.

So if you run this code right now, you will get a simple grid, but you will still not see any groupings, this is because we need to define a GroupingView node for this GridPanel to see the grouping in action.

<View>
    <ext:GroupingView ID="gvGrouping" runat="server"
        ForceFit="true" HideGroupedColumn="true">
    </ext:GroupingView>
</View>

Views are an extremely vital piece to the GridPanel as they can help you define further configuration in terms of function and formatting.  Here we are using the GroupingView which defines how the Grid will look when it is grouping.  We define two attributes for aesthetical purposes: ForceFit and HideGroupedColumn.  ForceFit merely forces the Grid to fit its entire available width, it tends to make the grid look a lot nicer.  HideGroupedColumn hides the column you are grouping on.

Running the code now you will see the GridPanel and your data grouped by the OrderId.  The blocks are expandable and collapsible, and their initial state can be defined on the GroupingView.  The GridPanel offers an immense amount of functionality and can really help you display your data.  It also best demonstrates ExtJs reliance on the “late-load” content loading technique.  Your controls will be rendered and the data will be brought in via Store’s that in most cases will be linked to a web service of some kind.  This data is then placed into either an Array, JSON, or XML, depending on what you define. In our example, we used JSON.  We associated this Store with a Grid via the StoreID attribute of the GridPanel.  In addition, we define the Grouping column for the Store, this is what enables our grouping.

Finally, we use a ColumnModel and View to define how the Grid will look under certain situations.  Remember that you do not have to define a column for all members of your JSON object, only the ones you want to display, sending a row command will still give you access to all the properties defined on the object via the JSON reader.