My Blog

Author:Philip BeadleCreated:Monday, September 15, 2008 10:40 AM
This blog is more general than my Blog at http://www.dotnetnuke.com/Community/Blogs/tabid/825/Default.aspx. You'll find mostly technical tips and tricks and things i am doing in the MS user group community.

Quick note to spruik my new book Silverlight For Dummies, you can buy it all over the net and at good book stores.

In Part 1 I set up the standard Starter Kit module to use interfaces and be more testable.  In Part 2 I will implement the MVP Pattern using the Web Forms MVP project that is now included as part of DotNetNuke.  The following code samples assume you are using DNN 5.4.  There are a few changes from DNN 5.3 that make it easier to write tests and get around an issue with Windsor Castle’s problems with classes that implement more than one interface.

In Part 1 I listed the classes and interfaces we will need.  The M part of MVP is for Model so the first class to add will be the model for the module.  The model will store any properties you need such as whether the item is editable, the lsit of items and any other properties you will need to store for use in the presenter.  In this way its a little like the View-Model used in MVVM.

Follow these steps to add the models for the Edit and View aspects of your module:

  1. Right click the Models folder and select Add Class.
  2. Name the first class ViewWebformsMVPModel.
  3. Add the name space to the class.
  4. Add a property to handle whether or not the Item in the list is editable by adding the following code
       1:Private _IsEditable As Boolean
       2:Public Property IsEditable() As Boolean
       3: Get
       4: Return _IsEditable
       5: End Get
       6: Set(ByVal value As Boolean)
       7:                _IsEditable = value
       8: End Set
       9:End Property
  5. Add another property to hold a List of the items
       1:Private _ListWebformsMVPInfo As List(Of WebformsMVPInfo)
       2:Public Property ListWebformsMVPInfo() As List(Of WebformsMVPInfo)
       3: Get
       4: Return _ListWebformsMVPInfo
       5: End Get
       6: Set(ByVal value As List(Of WebformsMVPInfo))
       7:                _ListWebformsMVPInfo = value
       8: End Set
       9:End Property
  6. Add another class and call it EditWebformsMVPModel
  7. Add a property to hold the item you are editing by adding the following code:
       1:Private _Item As WebformsMVPInfo
       2:Public Property Item() As WebformsMVPInfo
       3: Get
       4: Return _Item
       5: End Get
       6: Set(ByVal value As WebformsMVPInfo)
       7:                _Item = value
       8: End Set
       9:End Property

Thats the models done now the next letter in MVP is for View so I’ll now show you how to set up the views for the Edit and View screens.  The view will have an interface that defines the events and any methods that are needed.  The Interface for ViewWebFormsMVP is the simplest kind as it has no events or methods.  The Interface for the edit screen will have events for the three buttons on it for Save, Cancel and Delete and also two methods to control the visibility of the Delete button and the audit control.  To get the ascx controls to use the new interface you just implement in the code behind.  Doing this allows you to test the model and presenter with a mock view.

Follow these steps to set up the Views:

  1. Drag the edit and view ascx files into the Views folder.
  2. Right click the Views folder and add two interfaces, one called IViewWebformsMVP and the other called IEditWebformsMVP.
  3. Add the following bolded code to the IViewWebformsMVP interface so that view knows how to find the model:
       1:Imports DotNetNuke.Web.Mvp
       2: 
       3:Namespace YourCompany.Modules.WebFormsMVP
       4:Public Interface IViewViewWebformsMVP
       5:Inherits IModuleView(Of ViewWebformsMVPModel)
       6:End Interface
       7: 
       8:End Namespace
  4. Add the following bolded code to IEditWebformsMVP
       1:Imports DotNetNuke.Web.Mvp
       2: 
       3:Namespace YourCompany.Modules.WebFormsMVP
       4: 
       5:Public Interface IEditWebFormsMVP
       6:Inherits IModuleView(Of EditWebformsMVPModel)
       7:Sub ShowAuditControl(ByVal show As Boolean)
       8:Sub ShowDeleteButton(ByVal show As Boolean)
       9: 
      10:Event Cancel As EventHandler
      11:Event Delete As EventHandler
      12:Event Save As EventHandler(Of UIEventArgs(Of WebformsMVPInfo))
      13: 
      14:End Interface
      15: 
      16:End Namespace
  5. Open the code behind for ViewWebFormsMVP and add the following bolded code and delete the existing Inherits line:
       1:    Partial Class ViewWebformsMVP
       2: Inherits ModuleView(Of ViewWebformsMVPModel)
       3:Implements IViewWebformsMVP
       4:Implements Entities.Modules.IActionable
  6. Open the EditWebFormsMVP code behind and add the following bolded code:
       1:    Partial Class EditWebformsMVP
       2:Inherits ModuleView(Of EditWebformsMVPModel)
       3:Implements IEditWebFormsMVP
  7. Implement the interface by adding the following code:
       1:Public Sub ShowAuditControl(ByVal show As Boolean) Implements IEditWebFormsMVP.ShowAuditControl
       2: Throw New NotImplementedException()
       3:End Sub
       4: 
       5:Public Sub ShowDeleteButton(ByVal show As Boolean) Implements IEditWebFormsMVP.ShowDeleteButton
       6: Throw New NotImplementedException()
       7:End Sub
       8: 
       9:Public Event Cancel As EventHandler Implements IEditWebFormsMVP.Cancel
      10:Public Event Delete As EventHandler Implements IEditWebFormsMVP.Delete
      11:Public Event Save As EventHandler(Of UIEventArgs(Of WebformsMVPInfo)) Implements IEditWebFormsMVP.Save

