Chris's profileSwain's WorldPhotosBlogListsMore Tools Help

Blog


    November 16

    The ASP Ascension project: Building a Bridge between ASP.NET and the Silverlight Client

    I recently found myself wanting to add some rich Silverlight user experience to an ASP.NET site, but I also wanted to be able to keep working with the ASP.NET page framework.  Quickly I realized that there isn't an easy way to make a Silverlight application in a page interact with ASP.NET via the normal PostBack/CallBack methods.  After searching the web for a solution and coming up empty, I decided to take on the project myself.  The ASP Ascension project is the result.  The project will be available on CodePlex just as soon as I can get it cleaned up. 

    What is the ASP Ascension Project?

    Taking ASP.NET to a higher plane of existence!
    The purpose of the ASP Ascension project is to provide a framework to help developers bridge the gap between the Silverlight client and the ASP.NET framework. It is a set of ASP.NET AJAX server controls along with some Silverlight helper classes and controls that enable developers to quickly build Silverlight applications that can communicate with the ASP.NET framework.

    The Goal...

    The primary goal is to provide a framework to enable developers to quickly and easily bridge the gap between the Silverlight client layer and the ASP.NET layer. This includes firing ASP.NET events via postback or callback from the Silverlight client as well as basic data binding in the Silverlight client to data in the ASP.NET page.

    The secondary goal is to create a set of rich UI controls to provide an enhanced user experience in normal ASP.NET pages. Imagine if a developer could provide the richness of Silverlight with the ease and familiarity of ASP.NET. I want to make that possible. There might be instances where it would be advantageous to replace normal ASP.NET controls (Buttons, Navigation UI Elements, Data Lists, etc.) with the richness of Silverlight while maintaining the ability to interact with the ASP.NET page.

    For Starters...

    To start with, the project contains a few base classes to encapsulate the ability to maintain the Silverlight client state across PostBacks and any other base functionality that will help in developing more specialized ASP.NET Silverlight enabled controls.  The current version of the project also contains a control called the GenericControl that gives developers the ability to easily wire up a custom Silverlight application to the ASP.NET Framework by firing PostBacks or CallBacks in response to events that happen in the Silverlight client. 

    Coming up...

    What I would like to provide next is a set of useful Silverlight client controls that can interact with ASP.NET just like a normal ASP.NET WebControl.  One example might be a Fish-Eye navigation control that can bind to an ASP.NET DataSource and PostBack/CallBack to handle navigation.  Another example could be a DataList or ListView control.  However, it may make sense to start simple and implement some of the controls from the Silverlight Toolkit as ASPAscension controls. 

    If anyone has any recommendations, requests or suggestions, please leave a comment and let me know what you think. 

    Technorati Tags: ,,
    September 14

    The DynamicEntitySetControl

    As promised, here is the demo project containing the new DynamicEntitySetControl.  I've used the control along with the DynamicEntityRefControl in the Details.aspx page for the Order object in the demo website.  Since the demo site uses the Northwind database as it's source, the Order object has an EntitySet property referencing a collection of related Order_Details, and that in turn has a property referencing the Product.  So in the demo page I have a DynamicEntitySetControl with a DataField value of "Order_Details" and as it's DetailsTemplate I have a FormView control that contains a DynamicEntityRefControl.  That control has a DataField value of "Product" and contains various DynamicControls with DataField values referring to properties of the Product. 

    A few things to note about the DynamicEntitySetControl:

    • It is a templated control with two available templates: 
      • DetailsTemplate - A FormView or DetailsView control
      • ListTemplate - A list type control (ListView, GridView, or other DataBoundControl list)
    • It has 2 properties that are intended to be references to the details control (DetailControlID) and the list control (ListControlID) objects contained in the DetailsTemplate and ListTemplate respectively.  These IDs are used by the control to gain a reference to the internal databound controls and set their DataSource properties to the DataField of the DynamicEntitySetControl. 
    • The BeforeDataBind event allows the developer to modify the bound EntitySet before binding occurs.  For example, you could filter customer addresses to only include those addresses marked as Primary. 

    Here is an example of implementation from the demo project:

      1: <tr>
    
      2:     <th>
    
      3:         Order_Details
    
      4:     </th>
    
      5:     <td>
    
      6:         <asp:DynamicControl DataField="Order_Details" Mode="ReadOnly" runat="server" />
    
      7:         <br />
    
      8:         <osiris:DynamicEntitySetControl ID="descOrder_Details" runat="server" 
    
      9:             ListTemplatePath="Order_Details_List.ascx" ListControlID="Order_DetailsList"
    
     10:             DetailsViewControlID="fvOrder_Details" DataField="Order_Details" 
    
     11:             Mode="ReadOnly" ViewMode="Details">
    
     12:             <DetailsTemplate>
    
     13:                 <asp:FormView ID="fvOrder_Details" BorderStyle="None" BorderWidth="0" runat="server" DefaultMode="ReadOnly">
    
     14:                     <ItemTemplate>
    
     15:                         <table class="detailstable">
    
     16:                             <tr>
    
     17:                                 <td>
    
     18:                                     <osiris:DynamicLabel ID="dlUnitPrice" AssociatedControlID="dcUnitPrice" runat="server"/>
    
     19:                                 </td>
    
     20:                                 <td>
    
     21:                                     <asp:DynamicControl ID="dcUnitPrice" DataField="UnitPrice" Mode="ReadOnly" runat="server" />
    
     22:                                 </td>
    
     23:                             </tr>
    
     24:                             <tr>
    
     25:                                 <td>
    
     26:                                     <osiris:DynamicLabel ID="dlQuantity" AssociatedControlID="dcQuantity" runat="server"/>
    
     27:                                 </td>
    
     28:                                 <td>
    
     29:                                     <asp:DynamicControl ID="dcQuantity" DataField="Quantity" Mode="ReadOnly" runat="server" />
    
     30:                                 </td>
    
     31:                             </tr>
    
     32:                             <tr>
    
     33:                                 <td>
    
     34:                                     <osiris:DynamicLabel ID="dlDiscount" AssociatedControlID="dcDiscount" runat="server"/>
    
     35:                                 </td>
    
     36:                                 <td>
    
     37:                                     <asp:DynamicControl ID="dcDiscount" DataField="Discount" Mode="ReadOnly" runat="server" />
    
     38:                                 </td>
    
     39:                             </tr>
    
     40:                             <tr>
    
     41:                                 <td>
    
     42:                                     <osiris:DynamicLabel ID="dlProduct" AssociatedControlID="dcProduct" runat="server"/>
    
     43:                                 </td>
    
     44:                                 <td>
    
     45:                                     <asp:DynamicControl ID="dcProduct" DataField="Product" Mode="ReadOnly" runat="server" />
    
     46:                                 </td>
    
     47:                             </tr>
    
     48:                             <tr>
    
     49:                                 <td colspan="2">
    
     50:                                     <osiris:DynamicEntityRefControl ID="dercProduct" runat="server"
    
     51:                                         DataField="Product" Mode="ReadOnly" BorderWidth="0">
    
     52:                                         <ContentTemplate>
    
     53:                                             <table class="detailstable">
    
     54:                                                 <tr>
    
     55:                                                     <td>
    
     56:                                                         <osiris:DynamicLabel ID="dlProductName" AssociatedControlID="dcProductName" runat="server"/>
    
     57:                                                     </td>
    
     58:                                                     <td>
    
     59:                                                         <asp:DynamicControl ID="dcProductName" DataField="ProductName" Mode="ReadOnly" runat="server" />, 
    
     60:                                                     </td>
    
     61:                                                 </tr>
    
     62:                                                 <tr>
    
     63:                                                     <td>
    
     64:                                                         <osiris:DynamicLabel ID="dlProductUnitPrice" AssociatedControlID="dcProductUnitPrice" runat="server"/>
    
     65:                                                     </td>
    
     66:                                                     <td>
    
     67:                                                         <asp:DynamicControl ID="dcProductUnitPrice" DataField="UnitPrice" Mode="ReadOnly" runat="server" />, 
    
     68:                                                     </td>
    
     69:                                                 </tr>
    
     70:                                             </table>
    
     71:                                         </ContentTemplate>
    
     72:                                     </osiris:DynamicEntityRefControl>
    
     73:                                 </td>
    
     74:                             </tr>
    
     75:                         </table>
    
     76:                     </ItemTemplate>
    
     77:                 </asp:FormView>
    
     78:             </DetailsTemplate>
    
     79:         </osiris:DynamicEntitySetControl>
    
     80:     </td>
    
     81: </tr>
    
     82: 

    And here is the resulting page view:

    image

    As you can see, these two controls offer developers a much greater flexibility when dealing with Dynamic Data custom pages.  Hopefully they will be helpful to more than just myself. 

    You can get the updated demo project and source code here

    September 09

    Coming Soon: The DynamicEntitySetControl

    No sooner did I complete the DynamicEntityRefControl, than I realize the need for a control that could handle EntitySets

    Like the control that inspired it, this control is a templated control based on the decompiled DynamicControl.  It has two templates available for use by the developer:  ListTemplate, and DetailsTemplate.  Either template can contain any control that inherits from DataBoundControl, however, the ListTemplate should probably only contain List type controls (e.g. ListView, GridView, etc.) and the DetailsTemplate should probably only contain Detail oriented controls (e.g. DetailsView, FormView).  The control has properties to allow the developer to specify the ID's of the List control in the ListTemplate and the Details control in the DetailsTemplate.  As with the DynamicEntityRefControl, if the control is not bound to an EntitySet or if both templates are empty, then it functions just like a normal DynamicControl.  I've already got this control working, but I'm not quite ready to post the code here yet.  I want to use it for a couple of days first to get the major kinks worked out, and I'm sure there are some big things missing right now (i.e. handling switching modes from List to Details).  Stay tuned!

     

    September 04

    ASP.NET Dynamic Data has me in its grip again!

    To quote The Godfather, "Every time I try to get away, they keep pulling me back in!"  I can't help it!  ASP.NET Dynamic Data is a great new tool.  The key word being "new".  It packs a lot of punch right out of the gate, but it is still in its infancy.  As such, there are quite a few fairly basic features that are lacking.  As I've been working with Dynamic Data for a project at work, several of its limitations have surfaced.  I've blogged about a few of them in the past, but my latest discovery nearly forced me to return to the basics of ADO.NET data access and chuck Dynamic Data (at least for my current WebForm). 

    The Problem
    When viewing a row of data in a Dynamic Data custom page, you need to show details from a parent record.  For example, the Northwind database has an Orders table that is related to the Customers table (Figure 1).  The entity diagram generates an Order class for you that contains properties matching all of the columns from the Orders table.  It also creates properties for any references to parent or children records.  This means that the Order class has a property that is of type Customer and contains the related Customer object for the Order you are currently viewing.  What you need to do is be able to show the Customer Address information from within the Order Details view page.  However, this is not possible because the DynamicControl does not support nested properties in the DataField attribute (i.e. - DataField="Customer.Address"). 

    Figure 1
    (Figure 1)

    The Solutions
    When I first encountered this problem, I asked around the bloggoshere, and Stephen Naughton came up with a nice generic solution for me that involves creating a custom FieldTemplate.  While his solution was generic and in keeping with the pattern that Dynamic Data follows, it was not quite as flexible as I needed.  I wanted to be able to control the data fields displayed (which the FieldTemplate solution allows), but I also wanted to control the layout.  What I decided I needed was a templated control that would use the EntityRef property as its data source.  Hence, the DynamicEntityRefControl was born! 

    To create the DynamicEntityRefControl, I started by using trusty old .NET Reflector to decompile the existing DynamicControl provided by Dynamic Data.  Now I had a base with which to work to build my templated control.  The DynamicControl inherits from Control and does not implement INamingContainer (something essential to templated controls).  Therefore, I changed the class definition (first changing the name to DynamicEntityRefControl) so it inherits from CompositeControl and implemented INamingContainer (Figure 2). 

    (Figure 2)
        public class DynamicEntityRefControl : CompositeControl, IAttributeAccessor, 
            IFieldTemplateHost, IFieldFormattingOptions, INamingContainer
    </PRE< DIV>

     

    Next, I added a property of type ITemplate called ContentTemplate (Figure 3). 

    (Figure 3)
            [Browsable(false)]
            [DefaultValue(null)]
            [ResourceDescription("DynamicEntityRefControl_ContentTemplate")]
            [TemplateContainer(typeof(DynamicEntityRefControlContent))]
            [PersistenceMode(PersistenceMode.InnerProperty)]
            public virtual ITemplate ContentTemplate
            {
                get
                {
                    return _contentTemplate;
                }
                set
                {
                    _contentTemplate = value;
                }
            }
    </PRE< DIV>

     

    Then, I override the CreateChildControls() method.  I check to see if the data item the control is bound to is an EntityRef object and if the ContentTemplate has controls in it.  If so, then I set the ItemTemplate property of an internal FormView to the ContentTemplate and set the DataSource of the FormView to the Entity that the control is bound to (Figure 4).  You'll notice that I'm instantiating the ContentTemplate into the _content control even though I'm not using it.  This was the only way I could think of to check that the ContentTemplate had content. 

    (Figure 4)
            protected override void CreateChildControls()
            {
                if (this._content == null)
                    this._content = new DynamicEntityRefControlContent();
    
                this.ContentTemplate.InstantiateIn(this._content);
    
                if (this._content.Controls.Count > 0 && this.IsEntityRef)
                {
                    this.Controls.Clear();
    
                    this._formViewControl.ID = this.ID + "_FormViewControl";
                    this._formViewControl.DataSource = this.Entity;
                    this._formViewControl.ChangeMode(GetFormViewMode());
                    this._formViewControl.ItemTemplate = this.ContentTemplate;
    
                    this.Controls.Add(this._formViewControl);
                }
                else
                    base.CreateChildControls();
            }
    </PRE< DIV>

     

    A couple of notable properties I created - IsEntityRef (Figure 5) and Entity (Figure 6). 

    (Figure 5)
            [Browsable(false)]
            public virtual bool IsEntityRef
            {
                get
                {
                    if (this.Column == null)
                        return false;
    
                    AssociationAttribute assocAtt = null;
                    object[] assocAttAry = this.Column.EntityTypeProperty
    .GetCustomAttributes(typeof(AssociationAttribute), true); if (assocAttAry.Length > 0) assocAtt = assocAttAry[0] as AssociationAttribute; if (assocAtt != null && assocAtt.IsForeignKey) return true; else return false; } } </PRE< DIV>

     

    IsEntityRef is a helper property that checks the column the control is bound to for an AssociationAttribute.  If there is an AssociationAttribute and its IsForeignKey property is true, then the column the control is bound to is an EntityRef column. 

    (Figure 6)
            [Browsable(false)]
            public virtual object Entity
            {
                get
                {
                    MetaForeignKeyColumn mfkc = this.Column as MetaForeignKeyColumn;
    
                    if (mfkc == null)
                        return null;
    
                    //get the EntityRef property
                    object entity = mfkc.EntityTypeProperty.GetValue(this.Page.GetDataItem(), null);
    
                    IQueryable query = mfkc.ParentTable.Provider.GetQuery(mfkc.ParentTable.CreateContext());
                    MethodCallExpression expression = LinqExpressionHelper
    .BuildItemsQuery(query,
    mfkc.ParentTable,
    mfkc.ParentTable.PrimaryKeyColumns,
    mfkc.ParentTable.GetPrimaryKeyString(entity).Split(
    ",".ToCharArray())); var result = query.Provider.Execute(expression); if (result == null || !(result is IEnumerable)) return result; ArrayList returnValue = new ArrayList(); foreach (object o in ((IEnumerable)result)) { returnValue.Add(o); } return returnValue; } } } </PRE< DIV>

     

    The Entity property first sets a local variable as a MetaForeignKeyColumn.  This allows us to get a reference to the ParentTable.  Next, it gets an instance of the entity from the current data item of the Page.  Now it can build a query to get the Enity from the ParentTable using the LinqExpressionHelper class BuildItemsQuery() method.  Once we have an expression to use, we can execute the query on the ParentTable.  Finally, we add the results of the query to an ArrayList to be used as the DataSource for our internal FormView.  The reason I had to add the results to a new ArrayList rather than just returning the results of the query has to do with an error I was getting when I tried to just use the IEnumerable results.  For some reason it only wanted to allow me to enumerate the results once, but the nested DynamicControls in the template wanted to enumerate multiple times.  If you take the results and put them into a new list, the problem goes away. 

    The Demo
    To demonstrate how this is useful, I've created a simple Dynamic Data website based on the Northwind database.  The simple demonstration of the DynamicEntityRefControl can be seen on the Details View of the Orders page.  Since an Order directly relates to a Customer, the Order class has an EntityRef property called Customer.  This means we can use the DynamicEntityRefControl with its DataField set to Customer, and we can add child DynamicControls to the ContentTemplate to display information from the related Customer entity. 

    Figure 7 shows the declaration of the control in the Details.aspx custom page for the Order entity.  It displays the link to view the Customer Details View page, but it also displays a table containing the Customer Address, City, Region, and PostalCode.  If we wanted to, we could show the entire set of Customer data fields all on the Order Details View page.  I haven't tested it yet, but theoretically, you could display nested related data n levels deep. 

    (Figure 7)
    <tr>
        <th>
            Customer
        </th>
        <td>
            <asp:DynamicControl DataField="Customer" Mode="ReadOnly" runat="server" />
            <br />
            <cc1:DynamicEntityRefControl ID="DynamicEntityRefControl1" runat="server"
                DataField="Customer" Mode="ReadOnly">
                <ContentTemplate>
                    <table>
                        <tr>
                            <td>
                                <asp:DynamicControl ID="DynamicControl1" 
    DataField="Address" Mode="ReadOnly" runat="server" /> </td> </tr> <tr> <td> <asp:DynamicControl ID="DynamicControl2"
    DataField="City" Mode="ReadOnly" runat="server" />, <asp:DynamicControl ID="DynamicControl3"
    DataField="Region" Mode="ReadOnly" runat="server" /> <asp:DynamicControl ID="DynamicControl4"
    DataField="PostalCode" Mode="ReadOnly" runat="server" /> </td> </tr> </table> </ContentTemplate> </cc1:DynamicEntityRefControl> </td> </tr> </PRE< DIV>

     

    The Result
    image

    The Code
    You can download the demo project here.  It includes the DemoWeb site, the DynamicDataFutures project, the Osiris.DynamicData.Extensions project (a helper project that contains various extension methods and custom attribute definitions), and the Osiris.Controls project (contains the DynamicEntityRefControl among other helpful Dynamic Data related controls that would probably warrant their own blog posts each). 

    Technorati Tags: ,

    August 16

    Back to work on Tank Wars

    Finally found some time to do a little work on the Tank Wars game.  When we last left off, I was working on the authentication system for the application.  I had created a login form and I was using the custom Principal and Identity objects created by Jeremy Brown and the ASP.NET Authentication services (I used Brad Abrams' code as a guide). 

    The ASP.NET Authentication services are nice for logging in a user and getting the user's roles, but they fall short in the area of registering a new user.  They simply don't have that capability.  However, since Silverlight is meant to run in the browser, all you really need to do is redirect app users to an ASP.NET registration page where they can create a username and password and be sent back to the application to continue as an authenticated user.  The ASP.NET quickstarts can show you how to create a registration page if you don't already know. 

    While I really likes the code for the Silverlight Principal and Identity objects that I got from Jeremy, I wanted a way to detect changes to the status of a login so I could enable/disable controls as needed.  This is all possible with the controls as he created them just by attaching to the PrincipalValidationRequestCompleted and PrincipalLogoutCompleted events, but I wanted something a little cleaner.  What I came up with was a LoginStatusChanged event. 

    This new event has custom event arguments that contain a Status property.  The Status property is an enum value of LoggedOut, Validating, LoginFailed, or LoggedIn.  This allows me to attach to this event when a control is created and enable/disable any child controls I want from within the LoginStatusChanged event handler. 

    Now, when a control in my app is initialized and it contains controls that depend on the current user's login status and roles I simply set the initial state of those controls in the constructor, then modify their states in the LoginStatusChanged event handler. 

    Below is my modified version of the SilverlightPrincipal along with the custom LoginStatusEventArgs:

    Code Snippet
        public class SilverlightPrincipal : IPrincipal
        {
            #region Private Members
    
            SilverlightIdentity silverlightIdentity;
            List<string> roles;
    
            RoleServiceClient roleProxy;
            
            #endregion
    
            private event EventHandler PrincipalValidationRequestCompleted;
            private event EventHandler PrincipalLogoutCompleted;
    
            public event EventHandler<LoginStatusEventArgs> LoginStatusChanged;
    
            public SilverlightPrincipal()
            {
                silverlightIdentity = new SilverlightIdentity();
                silverlightIdentity.IdentityValidationRequestCompleted += silverIdentity_IdentityValidationRequestCompleted;
                silverlightIdentity.IdentityLogoutCompleted += silverlightIdentity_IdentityLogoutCompleted;
                roles = null;
                roleProxy = new RoleServiceClient();
                roleProxy.GetRolesForCurrentUserCompleted += roleProxy_GetRolesForCurrentUserCompleted;
    
                PrincipalValidationRequestCompleted += new EventHandler(SilverlightPrincipal_PrincipalValidationRequestCompleted);
                PrincipalLogoutCompleted += new EventHandler(SilverlightPrincipal_PrincipalLogoutCompleted);
            }
    
            void SilverlightPrincipal_PrincipalLogoutCompleted(object sender, EventArgs e)
            {
                if (LoginStatusChanged != null)
                {
                    LoginStatusChanged(this, new LoginStatusEventArgs(LoginStatusEventArgs.LoginStatus.LoggedOut));
                }
            }
    
            void SilverlightPrincipal_PrincipalValidationRequestCompleted(object sender, EventArgs e)
            {
                if (LoginStatusChanged != null)
                {
                    LoginStatusEventArgs eventArgs;
    
                    if (this.silverlightIdentity.IsAuthenticated)
                    {
                        eventArgs = new LoginStatusEventArgs(LoginStatusEventArgs.LoginStatus.LoggedIn);
                    }
                    else
                    {
                        eventArgs = new LoginStatusEventArgs(LoginStatusEventArgs.LoginStatus.LoginFailed);
                    }
                    LoginStatusChanged(this, eventArgs);
                }
            }
    
            void silverlightIdentity_IdentityLogoutCompleted(object sender, EventArgs e)
            {
                roles = null;
                if (PrincipalLogoutCompleted!=null)
                {
                    PrincipalLogoutCompleted(this,new EventArgs());
                }
            }
    
            void silverIdentity_IdentityValidationRequestCompleted(object sender, EventArgs e)
            {
                if (this.silverlightIdentity.IsAuthenticated)
                {
                    roleProxy.GetRolesForCurrentUserAsync();
                }
                else
                {
                    if (PrincipalValidationRequestCompleted != null)
                    {
                        PrincipalValidationRequestCompleted(this, new EventArgs());
                    }
                }
            }
    
            public void Validate(string userName, string password)
            {
                this.silverlightIdentity.Validate(userName, password);
    
                if (LoginStatusChanged != null)
                {
                    LoginStatusChanged(this, new LoginStatusEventArgs(LoginStatusEventArgs.LoginStatus.Validating));
                }
            }
    
            public void LogOut()
            {
                this.silverlightIdentity.LogOut();
            }
    
            void roleProxy_GetRolesForCurrentUserCompleted(object sender, GetRolesForCurrentUserCompletedEventArgs e)
            {
                if (e.Error == null)
                {
                    roles = new List<string>(e.Result);
                }
                
                if (PrincipalValidationRequestCompleted != null)
                {
                    PrincipalValidationRequestCompleted(this, new EventArgs());
                }
            }
    
            #region IPrincipal Members
    
            public IIdentity Identity
            {
                get { return silverlightIdentity; }
            }
    
            public bool IsInRole(string role)
            {
                bool isInRole = false;
                if (roles != null)
                {
                    isInRole = roles.Contains(role);
                }
    
                return isInRole;
            }
    
            #endregion
        }
    
        public class LoginStatusEventArgs : EventArgs
        {
            public enum LoginStatus
            {
                LoggedOut,
                Validating,
                LoginFailed,
                LoggedIn
            }
    
            private LoginStatus status = LoginStatus.LoggedOut;
    
            public LoginStatus Status 
            {
                get
                {
                    return status;
                }
            }
    
            public LoginStatusEventArgs(LoginStatus status)
            {
                this.status = status;
            }
        }
    </PRE< DIV>

     

    The first thing I did was to make the PrincipalValidationRequestCompleted and PrincipalLogoutCompleted events private and just use them inside the class in order to fire the LoginStatusChanged event with the correct LoginStatusEventArgs passed into it.  This just felt a little cleaner to me than having so many events publicly exposed. 

    This is a snippet of how the code is used in a control to enable/disable some buttons:

    Code Snippet
            void CurrentPrincipal_LoginStatusChanged(object sender, LoginStatusEventArgs e)
            {
                switch (e.Status)
                {
                    case LoginStatusEventArgs.LoginStatus.LoggedIn:
                        JoinBattleButton.IsEnabled = true;
                        CreateBattleButton.IsEnabled = true;
                        break;
                    case LoginStatusEventArgs.LoginStatus.Validating:
                    case LoginStatusEventArgs.LoginStatus.LoginFailed:
                    default:
                        JoinBattleButton.IsEnabled = false;
                        CreateBattleButton.IsEnabled = false;
                        break;
                }
            }
    </PRE< DIV>

     

    As a side note, I created an ISecurity interface to ensure that any controls that will be checking the Silverlight Principal or Identity objects at the current application level implement a property called CurrentPrincipal that looks like:

    Code Snippet
            #region ISecurity Members
    
            public SilverlightPrincipal CurrentPrincipal
            {
                get { return (Application.Current as App).CurrentPrincipal; }
            }
    
            #endregion
    </PRE< DIV>

     

    Finally, in the constructor for my Start control I attach to the LoginStatusChanged event and make a call to a private method that initializes control states based on permissions or authentication. 

    Code Snippet
            public Start()
            {
                InitializeComponent();
                CreateBattleButton.Click += new RoutedEventHandler(CreateBattleButton_Click);
                JoinBattleButton.Click += new RoutedEventHandler(JoinBattleButton_Click);
                CurrentPrincipal.LoginStatusChanged += new EventHandler<LoginStatusEventArgs>(CurrentPrincipal_LoginStatusChanged);
    
                InitializeControlPermissions();
            }
    
            private void InitializeControlPermissions()
            {
                JoinBattleButton.IsEnabled = CurrentPrincipal.Identity.IsAuthenticated;
                CreateBattleButton.IsEnabled = CurrentPrincipal.Identity.IsAuthenticated;
            }
    </PRE< DIV>

     

    What's next for the Tank Wars game?  Now that the users can login to the app, they need a way to either create a battle and wait for an opponent, or join an existing battle created by someone else.  This is what I hope to be blogging about next.  Stay tuned...

    August 08

    String Extension Method - RemoveWhiteSpace() and More...

    I was writing some code the other day where I needed to remove all of the white space characters from a string in several places.  Having recently been exposed to the new .NET 3.0 ability to add Extension Methods, I decided this would be a good opportunity to use this new feature.  For those who haven't heard of extension methods, .NET 3.0 and above gives you the ability to add helper methods to existing classes (including existing .NET Framework classes).  This means that I can create a RemoveWhiteSpace method for the String class.  But, would it be wise in this case to create an extension method, or is a simple helper method better?

    The process:
    The first thing I did was start by replacing a few white space characters (including HTML like "&nbsp;") directly in my code in the one place I needed this functionality.  But, the list kept getting longer and longer.  Then, I found a few more places where I needed to use this code, so I create a helper method that accepted a string and returned a cleaned up string.  Eventually, I realized how much cleaner my code would be if instead of doing:

    string newString = RemoveWhiteSpace(oldString);
    //do something with the newString.

    I could just do:

    oldString.RemoveWhiteSpace(); // just a little cleaner and readable.

    So, my first version of the RemoveWhiteSpace extension method was just a copy of the helper method I created previously.  However, I realized in the process of debugging that my list of white space characters to search for was incomplete.  Then I stumbled on an internal static field in the .NET Framework String class that allowed me to get the complete list of white space characters.  The String class has a static field that is not publicly available called WhitespaceChars that returns an array of characters (Char[]).  If only we had some magical way of accessing this non-public field...  Reflection to the rescue!

    The good stuff:
    So here is the code for the extension method.  All in all, it's really simple code.  Three things are needed to create an extension method:

    1. Create a static class
    2. Add a static method to the class
    3. Use the "this" keyword before the first parameter type to indicate that the extension method will apply to the type
      • declaration:  public static MyMethod(this string s)
      • usage: "Hello World".MyMethod();

    In addition to the RemoveWhiteSpace method, I created a public static property to expose the reflected value of WhitespaceChars in the String class.  I think both are useful and I hope they help someone else. 

    public static class StringExtensions
    {
        public static string RemoveWhiteSpace(this string s)
        {
            string newString = s;
            foreach (char c in WhiteSpaceChars)
            {
                newString = s.Replace(new String(new char[] { c }), String.Empty);
            }
    
            //string newString = s.Replace("&nbsp;", "").Replace("&#160;", "").Replace(" ", "").Replace("\t", "").Replace("\r", "").Replace("\n", "").Replace(Environment.NewLine, "");
            return newString;
        }
    
        public static Char[] WhiteSpaceChars
        {
            get
            {
                return "".GetType().GetField("WhitespaceChars", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null) as Char[];
            }
        }
    }
    August 04

    Distracted by Dynamic Data

    Lately my work has kept me so busy I haven't had time to do much with Tank Wars and Silverlight in general.  However, my work project is out on the fringe of Microsoft's latest technology using ASP.NET Dynamic Data.  There are lot's of posts out there on the basics of this new approach at data driven web site development, so I won't bother to cover them.  If you need a place to start, check out the Dynamic Data site. 

    While this is a nice new tool to help us rapidly create data driven web sites, it is still a relatively new tool.  As such, it has some bugs and oversights that have yet to be addressed.  I wanted to cover a couple of these in this post. 

    Problem 1:  I needed to be able to absolutely position my dynamic controls for my edit form.  While most developers these days shy away from this type of positioning on the web, there are times when it comes in handy.  When I tried to do this with the DynamicControl as it exists today, it wouldn't cooperate.  I could set the positioning in the inline-style to absolute, but the designer would ignore it and still would not let me drag the control around the design surface. 

    Solution 1:  Simply place the dynamic control inside a DIV or SPAN tag and absolutely position that instead.  While this works, and it is the easiest thing to do to solve the problem in the short term, if you have a lot of controls to position its probably not viable. 

    Solution 2:  Composite control to the rescue!  What I ended up doing for this project was creating a control that inherits from CompositeControl (something I haven't done since ASP.NET 1.1 I think).  By default, the CompositeControl renders a surrounding SPAN tag so all we need to do is add the DynamicControl as a child and expose its properties.  That's it, we're done!  Here's the code for that control so anyone who needs to absolutely position their DynamicControls will hopefully be saved some time and headaches:

    /// <summary>
    /// Summary description for AdvancedDynamicControl
    /// </summary>
    [Designer(typeof(AdvancedDynamicControlDesigner))]
    [DefaultProperty("ID")]
    [ToolboxData("<{0}:AdvancedDynamicControl runat=\"server\"></{0}:AdvancedDynamicControl>")]
    public class AdvancedDynamicControl : CompositeControl, IFieldTemplateHost, IFieldFormattingOptions
    {
        private DynamicControl dynamicControl;
    
        public AdvancedDynamicControl()
        {
            dynamicControl = new DynamicControl();
        }
    
        public AdvancedDynamicControl(DataBoundControlMode mode)
        {
            dynamicControl = new DynamicControl(mode);
    
            EnsureChildControls();
        }
    
        protected override void CreateChildControls()
        {
            dynamicControl.ID = this.ID + "_DynamicControl";
            Controls.Add(dynamicControl);
            base.CreateChildControls();
        }
    
        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                return HtmlTextWriterTag.Span;
            }
        }
    
        // Methods
        public string GetAttribute(string key)
        {
            return dynamicControl.GetAttribute(key);
        }
        public void SetAttribute(string key, string value)
        {
            dynamicControl.SetAttribute(key, value);
        }
    
        // Properties
        [MergableProperty(false), ParenthesizePropertyName(true), Filterable(false), Themeable(false)]
        public string DynamicControlID
        {
            get
            {
                return dynamicControl.ID;
            }
        }
        [Category("Behavior"), DefaultValue(false)]
        public bool ApplyFormatInEditMode
        {
            get
            {
                return dynamicControl.ApplyFormatInEditMode;
            }
            set
            {
                dynamicControl.ApplyFormatInEditMode = value;
            }
        }
        [Browsable(false)]
        public MetaColumn Column
        {
            [CompilerGenerated]
            get
            {
                return dynamicControl.Column;
            }
            [CompilerGenerated]
            set
            {
                dynamicControl.Column = value;
            }
        }
        [DefaultValue(false), Category("Behavior")]
        public bool ConvertEmptyStringToNull
        {
            get
            {
                return dynamicControl.ConvertEmptyStringToNull;
            }
            set
            {
                dynamicControl.ConvertEmptyStringToNull = value;
            }
        }
        [Category("Appearance"), DefaultValue(""), CssClassProperty]
        public override string CssClass
        {
            get
            {
                return dynamicControl.CssClass;
            }
            set
            {
                dynamicControl.CssClass = value;
            }
        }
        [Category("Data"), DefaultValue("")]
        public string DataField
        {
            get
            {
                return dynamicControl.DataField;
            }
            set
            {
                try
                {
                    dynamicControl.DataField = value;
                }
                catch (Exception ex)
                {
                    Debug.Write(ex);
                }
            }
        }
        [Category("Data"), DefaultValue("")]
        public string DataFormatString
        {
            get
            {
                return dynamicControl.DataFormatString;
            }
            set
            {
                dynamicControl.DataFormatString = value;
            }
        }
        [Browsable(false)]
        public Control FieldTemplate
        {
            [CompilerGenerated]
            get
            {
                return dynamicControl.FieldTemplate;
            }
        }
        [DefaultValue(true), Category("Behavior")]
        public bool HtmlEncode
        {
            get
            {
                return dynamicControl.HtmlEncode;
            }
            set
            {
                dynamicControl.HtmlEncode = value;
            }
        }
        public DataBoundControlMode Mode
        {
            [CompilerGenerated]
            get
            {
                return dynamicControl.Mode;
            }
            [CompilerGenerated]
            set
            {
                dynamicControl.Mode = value;
            }
        }
        [Category("Behavior"), DefaultValue("")]
        public string NullDisplayText
        {
            get
            {
                return dynamicControl.NullDisplayText;
            }
            set
            {
                dynamicControl.NullDisplayText = value;
            }
        }
        IFieldFormattingOptions IFieldTemplateHost.FormattingOptions
        {
            get
            {
                return dynamicControl;
            }
        }
        [Browsable(false)]
        public virtual MetaTable Table
        {
            get
            {
                return dynamicControl.Table;
            }
        }
        [DefaultValue(""), Category("Behavior")]
        public virtual string UIHint
        {
            get
            {
                return dynamicControl.UIHint;
            }
            set
            {
                dynamicControl.UIHint = value;
            }
        }
        [Category("Behavior"), DefaultValue(""), Themeable(false)]
        public virtual string ValidationGroup
        {
            get
            {
                return dynamicControl.ValidationGroup;
            }
            set
            {
                dynamicControl.ValidationGroup = value;
            }
        }
    
        public DynamicControl DynamicControl
        {
            get
            {
                return this.dynamicControl;
            }
        }
    }
    
    public class AdvancedDynamicControlDesigner : CompositeControlDesigner
    {
    }

    Problem 2:  This not so much a problem as it is an expansion of a concept.  In my project I'm also making use of the Dynamic Data Futures project.  This has a few more handy dandy little features to add to the Dynamic Data bag-O-stuff, but the one I liked in particular was the DynamicLabel.  It has ability to let the user associate the label control to a DynamicControl.  This is not possible with the regular Label control because the DynamicControl generates various sub-controls based on the mode and the data type to which it is bound.  So the creators of this control kindly bridged this gap for us by overriding the PreRender method and resetting the AssociatedControlID to the actual control created by the DynamicControl.  This is all well and good, but I needed it to do a little more.  I wanted the DynamicLabel to also set its Text property to the column DisplayName making the DynamicLabel truly dynamic.  Plus, this takes me more in the direction that Dynamic Data is trying to encourage anyway, and that is using the data structure and metadata to determine UI behavior. 

    Solution:  Create my own DynamicLabel control that inherits from the Dynamic Data Futures DynamicLabel and adds my functionality.  It's fairly simple actually.  Here's the code (I believe it is self explanatory). 

    /// <summary>
    /// Summary description for DynamicLabel
    /// </summary>
    [ToolboxData("<{0}:DynamicLabel runat=\"server\"></{0}:DynamicLabel>")]
    public class DynamicLabel : Label
    {
        public override string Text
        {
            get
            {
                if (DesignMode)
                    base.Text = "[DynamicLabel]";
                if (String.IsNullOrEmpty(base.Text.Replace("&nbsp;", "")))
                    base.Text = "[DynamicLabel]";
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }
    
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
    
            if (!DesignMode)
            {
                var dynCtrl = FindControl(AssociatedControlID);
                string adCtrlID = "";
                DynamicControl typedDynCtrl;
                try
                {
                    typedDynCtrl = (DynamicControl)dynCtrl;
                }
                catch
                {
                    typedDynCtrl = ((AdvancedDynamicControl)dynCtrl).DynamicControl;
                    adCtrlID = ((AdvancedDynamicControl)dynCtrl).ID + "$";
                }
    
                if (typedDynCtrl != null)
                {
                    var ftuc = typedDynCtrl.FieldTemplate as FieldTemplateUserControl;
                    AssociatedControlID = adCtrlID + ftuc.ID + "$" + ftuc.DataControl.ID;
    
                    if (String.IsNullOrEmpty(Text.Replace("&nbsp;", "")))
                        Text = typedDynCtrl.Column.DisplayName + ":";
                }
                else
                {
                    if (String.IsNullOrEmpty(Text.Replace("&nbsp;", "")))
                        Text = "[DynamicLabel]";
                }
            }
        }
    }

    Hopefully something like this will make it into the later versions of the Dynamic Data Futures controls, but in the meantime these should help. 

    Disclaimer:  I probably don't need this, but I'll say it anyway - this code is provided "AS IS" with no warranties of any kind. The entire risk arising out of the use or performance of the code is with you.

    July 09

    I'm still here...

    I'm working on the authentication system for the Tank Wars site.  I'm using the System.Web.ApplicationServices web services for user authentication, roles, an profile management.  Not much in the way of working code to show, so for now, here are some screenshots of the progress...

    image

    image

    More to come later.

    June 28

    Silverlight 2 Game Socket Server?

    Since the ultimate goal of my Tank Wars Silverlight 2 game is to allow players to sign in and play an opponent over the Internet, I knew I'd need to build a game server.  Having never done this before, I scoured the web for example code and came up empty.  Well, at least when it comes to C# based game socket servers or command servers, I couldn't find any example code.  I did, however, find a good example of a chat server on Pete Brown's blog.  So, I decided to adapt his code to suit my needs.  I mean, what really is a game server, but a way of accepting content from a client, processing/parsing it, and doing something with that data?  Since this can be done with text, a chat server is a good starting point. 

    Rather than mucking up my Tank Wars project with this test code, I decided to create a simple "proof of concept" project.  This solution consists essentially of three pieces:

    1. Silverlight Client - this project allows the user to use the arrow keys to move an ellipse around the screen.  The client also connects to the socket server and passes the coordinates of the ellipse to any other clients connected. 
    2. Game Server - this project accepts new client connections and receives data from them, then it relays that data to the other connected clients. 
    3. Policy Server - since Silverlight 2 Beta 2, a server of this type is a requirement to allow the client to connect to a server.  This is just a copy of the server that Mike Snow posted on his blog. 

    Disclaimer: This code is just a proof of concept project.  There's a lot that should be done to make this ready to be used in an actual game, but once you see the code you'll know what I mean.

    Download the complete solution here:

     

    Once you build the solution, you should run the PolicyServer.exe and the SocketServer.exe, then run two instances of the test web page.  You'll see that when you move the ellipse around the screen, the ellipse in the second instance of the the test web page moves as well.  You can also see the text received by the client socket that is parsed and used to position the ellipse. 

    Coming Up...

    I need to decide on a data format for my socket server packets.  I'm thinking I might want to quit using text for passing data/commands and figure out something less data intensive.  Expect a post on this once I figure it out. 

    I also need to create a way for the users to register and login.  And I want to allow the users to design a game map or choose from an existing library before starting a new game. 

    June 23

    Tank Wars Demo on Silverlight Streaming!

    Finally, it looks like Silverlight Streaming now supports Silverlight 2 Beta 2 projects!  Here's a demo of the Tank Wars project where it currently stands.  It's not really a game quite yet.  Just a tank that you can drive around (arrow keys) and shoot bullets ("F" key).

    </DIV< div>

    June 22

    Silverlight, MVC, and the Tank Wars Game

    TankWarsProjectScreenshot I had planned on getting the socket server created for the Tank Wars network gaming support for this post, but as I started looking into the code, I realized it needed some refactoring.  So, I decided to implement the MVC pattern for the game.  Why MVC?  I want to try to keep the UI as separated from the logic and state code as possible, and MVC was created to do just that. 

    Since I've never implemented the MVC pattern in a Silverlight 2 application, I did a little Googling to see if there were any examples out there that would give me a jumping off point.  Luckily I found this article by Joel Neubeck where he gives a good example of implementing the MVC pattern for a simple Silverlight game.  Using his example project as my template, I was able to fairly quickly convert my existing code to the new pattern.  The screen shot to the right shows my project structure.

    First off, I created folders for the Controllers, Models, and Views.  For the purposes of this game, I really only need a single controller class to manage all the content.  This is the brain for the application. 

    As for the models, there are four: Game, Player, Bullet and Tile.  Right now I've only got logic in the Game model.  I may decide later to pull more state information out of the Game model and put it in the Player and Bullet models. 

    Finally, we have the Views.  This is where all the UI is defined.  I kept my base classes and just moved them into the BaseClasses folder for better organization.  The Dialogs folder contains the Start and GameOver UI code and any other UI screens such as Options or Login/Register, etc.  Then, we have the familiar Sprites folder that contains our views for our game objects. 

    You may also notice the FarseerPhysics, GameHelpers, and Silverlight.FX projects.  The first two I talked about in this previous post.  The Silverlight.FX project is one I got from Nikhil Kothari's blog about using the ViewModel pattern with Silverlight.  While I'm not using his framework for the exact purpose he intended, it still has some great features like Glitz behaviors to enable various visual effects.  It looks like he's got lots more planned for the framework, so I can't wait to see it finished!

    On top of refactoring the code to implement MVC, I also added a basic start page and restart page.  These pages currently only have a simple button on them, but eventually they will allow the user to choose to login or register, choose a game map, and select various game options.  I've also used the new Shell view to create a structure to show the game status bar at the top of the screen and the game play area below. 

    Now that things are a little better organized, I can think about the networking support for the game.  This is where the fun begins!

    June 15

    Trying My Hand at Game Development with Silverlight 2 Beta 2

    I'm new to Silverlight programming in general and I'm even more of a novice at game development, so it only seems right that I try my hand at both right off the bat!  Maybe the Phoenix heat is starting to get to me?  I can't really say. 

    So I thought I'd start off by writing something fairly simple like the old tank warfare game I remember playing as a kid on the Atari system.  It was a basic game with two tanks that could drive around the screen through a simple maze of walls and try to shoot each other.  You could choose what maze to do battle in and your bullets would bounce off the walls 3 or 4 times so that you could hit your enemy from around corners. 

    I'm not one to figure "everything" out for myself.  I'd much rather let someone else do the work and then borrow what I need for my own purposes.  For this game, I knew I'd need some physics to handle the bouncing of bullets off the walls so I chose to use the Farseer Physics engine.  To implement the engine, I borrowed from Andy Beaulieu's Polygon Physics Demo project.  And finally, I decided to use the GameHelpers project from Silverlight Games 101 to get my basic Keyhandler and GameLoop code.  Much thanks to all of these sites for getting me on my way. 

    Now that all of the credits are out of the way, let's talk about what I've done already and what still needs to be done.  The first thing I did was expand on the SpriteBase class by creating several subclasses:

    TileSpriteBase- This UserControl adds the basic code for a simple tile sprite.

    BackgroundTileBase - This UserControl inherits from TileSpriteBase and adds the basic code for a backround tile sprite that will not act as a physical barrier, and thus does not create a corresponding Farseer Body object.

    WallTileBase - This UserControl inherits from TileSpriteBase and adds the basic code for a wall tile sprite that will act as a physical barrier, and therefore a Farseer Body object is created. 

    PlayerSpriteBase - This UserControl adds the basic code for a player sprite.

    BulletSpriteBase - This UserControl adds the basic code for a bullet projectile type of object. 

    Next, I created Xaml UserControls that will be used in the game from each of these base classes.  These Xaml UserControls are where the UI is designed for these sprites and where any special logic specific to the game should be contained. 

    Finally, I created the GamePlayArea.xaml control.  The Xaml for this control is fairly simple (ignore the colors, they're just there for debugging to make things more visible):

    Code Snippet
    <UserControl x:Class="TankWars.GamePlayArea"
        xmlns="http://schemas.microsoft.com/client/2007" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Width="640" Height="480">
        <Canvas x:Name="LayoutRoot" Width="640" Height="480" Background="GreenYellow">
            <!-- This is used for going full screen -->
            <Canvas.RenderTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="scaleWindow" ScaleX="1" ScaleY="1"></ScaleTransform>
                    <TranslateTransform x:Name="translateWindow" X="0" Y="0"/>
                </TransformGroup>
            </Canvas.RenderTransform>
        </Canvas>
    </UserControl>
    </PRE< DIV>

     

    The code behind for this file is where the fun stuff happens.  This is where we create the game timer, physics simulator (Farseer), setup our key handler to capture keyboard events, build our tile-based map, and instantiate our Player (in this case, a Tank).  When the code is more presentable, I'll probably post the project here for you to download.  For now, here's a peek at the constructor code:

    Code Snippet
    public GamePlayArea()
            {
                SpritesEnvironment = new List<SpriteBase>();
    
                InitializeComponent();
    
                ParentCanvas = FindName("LayoutRoot") as Canvas;
    
                PhysicsSimulator = new PhysicsSimulator();
                PhysicsSimulator.Iterations = 50;
                PhysicsSimulator.BiasFactor = .2F;
                //PhysicsSimulator.EnableDiagnostics = true;
                PhysicsSimulator.MaxContactsToDetect = 1000;
    
                //TODO: add tiles and other objects to canvas
                BuildMap();
    
                Player1 = new Tank(PhysicsSimulator, ParentCanvas, this);
                ParentCanvas.Children.Add(Player1);
                SpritesEnvironment.Add(Player1);
                Player1.BodyObject.Position = new Vector2(50, 50);
                Player1.BodyObject.Rotation = MathHelper.ToRadians(180);
                Player1.Update();
    
                Application.Current.Host.Content.Resized += new EventHandler(BrowserHost_Resize);
    
                GameTimer = new DispatcherTimerGameLoop(20);
                GameTimer.Update += new GameLoop.UpdateHandler(GameTimer_Update);
                this.KeyHandler = new KeyHandler(this);
                GameTimer.Start();
    
                /*foreach (SpriteBase sprite in SpritesEnvironment)
                {
                    sprite.SetDebug(true);
                }*/
            }</PRE< DIV>
     

    At the moment, all I've got working is a simple map is created, a tank is added, and the tank has the ability to fire as many as 3 bullets at one time.  You can drive the tank using the arrow keys and fire bullets using the F key.  Finally, you can make the tank explode with the E key (this is just there for testing purposes). 

    Where do we go from here?

    I'd like to create a site where a user can register, choose a map, and wait for an opponent to join the game.  I will need to create a sockets server and update each of the players screens with info about their opponent's activities.  There's still plenty of work to be done before I'd consider this little application a game, but progress is being made.  The bulk of my time up till now has been spent trying to learn to use the Farseer Physics engine, which, even though it is very user-friendly, did require me to dust off some of my math skills. 

    I plan to get a working demo uploaded to Silverlight Streaming once it supports Silverlight 2 Beta 2. 

    June 12

    My First Blog Post

    This is my first blog entry in what will hopefully turn out to be many interesting an informative posts about my various programming projects, thoughts, and of course examples.  But for now, I just wanted to introduce myself.  I'm Chris Swain.  I've been a programmer for over a decade now working almost exclusively with Microsoft technologies.  The bulk of my experience is in the web application world, and as of late I find myself hopping on the Silverlight bandwagon.  In fact, you'll probably find that the majority of my posts will be to do with my learning experiences in Silverlight. 

    I live and work in Phoenix, Arizona, but I'm originally from the nortwest side of Houston, Texas.  I'm relatively new here in Phoenix, so we'll see how I acclimate to the high temperatures and lack of humidity.  As long as there's a swimming pool nearby, I should be fine!  Hot