Now the the views have their interfaces all wired up we still have a bit of work to do.  You’ll notice that the code behind files still have lots of code in the events for the buttons and load etc.  All of this code needs to be moved to the presenter classes as its code that needs to be tested.  Then we need to change the events to raise the event from the interface so the presenter can do its job.  To set up the presenter classes do the following:

  1. Right click the Presenters folder and add two classes, one called EditWebformsMVPPresenter and the other called ViewWebformsMVPPresenter.
  2. Add the name spaces.
  3. The presenter class needs to inherit from the core WebFormsMVP presenter class.
  4. The presenter also needs two constructors, add the following code in bold to set this up.  The first constructor is used in production and the second one is sued for test which allows you to pass in a mock controller for testing.
  5.    1:Namespace YourCompany.Modules.WebformsMVP
       2:Public Class ViewWebformsMVPPresenter
       3:Inherits ModulePresenter(Of IViewWebformsMVP, ViewWebformsMVPModel)
       4: 
       5:Public Sub New(ByVal _View As IViewWebformsMVP)
       6: Me.New(_View, New WebformsMVPController(New SqlDataProvider()))
       7:End Sub
       8: 
       9:Public Sub New(ByVal _View As IViewWebformsMVP, ByVal _Controller As IWebformsMVPController)
      10: MyBase.New(_View)
      11:End Sub
      12:End Class
      13:End Namespace
  6. Do the same for the EditWebFormsMVPPresenter
  7.    1:Namespace YourCompany.Modules.WebformsMVP
       2:Public Class EditWebformsMVPPresenter
       3:Inherits ModulePresenter(Of IEditWebFormsMVP, EditWebformsMVPModel)
       4: 
       5:Public Sub New(ByVal _View As IEditWebFormsMVP)
       6: Me.New(_View, New WebformsMVPController(New SqlDataProvider()))
       7:End Sub
       8: 
       9:Public Sub New(ByVal _View As IEditWebFormsMVP, ByVal _Controller As IWebformsMVPController)
      10: MyBase.New(_View)
      11:End Sub
      12:End Class

The classes are now set up so the next step is to move the logic code from the code behind files into the presenter classes.  This is pretty much a Cut & Paste operation with a few changes to use the View rather than controls directly.  The ViewWebFormsMVP is the simplest so let’s do that one first.

  1. Cut the code inside the Page_Load event in the ViewWebFormsMVP.ascx.vb file.
  2. Paste it into the second constructor directly after the MyBase.New(_View) line of code.
  3. Change the lines of code that bind the List to the lstContent Datalist to assign the List to the View.Model.List.
  4. Then change the ProcessModuleLoadException call to remove the Me from it as this is no longer needed as the base class puts that in.  The code looks like this:
  5.    1:Public Sub New(ByVal _View As IViewWebformsMVP, ByVal _Controller As IWebformsMVPController)
       2: MyBase.New(_View)
       3: Try
       4: Dim objWebformsMVPs As New WebformsMVPController
       5: Dim colWebformsMVPs As List(Of WebformsMVPInfo)
       6: 
       7: ' get the content from the WebformsMVP table
       8:                colWebformsMVPs = objWebformsMVPs.GetWebformsMVPs(ModuleId)
       9: 
      10: If colWebformsMVPs.Count = 0 Then
      11: ' add the content to the WebformsMVP table
      12: Dim objWebformsMVP As WebformsMVPInfo = New WebformsMVPInfo
      13:                    objWebformsMVP.ModuleId = ModuleId
      14:                    objWebformsMVP.Content = Localization.GetString("DefaultContent", LocalResourceFile)
      15:                    objWebformsMVP.CreatedByUser = Me.UserId
      16:                    objWebformsMVPs.AddWebformsMVP(objWebformsMVP)
      17: ' get the content from the WebformsMVP table
      18:                    colWebformsMVPs = objWebformsMVPs.GetWebformsMVPs(ModuleId)
      19: End If
      20: 
      21: ' bind the content to the repeater
      22:                _View.Model.ListWebformsMVPInfo = colWebformsMVPs
      23: 
      24: Catch exc As Exception'Module failed to load
      25:                ProcessModuleLoadException(exc)
      26: End Try
      27:End Sub
  6. For now comment out the lstContent_ItemDataBound method, I’ll come back to that later.
  7. There is one last step to wire up the Vie to the Presenter.  In the ViewWebformsMVP.ascx.vb file you need to add an attribute to the class to tell it which Presenter it should use.  To do this add the bolded code to the class as shown below:
  8.    1:<PresenterBinding(GetType(ViewWebformsMVPPresenter))> _
       2:    Partial Class ViewWebformsMVP
       3:Inherits ModuleView(Of ViewWebformsMVPModel)

Now that the Presenter takes care of all the logic and it has no knowledge of the controls that the values it works with should go in the ascx file needs to be updated to use some one way data binding.  To do this set the DataSource property on the DataList control in the markup and then call Me.DataBind() in the Page_PreRender event.  Foolow these steps to accomplish this:

  1. Set the DataSource property in the mark up as below
  2. <asp:datalist id="lstContent"DataSource='<%# Model.ListWebformsMVPInfo %>'datakeyfield="ItemID" runat="server" cellpadding="4">
  3. In the code behind file add the Page_PreRender event and tll the page to DataBind.
  4.    1:Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
       2: Me.DataBind()
       3:End Sub
  5. Next is to modify the ascx a bit more to use the Model properties.  The whole DataList looks like this:  Notice the use of ModuleContext, this is available in the base class and gives access to most of the DNN properties.
  6.    1:<asp:datalist id="lstContent"DataSource='<%# Model.ListWebformsMVPInfo %>' datakeyfield="ItemID" runat="server" cellpadding="4">
       2:<ItemTemplate>
       3:<table cellpadding="4" width="100%">
       4: <tr>
       5: <td valign="top" width="100%" align="left">
       6: <asp:HyperLink ID="HyperLink1" NavigateUrl='<%# ModuleContext.EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID")) %>'
       7: Visible="<%# Model.IsEditable %>" runat="server">
       8: <asp:Image ID="Image1" runat="server" ImageUrl="~/images/edit.gif" AlternateText="Edit"
       9: Visible="<%#Model.IsEditable%>" resourcekey="Edit" /></asp:HyperLink>
      10: <asp:Label ID="lblContent" runat="server" Text='<%# DataBinder.Eval(Container.DataItem,"DisplayContent") %>' CssClass="Normal" />
      11: </td>
      12: </tr>
      13:</table>
      14:</ItemTemplate>
      15:  </asp:datalist>

That's the ViewWebFormsMVP control finished.  The refactoring is relatively simple because you just move the code from the code behind to the presenter and change any references directly to controls to use the property on the View.Model.

The process is the same for the EditWebFormsMVP control except that we have events to handle as well.  I won’t show all the code here as there is a lot.  I’ll show one event and how to pass the values from the View to the Presenter using the UIEventArgs.  Firstly let’s look at the UIEventArgs class, it’s very simple, it a normal EventArgs class with a generic property that you can add any object to.  In this example the generic will be the WebformsMVPInfo object.

   1:Public Class UIEventArgs(Of T)
   2:Inherits EventArgs
   3: 
   4:Public Sub New(ByVal item As T)
   5:        itemValue = item
   6:End Sub
   7: 
   8:Private itemValue As T
   9:Public Property Item() As T
  10:Get
  11: Return itemValue
  12:End Get
  13:Set(ByVal value As T)
  14:            itemValue = value
  15:End Set
  16:End Property
  17: 
  18:End Class

In the Edit presenter I need to know the ItemId that is to be edited which is in the QueryString.  To access the Request object you will need a refernce to System.Web.Abstractions.  Add a property to the presenter for ItemId like so:

   1:Public ReadOnly Property ItemId() As Long
   2: Get
   3: Dim _ItemId As Integer = Null.NullInteger
   4: If Not String.IsNullOrEmpty(Request.Params("ItemId")) Then
   5:                    _ItemId = Int32.Parse(Request.Params("ItemId"))
   6: End If
   7: Return _ItemId
   8: End Get
   9:End Property

In the ViewWebFormsMVPPresenter I added the code to populate the Model in the constructor, this works fine because I don’t need anything from the Request.  However in the Edit presenter I need to know the ID of the item to be edited which is gathered from the QueryString therefore I can’t put the code in the constructor.  Instead the code goes into an Override for OnLoad.  Before I show you the code for that you will need to add a provate variable to hold the Controller class.

   1:#Region "Private Members"
   2:Private Controller As IWebformsMVPController
   3:#End Region

The last thing to do before we add the OnLoad code is to add another constructor to the Info class so we can create a new one if the edit form is adding a new record.

   1:Public Sub New(ByVal mId As Integer, ByVal uId As Integer)
   2:            ModuleId = mId
   3:            ItemId = Null.NullInteger
   4:            CreatedByUser = uId
   5:            CreatedDate = Now
   6:End Sub

Add the OnLoad code now by adding the following code:

   1:Protected Overrides Sub OnLoad()
   2: MyBase.OnLoad()
   3: Try
   4: If Not DotNetNuke.Common.Utilities.Null.NullInteger = ItemId Then
   5: ' get content
   6: Dim objTestDrivenDNNModule As WebformsMVPInfo = Controller.GetWebformsMVP(ModuleId, ItemId)
   7: If Not objTestDrivenDNNModule Is Nothing Then
   8:                        View.Model.Item = objTestDrivenDNNModule
   9: Else ' security violation attempt to access item not related to this Module
  10:                        Response.Redirect(NavigateURL())
  11: End If
  12: Else
  13:                    View.Model.Item = New WebformsMVPInfo(ModuleId, UserId)
  14:                    View.ShowDeleteButton(False)
  15:                    View.ShowAuditControl(False)
  16: End If
  17: Catch exc As Exception
  18:                ProcessModuleLoadException(exc)
  19: End Try
  20:End Sub

That takes care of loading the correct item into the View.Model.Item.  As with the ViewWebFormsMVP control the ascx markup needs to be updated to use one way databinding.  The change is very simple, just set the Text property to the Content property of the Item as shown in bold below:

   1:<table width="650" cellspacing="0" cellpadding="0" border="0" summary="Edit Table">
   2:<tr valign="top">
   3:<td class="SubHead" width="125"><dnn:label id="lblContent" runat="server" controlname="lblContent" suffix=":"></dnn:label></td>
   4:<td>
   5: <dnn:texteditor id="txtContent" Text='<%# Model.Item.Content %>' runat="server" height="200" width="500" />
   6: <asp:RequiredFieldValidator ID="valContent" resourcekey="valContent.ErrorMessage" ControlToValidate="txtContent"
   7: CssClass="NormalRed" Display="Dynamic" ErrorMessage="&lt;br>Content is required" Runat="server" />
   8:</td>
   9:</tr>
  10:</table>

There are only a couple of things left to do now, create the methods in the presenter that will fire when the events are activated, wire up those methods in the constructor and show you how to use the UIEventArgs in the Save method.  You’ll notice that the code is literally Cut & Pasted from the code behind.

Firstly add the following code to implement a method for the Cancel event:

   1:Public Sub Cancel(ByVal sender As Object, ByVal e As EventArgs)
   2:            Response.Redirect(NavigateURL())
   3:End Sub

Now implement the Delete method:

   1:Public Sub Delete(ByVal sender As Object, ByVal e As EventArgs)
   2: Try
   3: ' Only attempt to delete the item if it exists already
   4: Dim objTestDrivenDNNModule As WebformsMVPInfo = Controller.GetWebformsMVP(ModuleId, ItemId)
   5: If Not objTestDrivenDNNModule Is Nothing Then
   6:                    Controller.DeleteWebformsMVP(ModuleId, ItemId)
   7: ' Redirect back to the portal home page
   8:                    Response.Redirect(NavigateURL())
   9: Else ' security violation attempt to access item not related to this Module
  10:                    Response.Redirect(NavigateURL())
  11: End If
  12: Catch exc As Exception
  13: 'Show a UI message later
  14:                ProcessModuleLoadException(exc)
  15: End Try
  16:End Sub

The Save method has a slightly different signature because it uses UIEventArgs.  However the code is just cut pasted from the code behind as well.

   1:Public Sub Save(ByVal sender As Object, ByVal e As UIEventArgs(Of WebformsMVPInfo))
   2: Try
   3:                e.Item.CreatedByUser = UserId
   4:                e.Item.ModuleId = ModuleId
   5:                e.Item.ItemId = ItemId
   6: If DotNetNuke.Common.Utilities.Null.NullInteger = ItemId Then
   7: ' add the content 
   8:                    Controller.AddWebformsMVP(e.Item)
   9: Else
  10: ' update the content 
  11:                    Controller.UpdateWebformsMVP(e.Item)
  12: End If
  13: ' Redirect back to the portal home page
  14:                Response.Redirect(NavigateURL())
  15: Catch exc As Exception'Module failed to load
  16:                ProcessModuleLoadException(exc)
  17: End Try
  18:End Sub

That’s the methods created now wire them up in the constructor like so:

   1:Public Sub New(ByVal _View As IEditWebFormsMVP, ByVal _Controller As IWebformsMVPController)
   2: MyBase.New(_View)
   3:            Controller = _Controller
   4: AddHandler View.Cancel, AddressOf Cancel
   5: AddHandler View.Delete, AddressOf Delete
   6: AddHandler View.Save, AddressOf Save
   7:End Sub

Now change the code behind file so the events simple raise events for the presenter to listen to like so:

   1:Private Sub cmdCancel_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdCancel.Click
   2: RaiseEvent Cancel(Me, e)
   3:End Sub
   4: 
   5:Private Sub cmdUpdate_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdUpdate.Click
   6:            Model.Item = New WebformsMVPInfo()
   7:            Model.Item.Content = txtContent.Text
   8: RaiseEvent Save(Me, New UIEventArgs(Of WebformsMVPInfo)(Model.Item))
   9:End Sub
  10: 
  11:Private Sub cmdDelete_Click(ByVal sender As Object, ByVal e As EventArgs) Handles cmdDelete.Click
  12: RaiseEvent Delete(Me, e)
  13:End Sub

The very last thing to do is implement the two methods that control the visbility of the delete button and audit control.

   1:Public Sub ShowAuditControl(ByVal show As Boolean) Implements IEditWebFormsMVP.ShowAuditControl
   2:            ctlAudit.Visible = show
   3:End Sub
   4: 
   5:Public Sub ShowDeleteButton(ByVal show As Boolean) Implements IEditWebFormsMVP.ShowDeleteButton
   6:            cmdDelete.Visible = show
   7:End Sub

Now build your project and, uh oh forgot to do something.  In the Actions menu code you need to add ModuleContext to the front of the EditURL and GetNextActionID like so:

1:Public ReadOnly Property ModuleActions() As Entities.Modules.Actions.ModuleActionCollection

Implements Entities.Modules.IActionable.ModuleActions

   2: Get
   3: Dim Actions As New Entities.Modules.Actions.ModuleActionCollection
   4:                Actions.Add(ModuleContext.GetNextActionID, 
Localization.GetString(Entities.Modules.Actions.ModuleActionType.AddContent, LocalResourceFile),
                         Entities.Modules.Actions.ModuleActionType.AddContent, "", "", ModuleContext.EditUrl(), False, 
                         Security.SecurityAccessLevel.Edit, True, False)
   5: Return Actions
   6: End Get
   7:End Property

Now its all go :)  Your module is ready to rock and be tested which I’ll cover in Part 3.  The solution thus far is available here.

I wrote this script to help me be able to quickly run up a new auto installed DNN site from a zip file.  Check it out and please use yourself.

#cd "C:\DotNetNuke\ProReleases"
cd "C:\DotNetNuke\Releases"
$CurrentLocation=get-location

#$CEorPE ="Professional"
$CEorPE ="Community"
$version="050400"
$ZipFile = "$CurrentLocation\5.4.0\DotNetNuke_Community_5.4.0.94_Install.zip"
$websiteName = "DotNetNuke" + $CEorPE + $version + "Install"
$websiteAddress = "DotNetNuke" + $CEorPE + $version + ".Install"
$dbname = "DotNetNuke" + $CEorPE + $version + "Install"
$physicalPath = "DotNetNuke" + $CEorPE + $version + "Install"

#remove existing IIS Site and App pool
Import-Module WebAdministration
Remove-Item iis:\Sites\$websiteName -Recurse
Remove-Item IIS:\AppPools\DotNetNukeAppPool -Recurse

$shell=new-object -com shell.application

$CurrentPath=$CurrentLocation.path

$OutPutPath = "$CurrentPath\$physicalPath"

$OutPutLocation=$shell.namespace($OutPutPath)

#delete anything in the destination folder
new-item —force -path $OutPutPath -itemtype "directory"
get-childitem $OutPutPath | remove-item -force -recurse

$ZipFolder = $shell.namespace($ZipFile)

$OutPutLocation.Copyhere($ZipFolder.items())

#Set the ACL on the folder
$acl = Get-Acl $OutPutPath
$permission = "NETWORK SERVICE","FullControl","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.SetAccessRule($accessRule)
Get-ChildItem $OutPutPath -Recurse -Force | Set-Acl -AclObject $acl

#Create the IIS Site
New-Item IIS:\AppPools\DotNetNukeAppPool -Force

Set-ItemProperty IIS:\AppPools\DotNetNukeAppPool -name ProcessModel.identityType -Value 2

New-Item iis:\Sites\$websiteName -bindings @{protocol="http";bindingInformation=":80:$websiteAddress"} -physicalPath $OutPutPath
Set-ItemProperty IIS:\Sites\$websiteName -name applicationPool -value DotNetNukeAppPool

#Update the hosts file
$hostsentry = Select-String $Env:SystemRoot\System32\drivers\etc\hosts -pattern "$websiteAddress" -quiet
if (-not $hostsentry)
{
    Add-Content $Env:SystemRoot\System32\drivers\etc\hosts "127.0.0.1        $websiteAddress"
}

#Now open IE and navigate to the site
$ie = new-object -com "InternetExplorer.Application"

$ie.Visible = $true

$ie.Navigate("http://$websiteAddress/")

#some of the code below from http://msdn.microsoft.com/en-us/magazine/cc337896.aspx

$wait = $true
  $numWaits = 0
  while ($wait -and $numWaits -lt 100) {
    $numWaits++
    [System.Threading.Thread]::Sleep(500)
  $doc = $ie.Document
    if ($doc -ne $null) {
      $wait = $false
    }
    else {
      write-host "Waiting for app to respond $numWaits . . ."
    }
  }
  if ($numWaits -eq 100) {
    throw "Application did not respond after 100 delays"
  }
  else {
    write-host "Application has responded"
  }
$auto = $doc.getElementByID("wizInstall_rblInstall_2")
$auto.checked = $true
$nextBtn = $doc.getElementByID("wizInstall_StartNavigationTemplateContainerID_StartNextLinkButton")
$nextBtn.click()
[System.Threading.Thread]::Sleep(60000)
$ie.Navigate("http://$websiteAddress/default.aspx")

 This morning at the QA team meeting we discussed the Testable DNN Module and its use of Webforms MVP to make it testable.  During the conversation we decided we needed some step by step information on how it all ties together in a “Michael Washington” style tutorial.  Charles Nurse has written up a great post on the theory behind the use of Webforms MVP here, so this tutorial will be more along the lines of what to do not why you do it.  Please make sure you read Charles’ post because I am assuming you have.

Read More »

In Part 3 we did the basics of setting up MVVM for our module.  Now lets look at getting some data into our application using RIA Services.  It’s fairly straight forward to set up RIA Services as the Visual Studio templates do all the lifting for you.  Follow these steps to add RIA Services to your solution:

Note: I have change the table name for the Announcements table to Announcement so that the generated class names and collections make more sense.

  1. We already added the project we are going to use so go to AnnouncementServices and Add a New Item and choose Linq to Sql Classes, and call it AnnouncementDataClasses.dbml.
    You can use Entity Framework here if you prefer.
    image
  2. Now open the Server Explorer and open your DotNetNuke database and drag the Announcement table onto the design surface.  You should see this;
    image
  3. Now build your solution.  You need to build it so that when we add the domain service the wizard can find the entity classes.
  4. Now add another new item called Domain Service Class and call it AnnouncementDomainService.cs
    image
  5. Check all the boxes so that you get the meta data class, Client Access and the CRUD methods
    image
  6. Add a reference from your web application MySilverlightApplication.Web to the AnnouncementServices project and build your application.
  7. Your solution explorer should look like this now
    image

You can see the “magic” file generated by the RIA Services link to your Silverlight Application.  Investigate this file and take note of the classes in there.  This is how we will communicate with the server from our client side application.  You can see that we have a partial class called Announcement which is an entity class.

If you’re wondering how RIA Communicates with the server when havent made any Service References have a look at line 348:

public AnnouncementDomainContext() : 
this(new HttpDomainClient(new Uri("DataService.axd/AnnouncementServices-AnnouncementDomainService/", System.UriKind.Relative)))
{
}

RIA uses a httpHandler to manage the communication so you need to make sure that you update the web.config of your site.  Look at the app.config file in the AnnouncementServices project and copy the handler.  If you are using IIS 7+ you will need to also add the handler to the system.webserver section.  So add the following 2 items:

This goes in system.web handlers section
<add path="DataService.axd" verb="GET,POST" type="System.Web.Ria.DataServiceFactory, System.Web.Ria, Version=2.0.0.0, 
Culture=neutral, PublicKeyToken=31bf3856ad364e35"
validate="false"/>

and this goes in system.webserver handlers section

<add name="DataService" verb="GET,POST" path="DataService.axd" type="System.Web.Ria.DataServiceFactory, System.Web.Ria,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
/>

The next step is to update our model so that it has a method to retrieve Announcement entities from the database. 

Modify the IAnnouncementModel interface like so:

namespace MySilverlightApplication.Model
{
public interface IAnnouncementsModel
    {
void GetAnnouncementList(Action<IEnumerable<Announcement>> action);
    }
}

Tip: If you are using ReSharper you can include the generated file in the project to help it find the generated classes.

Now we need to implement the interface’s new method:

namespace MySilverlightApplication.Model
{
public class AnnouncementsModel : IAnnouncementsModel
    {
public void GetAnnouncementList(Action<IEnumerable<Announcement>> action)
        {
            var context = new AnnouncementDomainContext();
            var qry = context.GetAnnouncementsQuery().Where(a => a.ModuleID == 376);
            context.Load(qry, op => action(op.Entities), null);
        }
    }
}

Now let’s write our first test.  We are going to write a test that tests to make sure we have two Announcement entities loaded into the ViewModel when create a new ViewModel.  We are going to do some TDD here so this test will fail until we write the minimum amount of production code to make it pass.

Open up your MSTest project and change the UnitTest.cs file name to ViewModelTests.cs

Now modify it so it looks like this:

namespace MySilverlightApplication.MSTest
{
    [TestClass]
public class ViewModelTests : SilverlightTest
    {
readonly MainPageViewModel _viewModel = new MainPageViewModel(new MockAnnouncementsModel());
 
        [TestMethod]
public void ViewModelDataLoadsCorrectly()
        {
            Assert.AreEqual(2, _viewModel);
        }
    }
}

Here you can see why we used two constructors on the MainPageViewModel class and why we instantiate it with an interface, so we can pass in our own MockAnnouncementsModel for testing.

Run the test and it will fail because it doesnt make much sense yet.  What we need is something on the VM to hold a list of Announcement entities so lets add an ObservableCollection.  Add the following code to the ViewModel

private ObservableCollection<Announcement> _announcements;
public ObservableCollection<Announcement> Announcements
{
    get { return _announcements; }
    set
    {
        _announcements = value;
//OnPropertyChanged("Announcements");
    }
}

Don’t worry about the OnPropertyChanged bit yet, we’ll get to that soon.

Ok so now we have somewhere to store our result lets populate it.  Add the following method:

public void LoadModel()
{
    _announcementsList.GetAnnouncementList(op =>
    {
        Announcements = op.AsObservableCollection();
if (LoadComplete != null) LoadComplete(this, null);
    });
}

I’m using an extension method here called AsObservableCollection because I’m always turning IEnumerable in AsObservable.  To do this add a new Silverlight class library project to your solution and call it Common.  Then add a class called Extensions and modify it as below:

namespace Common
{
public static class Extensions
    {
public static ObservableCollection<T> AsObservableCollection<T>(this System.Collections.Generic.IEnumerable<T> col)
        {
            var newcol = new ObservableCollection<T>();
 foreach (var item in col)
            {
                newcol.Add(item);
            }
 return newcol;
        }
    }
}

Now add a reference to it from the MySilverlightApplication project and build your solution.

Next we’ll modify the Mock data model class so it returns 2 Announcement entities.

namespace MySilverlightApplication.MSTest
{
class MockAnnouncementsModel : IAnnouncementsModel
    {
public void GetAnnouncementList(Action<IEnumerable<Announcement>> action)
        {
            action(MockData());
        }
 
public IEnumerable<Announcement> MockData()
        {
 return new[]
                  {
 new Announcement()
                    {
                      Title = "Test Title 1",
                      CreatedDate = new DateTime(2009, 9, 9)
                    },
 new Announcement()
                    {
                      Title = "Test Title 2",
                      CreatedDate = new DateTime(2009, 9, 10)
                    }
                  };
        }
    }
}

Lastly modify the test so it calls the Load method and does it’s assert against the count of the collection like so:

[TestMethod]
public void ViewModelDataLoadsCorrectly()
{
    _viewModel.LoadModel();
    Assert.AreEqual(2, _viewModel.Announcements.Count());
}

Run the test and you should get a nice green tick:

image

Righto, good news the test passes so now we can go to our DNN web site and have a look at the module.  Make sure you have some announcements in the database first :)

Make sure you attach the debugger to the process and put a break point in the LoadModel so that you can see the results coming back properly.  Now if you’re wondering why the results don’t show up in the site that’s because we need to let the UI know that we have some results.  To do that we Implement INotifyPropertyChanged.  Since we will use this all over the place we’ll add it into the Common project.

Add a new class called ViewModelBase and modify it to look like:

namespace Common
{
public class ViewModelBase : INotifyPropertyChanged
    {
public event PropertyChangedEventHandler PropertyChanged;
 
protected void OnPropertyChanged(params string[] propertyNames)
        {
 if (null == PropertyChanged)
 return;
 foreach (var each in propertyNames)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(each));
            }
        }
    }
}

Now go the MainPageViewModel class and make it inherit the new ViewModelBase class and then uncomment the OnPropertyChanged line.  Now rebuild and check your module.  You should get this:

image

Ok that’s it for Part 4, we looked at how to write a test for the View Model and how to hook up RIA services and get data into our UI.  Remember the code is available at CodePlex http://dnnsilverlightria.codeplex.com/

I have been adding the source code for this blog series on my Downloads page at each step and I also loaded it up to CodePlex. http://dnnsilverlightria.codeplex.com/.  If you would like to contribute please contact me.

So we’re all set to write some code now.  I will be using the MVVM pattern to separate the concerns and to make the logic testable.  MVVM stands for Model – View – View Model, the View is the MainPage.xaml, the View Model will be MainPage.vm.cs and the Model will be our list of announcements.

  1. Add a new Silverlight class to the MySilverlightApplication project and call it MainPage.vm.cs, this will be the code file for the View Model.
    I have a registry entry that assocaites my MainPage.vm.cs file with my MainPage.xaml.cs file the same way the MainPage.xaml.cs file is associated.  It looks like this for my 64 bit machine

    Windows Registry Editor Version 5.00
  2. [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\9.0\Projects\{FAE04EC0-301F-11d3-BF4B-00C04F79EFBC}\RelatedFiles\.xaml]
    ".xaml.cs"=dword:00000039
    ".cs"=dword:00000002
    ".vm.cs"=dword:00000039
    ".design.cs"=dword:00000039

  3. Change the name of the class to MainPageViewModel,
  4. Now we need to set up our Model which will use an interface so we can easily swap out the model into our View Model for testing.
    1. Add a folder called Model,
    2. Add a class called IAnnouncementsModel, it’s actually an interface so you’ll need to change it from a class to interface.
    3. Your interface should look like this:
    4. namespace MySilverlightApplication.Model
      {
      public interface IAnnouncementsModel
          {
          }
      }
    5. Now we need a concrete implementation of this so add another class called AnnouncementsModel that implements this interface.
    6. Your class should look like this:
    7. namespace MySilverlightApplication.Model
      {
      public class AnnouncementsModel : IAnnouncementsModel
          {
       
          }

That's the model setup for now, we’ll add methods to it a bit later, let’s wire up the View Model with our model now.  We will provide two constructors for the View Model.  One will be used when we test it and the other will be used when it runs normally.  Edit your View Model class to look like the following:

namespace MySilverlightApplication
{
public class MainPageViewModel
    {
private readonly IAnnouncementsModel _announcementsList;
public event EventHandler LoadComplete;
 
public MainPageViewModel()
            : this(new AnnouncementsModel())
        {
        }
 
public MainPageViewModel(IAnnouncementsModel announcementsList)
        {
            _announcementsList = announcementsList;
        }
    }
}

The last thing we need to do is to tell the View to set its DataContext to the ViewModel at runtime.  We only want this to occur outside of Blend so the following test will load our VM at runtime and will use the SampleData in Blend.

Change the code behind file for MainPage.xaml.cs to look like the following:

namespace MySilverlightApplication
{
public partial class MainPage : UserControl
    {
public MainPage()
        {
            InitializeComponent();
 if (!System.ComponentModel.DesignerProperties.IsInDesignTool)
            {
                DataContext = new MainPageViewModel();
            }
        }
    }
}

So that’s our MVVM pattern set up.  In Part 4 we will look at creating the RIA Service for the Announcements and doing some testing of our ViewModel.

The files for these parts are available on the Downloads page of this site.

Now that you have your modules registered place it on a page.  You should see your module with an empty Silverlight control inside it.  Now switch to the settigns for the module and hit the checkbox to change it to load the SLTest Silverlight control.  This will execute the one test that is in teh file at this point and it will fail giving you the following result.

image

As you can see the testing results panel shows up as it is conducting the tests and gives you the result.  This is an excellent way to create integration tests for your module and it makes it very easy to ensure that as you code you are keeping your quality high and not introducing any new issues.  You can try out debugging at this point as well.  In the SLTest project add a break point to the Initialize event and then do Attach to process and select the Silverlight process like so.

image

Let’s get into designing our Announcements list.  The idea is that this module will replace the existing Announcements module.  So we will re-implement the same functionality starting off with listing the items in a nice list.  To do this we will use Expression Blend and use the cool new Sample Data feature.  Follow these steps:

  1. Right click the MainPage.xaml file and select Open In Expression Blend,
  2. Add a ListBox control to the page and set its margins to 8 all around,
  3. Select the Data Tab,
  4. Click “Define new Sample Data”,
  5. Uncheck the “Enable smale data when application is running”, we don’t want sample data then we will have our own data from the database.  Unchecking this makes Silverlight ignore the sample data at run time which is exactly what we want to happen.
  6. Select Ok.
  7. You will now have the Sample data showing in the Data Tab.  Change the name from Collection to Announcements, this will be the name for our real data collection as well that we will use RIA to retrieve later.
  8. Now change the property names to match the fields in the Announcements table in your DNN database.  You may need to install the standard DNN Announcements module to see this table.  You only need to add the properties for the fields you want to show in the list here.
    1. Make sure you set the property names exactly as they are in the database. 
    2. You can change the sample data by clicking the symbol to the right of the property name.  Set the Title property to be String – Name, set the CreatedDate to String – Date etc.
  9. Now select the Announcements Collection and drag it onto the ListBox.  Its hould now look like the following
    image
  10. When you drag on the collection Blend adds a DataContext to the LayoutRoot grid.  Remove this text in the xaml

    d:DataContext="{Binding Source={StaticResource SampleDataSource}}"

    and move it into the UserControl declaration as shown below:

    <UserControl x:Class="MySilverlightApplication.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    d:DataContext="{Binding Source={StaticResource SampleDataSource}}"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

Ok you are now set up to design the ListBox as you see fit.  Have some fun with Blend, you can easily edit how the ListBox looks by right clicking it and selecting “Edit Additional Templates” –> “Edit Generated Items” –> “Edit Current”.  You are now in template editing mode and can edit the items template to your hearts content.

Om so now you can see a nicely formatted list of items in Blend.  Try going back to Visual Studio rebuilding your application and checking it out in runtime mode.  You’ll see there are no items in the list :)  This is what is called Blendability, your designer can now design away and create a beautiful interface with nice sample data that doesnt interfere with the application when it is running in production.

That’s it for Part 2.  In Part 3 we will look at using the MVVM pattern to make our application easily testable and we’ll get some real data into our list.

This series of posts will show you a bunch of things I have learnt about building integrated Silverlight – DotNetNuke modules with RIA Services

Projects to add:

  1. Add a new Silverlight Project.  Call it MySilverlightApplication.
  2. Select to host it in a new Web Application called MySilverlightApplication.Web and check the Enable RIA Services link.
  3. Add a new Silverlight Unit Testing project and call it MySilverlightApplication.SLtest.
  4. Add a new Unit Test Project and call it MySilverlightApplication.MSTest
  5. Create a Solution Folder called Modules and move these four projects into it.
  6. Create another solution folder and call it Services.
  7. Add a new .Net class library (not a Silverlight one) and call it AuthenticationServices
  8. Our module is going to interact with the Announcements table so add another .Net class library and call it AnnouncementServices

Ok we now have all the projects we need to build a fully testable and Blendable Silverlight Application for DotNetNuke.  Let’s do a little cleaning up and get everything ready for easy designing, testing and developing.

  1. Delete the .aspx and .html files from the MySilverlightApplication.Web project.
  2. Click on the web.config file and set the build action to None.  The DNN site already has a web.config.
  3. Click on the Silverlight.js file and set the Copy To Output Directory to Copy If Newer.
  4. Open double click teh Properties node in teh Solution Explorer and add the MySilverlightApplication.SLtest project as a linked Silverlight Application.
  5. Add a new Folder to the MySilverlightApplication.SLtest called Dependencies and add the Microsoft.Silverlight.Testing.dll and Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll files to it. 
  6. Now add references to those two files so that your Silverlight Test project builds.
  7. Open the MSTest project and remove the references to System and System.Core, we need to replace these with the Silverlight specific versions.
  8. Add a reference to C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v3.0\system.dll.  You will also need references to System.Core, System.Net and System.Windows which are all in the same folder.
  9. Also add a reference to the same two dlls as in step 5 and to C:\Program Files (x86)\Microsoft SDKs\RIA Services\v1.0\Libraries\Silverlight\System.Windows.Ria.dll
  10. Now to both the SLTest and MSTest projects ad a reference to the Silverlight Application.

Everything should be building ok now so let’s make some changes to the .csproj files so that the correct files are deployed to our Development DotNetNuke site for easy debugging and automated integration testing.

For debugging we need the .xap, .dll and .pdb files to be deployed onto the Development DNN instance, to do this I modify the .csproj files to do a copy after the build is finished.

  1. Right click the MySilverlightApplication and select Unload Project.
  2. Right click it again and select Edit which will open the MSBuild file for the project.
  3. Towards the bottom of the file you will see some commented out code.  Paste the following immediately after that code.
<Target Name="AfterBuild" DependsOnTargets="DeployModule"
Condition=" '$(IsDesktopBuild)'!='false' ">
</Target>
<PropertyGroup>
<DNNDirectory>
      D:\DotNetNuke\DotNetNuke_Community_05.01.01_Install
</DNNDirectory>
<ModuleFolder>
      MySilverlightApplication
</ModuleFolder>
</PropertyGroup>
<Target Name="DeployModule">
<CreateItem Include="$(MSBuildProjectDirectory)\$(OutputPath)\*.xap">
<Output TaskParameter="Include" ItemName="ModuleXap" />
</CreateItem>
<CreateItem Include="$(MSBuildProjectDirectory)\$(OutputPath)\*.dll">
<Output TaskParameter="Include" ItemName="ModuleAssemblies" />
</CreateItem>
<CreateItem Include="$(MSBuildProjectDirectory)\$(OutputPath)\*.pdb">
<Output TaskParameter="Include" ItemName="ModuleDebug" />
</CreateItem>
<Copy SourceFiles="@(ModuleAssemblies);@(ModuleDebug);@(ModuleXap)" 
DestinationFolder="$(DNNDirectory)\DesktopModules\$(ModuleFolder)\ClientBin" />
</Target>

Make sure you update the DNNDirectory and the ModuleFolder to your required values.  Do exactly the same for the SLTest project as well.  The condition in the Target is there so that this step is ignored when the solution is built with Team Foundation Server Build.

We need to do a very similar modification to the Web project as well.  There is a slight difference here in that this change also packages up the appropriate files and creates the .zip file for install and source and a total package as well so every time you build you have a deployable .zip file ready to install on any other DotNetNuke site.  You will need to install the MSBuild Community Tasks from to use the Zip target.

  1. Install the MSBuild Community Tasks
  2. Paste the following Immediately after the commented out code as before.
<Import Project="$(MSBuildExtensionsPath)\
       MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Target Name="AfterBuild" 
DependsOnTargets="DeployModule">
</Target>
<PropertyGroup>
<Major>01</Major>
<Minor>00</Minor>
<Build>00</Build>
</PropertyGroup>
<PropertyGroup>
<ModuleFolder>
      CHEAPLiveCosts
</ModuleFolder>
<DNNDirectory>
      C:\Sandboxes\CHEAP\CHEAP_Development
</DNNDirectory>
<PackageDirectory>
      C:\DotNetNuke\Packages
</PackageDirectory>
</PropertyGroup>
<Target Name="DeployModule">
<CreateItem Include="$(MSBuildProjectDirectory)\$(OutputPath)\*.dll">
<Output TaskParameter="Include" ItemName="ModuleAssemblies" />
</CreateItem>
<CreateItem Include="$(MSBuildProjectDirectory)\$(OutputPath)\*.pdb">
<Output TaskParameter="Include" ItemName="ModuleDebug" />
</CreateItem>
<Copy SourceFiles="@(Content)" 
DestinationFiles="@(Content -> '$(DNNDirectory)\
                         DesktopModules\$(ModuleFolder)\%(Identity)')" 
          SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ModuleAssemblies);@(ModuleDebug)" 
DestinationFolder="$(DNNDirectory)\bin" />
<Copy SourceFiles="@(Content)" 
DestinationFiles="@(Content -> '$(MSBuildProjectDirectory)\Package\%(Identity)')" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ModuleAssemblies);" 
DestinationFolder="$(MSBuildProjectDirectory)\Package\bin" />
<CreateItem Include="$(MSBuildProjectDirectory)\Package\**\*.*">
<Output TaskParameter="Include" ItemName="OutputContent" />
</CreateItem>
<Zip Files="@(OutputContent)" 
WorkingDirectory="$(MSBuildProjectDirectory)\Package" 
ZipFileName="$(ProjectName)_$(Major).$(Minor).$(Build)_Install.zip" />
</Target>

For formatting I have changed the DNNDirectory, PackageDirectory and ModuleFolder nodes.  Remove the line breaks otherwise you will get an error that says you have illegal characters in the path.

 

Now when you build your solution you should get a new folder under DesktopModules that has a ClientBin folder and the js file.  The ClientBin folder should have the .xap file and any associated dlls and pdb files.

Next we need to add the Usercontrol files to the .Web project so we can register and host the Silverlight application inside DotNetNuke.  Lately i have been using the same Web project to host both the Silverlight Application and the SLTest xap, I add a setting so that i can switch from real to integration test easily. 

  1. Add a new User Control to the Web project and call it View.ascx.
  2. Add another User Control and call it Settings.
  3. Add a reference to the DotNetNuke.dll file.
  4. Open up the View.ascx file and paste the following in:
    <script type="text/javascript">
    function onSilverlightError(sender, args) {
    var appSource = "";
    if (sender != null && sender != 0) {
                appSource = sender.getHost().Source;
            } var errorType = args.ErrorType;
    var iErrorCode = args.ErrorCode;
    if (errorType == "ImageError" || errorType == "MediaError") {
     return;
            }
    var errMsg = "Unhandled Error in Silverlight Application " + 
            appSource + "\n";
            errMsg += "Code: " + iErrorCode + "    \n";
            errMsg += "Category: " + errorType + "       \n";
            errMsg += "Message: " + args.ErrorMessage + "     \n";
    if (errorType == "ParserError") {
                errMsg += "File: " + args.xamlFile + "     \n";
                errMsg += "Line: " + args.lineNumber + "     \n";
                errMsg += "Position: " + args.charPosition + "     \n";
            }
    else if (errorType == "RuntimeError") {
     if (args.lineNumber != 0) {
                    errMsg += "Line: " + args.lineNumber + "     \n";
                    errMsg += "Position: " + args.charPosition + "     \n";
                }
                errMsg += "MethodName: " + args.methodName + "     \n";
            }
    throw new Error(errMsg);
        }
    </script>
     
    <div id="silverlightControlHost" style="position: relative; 
        width: 100%; height: 500px;
        vertical-align: top; z-index: 50;">
    <object data="data:application/x-silverlight-2," 
    type="application/x-silverlight-2"
    width="100%" height="100%">
    <param name="source" value='<%=SilverlightApplication %>'/>
    <param name="onError" value="onSilverlightError" />
    <param name="background" value="Transparent" />
    <param name="windowless" value="true" /> 
    <param name="minRuntimeVersion" value="3.0.40624.0" />
    <param name="autoUpgrade" value="true" />
    <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration: none">
     <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight"
     style="border-style: none" />
    </a>
    </object>
    <iframe id="_sl_historyFrame" style="visibility: 
            hidden; height: 0px; width: 0px;
            border: 0px"></iframe>
    </div>
  5. Open up the code behind file and modify it to look like this.
    public partial class View : PortalModuleBase
    {
    public string SilverlightApplication { get; set; }
    public string SilverlightInitParams { get; set; }
    protected void Page_Load(object sender, EventArgs e)
    {
    bool isChecked;
    var xapFile = "MySilverlightApplication.xap";if (bool.TryParse(Settings["IsTest"].ToString(), out isChecked))
    {
    if (isChecked)
    xapFile = "MySilverlightApplication.SLTest.xap";
    }
    // Register Silverlight.js file
    Page.ClientScript.RegisterClientScriptInclude(this.GetType(), "SilverlightJS", (String.Format(@"{0}{1}", 
    TemplateSourceDirectory, "/Silverlight.js")));// Set the path to the .xap file
    SilverlightApplication = String.Format("{0}/ClientBin/{1}", 
    TemplateSourceDirectory, xapFile);// Pass the Initialization Parameters to the Silverlight Control
    SilverlightInitParams = string.Format("Test={0}", "Some init stuff");
    }
    }
  6. In the settings file add a check box and save its value as a setting and call the setting “IsTest”.
  7. Now build you solution.
  8. You are now ready to register your module with DotNetNuke so go ahead and do that.

That’s it for Part 1.  You are now set up ready to start developing, designing and testing your application. 

Last week I attended Tech Ed Australia for the 5th time in a row and what a great time was had by all.  I did plenty of networking (really means beer drinking with delegates) and attended a bunch of cool sessions and I even delivered a lunch time session on my fave topics, DotNetNuke, Silverlight and RIA Services. 

I went to a bunch of Silverlight/UX sessions by Shane Morris (Microsoft) which were excellent and really brought home to me how much room for improvement there is the application/site design UX space.  So Im really going to be pushing some of his recommendations and finding ways to convince clients that there are better ways they just have to trust me :)

Jordan and Mahesh who both work with me at Readify gave an excellent session  on architectural considerations when building Silverlight applications and Jordan also had a going deeper with Silverlight session that I can’t wait to get my hands on the code from.

I also went to see John and Bronwen from Soul Solutions do a session on spatial data and the Bing Maps Control.  That was an eye opener.  I had heard about spatial in SQL 2008 but had never used it, now I’m going to use it on my current project to display some mined data.

Thanx everyone who I met and caught up with it was a great time.

  
DNN Template Maker

Artisteer - Web Design Generator

  
UsersOnline
MembershipMembership:
Latest New UserLatest:andpro
Past 24 HoursPast 24 Hours:0
Prev. 24 HoursPrev. 24 Hours:0
User CountOverall:32

People OnlinePeople Online:
VisitorsVisitors:12
MembersMembers:0
TotalTotal:12

Online NowOnline Now:
  
Talk to me
  
Good Books
My Logos


MVP Logo
From: 2004-2009

Lorraine Young's DNN Site

DotNetNuke Sponsor and Platinum Benefactor logo

 Microsoft ASP.net logo

microsoftcertifiedprofessional.gif

vicnet_logo.gif