Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

Sunday, 25 March 2012

The best of VS11 (Talk from Dev11 Launch)–Demos

Continuing on from my previous post on the best bits of Visual Studio 11, these are the demos that I showed as part of that talk.

Testing integration

As part of the new test runners integration, this demo shows how to run different testing frameworks easily within the same project. To show the testing frameworks in  Unit Test Manager windows, you will need to install the xUnit.net test runner and the nUnit test adapter via the Extension Manager or from the Visual Studio Gallery. br />Once these are installed when you build the application you will see the 3 different unit testing frameworks appearing in the Unit Test Manager pane.
Download the demo project.

OAuth and OpenID providers

One of the new features that appeared with WebMatrix 2 Beta Refresh  (now with added Unicorn sauce) and Web Pages 2 Beta are the OpenID and OAuth providers which allow you to build applications that support these systems such as Facebook, Twitter and Google logins.
Currently these are only available in ASP.NET WebPages but as part of the whole one ASP.NET concept these features will be released for ASP.NET MVC and WebForms later.
This project comes from the WebMatrix starter site. It shows how to use the Google, Twitter and Facebook logins.

Using Google OpenID

Google OpenID is one of the simplest providers to implement. In the _AppStart.cshtml file add the following after the WebSecurity.InitializeDatabaseConnection
OAuthWebSecurity.RegisterOpenIDClient(BuiltInOpenIDClient.Google);

In the Account/login.cshtml you can modify the social login section to look like
<section class="social" id="socialLoginForm">
<form method="post">
<h2>Use another service to log in.</h2>
<fieldset>
<legend>Log in using another service</legend>
<input type="submit" name="provider" id="google" value="Google" title="Log in your Google account" />
</fieldset>
</form>
</section>

Using Yahoo for OpenID is the same procedure

Using Twitter OAuth


Twitter OAuth requires you to create a Twitter application first because the Oauth provider requires a consumerKey and consumerSecret tokens.

Head over to the Twitter developers site and create a new app. If you are developing on localhost, Twitter may not accept this as a valid domain name so you can use the 127.0.0.1 loopback address instead for the WebSite field in the new application creation.

Once you have your Twitter application setup just take note of the Consumer Key and Consumer Secret values.

Back to _AppStart.cshtml add the following
OAuthWebSecurity.RegisterOAuthClient(BuiltInOAuthClient.Twitter,
consumerKey: "",
consumerSecret: "");

Insert you own consumerKey and Secret and then add the new input in your socialLoginForm and viola Twitter login integration!

Download the sample

Maps Helper

The final demo of the day is using the new Maps feature. This one also comes with WebMatrix 2 Beta Refresh but you can use it in MVC or WebForms right now. You need the new v2 of Microsoft.Web.Helpers assembly. You can get this assembly from the Bakery starter site in WebMatrix 2 Beta or from conveniently from here

To add a Google map to your website is as simple as the following lines of code.
<section id="map">
<div style="margin-bottom: 5px; font-weight: bold;">UiS Stavanger</div>
@Maps.GetGoogleHtml("Kjell Arholmsgt. 41, 4036 Stavanger", zoom: 15)
</section>
@Assets.GetScripts()
@Assets.GetStyles()

The map types that are supported right now are Google, Bing, Yahoo and MapQuest.

Download the sample.

Some handy information


You can get more on the new features of Web Pages 2 Beta here. Some of the new features that are in Web Pages now, will appear in the other parts of ASP.NET in line with the one ASP.NET vision.

Sunday, 19 September 2010

Bing Maps Silverlight control weather mash-up with yr.no - Part 3

In this post, I will take a look at creating a web service that will feed weather data to our Silverlight application that we have created in the previous posts
yr.noFirstly a little about where we are going to get the weather data from. yr.no is the joint online weather service from the Norwegian Meteorological Institute (met.no) and the Norwegian Broadcasting Corporation (NRK) and it offers weather forecasts in English (in addition to Norwegian Nynorsk and Norwegian Bokmål) for more than 700,000 places in Norway and 6.3 million places worldwide.

Yr.no supplies its weather data in XML format and you can get this XML by adding forecast.xml to the URL that is generated when you look for a location. For example for my current location in Stavanger, the generated address is http://www.yr.no/place/Norway/Rogaland/Stavanger/Stavanger/ and the XML for this http://www.yr.no/place/Norway/Rogaland/Stavanger/Stavanger/forecast.xml. This will generate most of the information in English.

To get this information in Norwegian you change the place to sted and use varsel.xml instead.

There are additional services provided by eKlima which you can use instead of yr.no if you wish. You can find more information on eKlima here.What I am going to do in this post, is creating the web service. So firstly we will create a web blank web application and a new web service to it.

In my project I have created a common library that contains a business layer, data layer and some classes. I have created a BaseInformation class which contains the basic information that is used by the map to create pushpins. This allows us to create more different objects which can use this class and make a generic method for adding pushpins with different data types, images etc.
1: public class BaseInformation
2:    {
3:        public int ID { get; set; }
4:        public string Title { get; set; }
5:        public double Latitude { get; set; }
6:        public double Longitude { get; set; }
7:        public string ImageLocation { get; set; }
8:        public string Description { get; set; }
9:        public string ToolTipImage { get; set; }
10:    }
The WeatherInfo class contains information specific to weather data and we will use this in our custom tooltip that we will be adding to the pushpin when its created on the map.
1:     public class WeatherInfo : BaseInformation
2:     {
3:         public string TempC { get; set; }
4:         public string FromTime { get; set; }
5:         public string ToTime { get; set; }
6:         public string SymbolName { get; set; }
7:         public string SymbolNumber { get; set; }
8:         public string WindData { get; set; }
9:         public string WindImage { get; set; }
10:     }
We have a simple class with an even simpler method that returns a generic list of strings that contains the XML addresses to download the information from yr.no.
1:    public IEnumerable<string> GetData()
2:         {
3:             return new List<string>
4:                        {
5:                            "http://www.yr.no/place/Norway/Rogaland/Sandnes/Sandnes/varsel.xml",
6:                            "http://www.yr.no/sted/Norge/Rogaland/Stavanger/Stavanger/varsel.xml",
7:                            "http://www.yr.no/place/Norway/Rogaland/Stavanger/Forus/varsel.xml",
8:                        };
9:         }
In the data layer we will create a new method that we will use to download the XML and manipulate it to our needs. The method stub looks like the following
1:       public static WeatherInfo GetWeatherInfo(string feed)
2:         {
3:            
4:         }
So first we need to download the XML file from yr.no. We use the XmlDocument class and use the Load method to load the XML.
1:             var xDoc = new XmlDocument();
2:             xDoc.Load(feed);
Since the XML file is quite large and contains multiple different elements we will create some datasets from the different elements and then use LINQ to join these datasets together and create the objects that we need.

So first creating the datasets from XML. This is a quick method which takes the name of the tag you want to turn into a dataset and the XMLDocument.
1:        private static DataSet GetDataSet(XmlDocument xDoc, string tagName)
2:         {
3:             var forecast = xDoc.GetElementsByTagName(tagName);
4: 
5:             var ds = new DataSet();
6:             var readerStream = new StringReader(forecast[0].OuterXml);
7:             ds.ReadXml(readerStream);
8:             return ds;
9:         }
We will create datasets from the following XML elements tabular, location, and forecast and we will join these together using LINQ. The following LINQ query is not the best in the world due its level of complexity so I apologise in advance if your eyes burn out of your skull and you decide to run off screaming “Oh the humanity”
1: from time in ds.Tables["time"].AsEnumerable()
2:                     join temp in ds.Tables["temperature"].AsEnumerable()
3:                         on time.Field<int>("time_id") equals
4:                         temp.Field<int>("time_id")
5:                     join symbol in ds.Tables["symbol"].AsEnumerable()
6:                         on time.Field<int>("time_id") equals symbol.Field<int>("time_id")
7:                     join windDirection in ds.Tables["windDirection"].AsEnumerable()
8:                         on time.Field<int>("time_id") equals windDirection.Field<int>("time_id")
9:                     join windSpeed in ds.Tables["windSpeed"].AsEnumerable()
10:                        on time.Field<int>("time_id") equals windSpeed.Field<int>("time_id")
That is the basic LINQ query and we will use that as the basis to generate a WeatherInfo object using the second part of the query.
1:  select new WeatherInfo
2:                     {
3:                         ID = time.Field<int>("time_id"),
4:                         TempC = temp.Field<string>("value") +"C",
5:                         FromTime = time.Field<string>("from"),
6:                         ToTime = time.Field<string>("to"),
7:                         SymbolNumber = symbol.Field<string>("number"),
8:                         SymbolName = symbol.Field<string>("name"),
9:                         WindData = string.Format("{0} {1} {2} m/s", windDirection.Field<string>("name"), windSpeed.Field<string>("name"), windSpeed.Field<string>("mps"))
10:                     }).FirstOrDefault();
The handy thing here is the cast of the WeatherInfo at the start to create an object that we can populate. Also the helper method FirstOrDefault(). If there is no records, it will create a blank object and we will return that. The Full method looks like this
1:     private static WeatherInfo GetWeatherInfoFromDataSet(DataSet ds)
2:         {
3:             return (from time in ds.Tables["time"].AsEnumerable()
4:                     join temp in ds.Tables["temperature"].AsEnumerable()
5:                         on time.Field<int>("time_id") equals
6:                         temp.Field<int>("time_id")
7:                     join symbol in ds.Tables["symbol"].AsEnumerable()
8:                         on time.Field<int>("time_id") equals symbol.Field<int>("time_id")
9:                     join windDirection in ds.Tables["windDirection"].AsEnumerable()
10:                         on time.Field<int>("time_id") equals windDirection.Field<int>("time_id")
11:                     join windSpeed in ds.Tables["windSpeed"].AsEnumerable()
12:                        on time.Field<int>("time_id") equals windSpeed.Field<int>("time_id")
13:                     select new WeatherInfo
14:                     {
15:                         ID = time.Field<int>("time_id"),
16:                         TempC = temp.Field<string>("value") +"C",
17:                         FromTime = time.Field<string>("from"),
18:                         ToTime = time.Field<string>("to"),
19:                         SymbolNumber = symbol.Field<string>("number"),
20:                         SymbolName = symbol.Field<string>("name"),
21:                         WindData = string.Format("{0} {1} {2} m/s", windDirection.Field<string>("name"), windSpeed.Field<string>("name"), windSpeed.Field<string>("mps"))
22:                     }).FirstOrDefault();
23:         }
In the GetWeatherInfo method we will finalise any properties in the object that we haven’t set before this. Such as the latitude and longitude. Now because in Norway the decimal separator is a comma (,) rather than the decimal point (.) this can cause some issues when doing conversions between strings and doubles. So we will use the NumberFormatInfo class to specify how the conversion should take place.
1: weatherInfo.Latitude = double.Parse(dr["latitude"].ToString(), NumberFormatInfo.InvariantInfo);
2: weatherInfo.Longitude = double.Parse(dr["longitude"].ToString(), NumberFormatInfo.InvariantInfo);
The full method for GetWeatherInfo is as follows
1: public static WeatherInfo GetWeatherInfo(string feed)
2:         {
3:             var xDoc = new XmlDocument();
4:             xDoc.Load(feed);
5: 
6:             var ds = GetDataSet(xDoc, "tabular");
7: 
8:             var weatherInfo = GetWeatherInfoFromDataSet(ds);
9: 
10:             var coDs = GetDataSet(xDoc, "location");
11:             var weatherDescDs = GetDataSet(xDoc, "forecast");
12: 
13:             weatherInfo.Description = weatherDescDs.Tables["time"].Rows[0]["body"].ToString();
14:             weatherInfo.Description = weatherInfo.Description.Replace(@"<strong>", "");
15:             weatherInfo.Description = weatherInfo.Description.Replace(@":</strong>", " -");
16: 
17:             weatherInfo.WindImage = "images/windsock.png";
18: 
19:             var dr = coDs.Tables["location"].Rows[1];
20: 
21:             weatherInfo.Latitude = double.Parse(dr["latitude"].ToString(), NumberFormatInfo.InvariantInfo);
22:             weatherInfo.Longitude = double.Parse(dr["longitude"].ToString(), NumberFormatInfo.InvariantInfo);
23: 
24:             weatherInfo.Title = coDs.Tables["location"].Rows[0]["name"].ToString();
25:             weatherInfo.ToolTipImage = "images/temp.png";
26:             weatherInfo.ImageLocation = string.Format("images/{0}.png", weatherInfo.SymbolNumber);
27: 
28:             return weatherInfo;
29:         }
In the Business layer we have a simple method that creates a list of these weatherInfo objects based on the list of feeds that I talked about earlier.
1:  public static List<WeatherInfo> GetAllWeatherInfo()
2:         {
3:             var allFeeds = new FakeWeatherFeeds().GetData();
4: 
5:             var weatherInfos = allFeeds.Select(WeatherInfoDl.GetWeatherInfo).ToList();
6: 
7:             return weatherInfos;
8:         }
Instead of a foreach loop I am using some LINQ to pass in the method and create the list. And finally the web service method looks like the following.
1: [WebMethod]
2:         public List<WeatherInfo> GetAllWeather()
3:         {
4:             return WeatherInfoBl.GetAllWeatherInfo();
5:         }
In the next post we will put this altogether with the Silverlight map application and binding the list of objects to the map.

Friday, 17 September 2010

Bing Maps Silverlight control weather mash-up with yr.no - Part 2

Following on from the previous post will now look at adding pushpins to the map.

PushPins inherit from the UIElement class meaning that any UIElement can be used as a pushpin. So that means we can use Images as pushpins. So first lets add a normal pushpin to the map.

We are going to add a button to our map and wire up an event handler to add a pushpin to the map. In MainPage.xaml in your grid add a button

In the Button_Click event we are going to create a new MapLayer and add the pushpin to it. The pushpin is going to mark the location of the Empire State Building in downtown New York. It uses the following decimal co-ordinates. In MainPage.xaml.cs



using Microsoft.Maps.MapControl;

private void Button_Click(object sender, RoutedEventArgs e)
{
MapLayer mapLayer = new MapLayer();
bingMap.Children.Add(mapLayer);

Location location = new Location { Latitude = 40.748687, Longitude = -73.985549 };

Pushpin pushpin = new Pushpin {Location = location};

mapLayer.Children.Add(pushpin);

}


What we have done here is created a new MapLayer object and added that to the children collection of the parent Map. We then create a location object with the co-ordinates of the Empire State Building and create a new pushpin at this location. We finally add the pushpin to the map layer which shows it on the map.



This is a bit simple and being honest a bit boring. So lets mix it up a bit.


450px-Empire_State_Building_from_the_Top_of_the_RockSince we are using the Empire State Building, I am going to use an image of this renowned landmark and use that as its pushpin. So a quick look on wikipedia for an image we find the one on the left (http://en.wikipedia.org/wiki/File:Empire_State_Building_from_the_Top_of_the_Rock.jpg). We will use this image and load it as the pushpin in our map.

Just like before we are going to add a new button to our map. But we are going to encapsulate both buttons in a StackPanel so that we can arrange them easily.



I have downloaded the image and created a new folder in my Silverlight application called images and placed the image in there. I renamed it to Empire.jpg just so I wouldn’t have as much to type. If you want to use the image directly from Wikipedia you will need the following URL http://upload.wikimedia.org/wikipedia/commons/c/c7/Empire_State_Building_from_the_Top_of_the_Rock.jpg and use the UriKind.Absolute option.


In MainPage.xaml you will need to replace your button code with the following.


In MainPage.xaml.cs


So in this code sample, we are doing something very similar to the previous sample. First you create a new layer and as before add it to the parent map. Next you create a new image with a source from either a relative or absolute Url and set its height and width. The difference now is that we use the MapLayer methods to set the position for the Image we created and finally add the image to the maplayer object.

In the next post, I will go through getting weather data from yr.no and using LINQ to create weather data objects that can be rendered on the map using the above methods.

Tuesday, 14 September 2010

Bing Maps Silverlight control weather mash-up with yr.no - Part 1

So I have shown before in a previous post how to use the AJAX version of the Bing Maps control. Now I am going to show the new version of the map control that is in Silverlight. Microsoft have released the Bing Maps Silverlight Control SDK which you use to add map control to your website.

Now in comparison to the AJAX version, this new version is smooth. In fact its much easier to program with and you can do a lot of nice things with it. One of the biggest advantages is the fact that pushpins on the map can be anything that inherits from the UIElement class. This means you can have controls, user controls or images as pushpins. This functionality in itself extends the possibilities of customising the UI experience for your users. Additionally there is added benefit of the rich UI features of Silverlight that can allow to extend aspects of the map to your liking.

So how do you start all this great stuff.

  1. Go to the download site and download and install the SDK
  2. Surf to the Bing Maps Account portal and create a new account
  3. Generate a new developer key
  4. Open Visual Studio and get started!
    1. Note that for all this I am using Visual Studio 2010 and Silverlight 4. You can download the Silverlight 4 Tools for VS 2010 from here

So lets do that

1

First we are going to create a new Silverlight Application. If you get prompted download the Silverlight Developer runtime and install it.

Next choose the option to host the Silverlight application in an ASP.NET Web application. This just adds the boilerplate code for you to show your application in a web page.

Now you should have a nice simple application with your basic Silverlight application loaded and standard ASP.NET web application created.

You will need to add the Bing Map DLLs as references in your project. Right click on the references folder and browse to where you installed the SDK. If you chose the default installation path you will find it in your Program Files directory or (x86) directory on 64bit Windows in Bing Maps Silverlight Control. You will find the DLLs you need in the V1\Libraries directory. You will need both of them which are

  • Microsoft.Maps.MapControl.Common
  • Microsoft.Maps.MapControl

Once they are added we can use the Bing Maps Silverlight Control in our Silverlight application.

Add the namespace to your XAML code

xmlns:Maps="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"

You can reference the maps by using the Maps prefix. Inside in the grid add a map control

<Grid x:Name="LayoutRoot" Background="White">
   <
Maps:Map Name="bingMap" CredentialsProvider="Your key here"></Maps:Map>
</
Grid>
 
You need to create a new key in the Bing Maps Account portal if you haven’t already done so and then copy it into the CredentialsProvider so that your map will work correctly otherwise you will see an invalid credentials warning on your map.
Your code should look like this
<UserControl x:Class="MashupMapApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Maps="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<
Grid x:Name="LayoutRoot" Background="White">
<
Maps:Map Name="bingMap" CredentialsProvider="your key"></Maps:Map>
</
Grid>
</
UserControl>

Pressing F5 to debug should bring up the Silverlight map control in its test page.


In the next post, I will go through adding standard and custom pushpins to the map.

Tuesday, 11 May 2010

Post DDD Scotland

Logo Another conference down and this time I was lucky to a speaker at this one. The event was Developer Days Scotland a locally organized event with local community speakers for the local community. As you can guess its about being local! Its a great event showcasing the best of the the UK talent with the odd foreign guest thrown into the mix (like myself). It was superbly organized by Colin Angus Makay, Andy Gibson, Craig Murphy and supported on the website by Phil Winstanley.

The idea behind DDD is that the sessions are submitted by the speakers and the community chooses them in the form of a vote. That way the community decides what it wants to see rather than the organisers so it remains a more democratic system. Also for the speakers it means that someone thought enough of your talk to vote for it so they might actually show up!

My session. Photo by Craig Murphy (http://www.flickr.com/photos/craigmurphy/) I presented the Defensive Programming 101 session to a fairly packed room who thankfully had great stories to share and gave me some new insights into the topic. The slide deck for this presentation is available here and the code demos are here.

I managed to poke my head in at a few sessions and caught Martin Hinshelwood’s session on Scrum with Team Foundation Server 2010 which was very informative and also gave me a load of information on how to upgrade our TFS 2005 server to 2010. 

In the speakers lounge, at times I was the only non MVP in the room, for which I got some friendly abuse and the jibes of “Sorry are you under NDA??”. It was great to be able to talk with some extremely knowledgeable people and get some extremely helpful advice on different topics that I had been looking at. Chatting to Liam Westley and I managed to get some great information on accessories for my HP laptop.

On the Friday night before the event, there was a speaker and organiser dinner where we witness to a flammability test on hair products ably demonstrated by Phil and Seb Lambla. As a public service announcement, certain hair products are highly flammable and should be kept well away from naked flames.

The post event dinner was excellent and we were treated to a very tasty Italian meal where due to some clever spotting by Phil and Liam some of us managed to order steaks much to the chagrin of the other people at our table.

Overall it was an excellent event, well run and organised and that showed in the final result. Enthusiastic speakers whose talks where chosen by the people who go to them. I think it gave me ideas and that we should run something similar in Norway. I suppose that is how it all starts really!

Photos from the event can be seen here

Thursday, 8 April 2010

Dynamic User Controls – Part 5 – Refactoring the code

The previous post dealt with getting the user controls to be dynamic with both addition and removal methods for the controls. In this post we will look at doing code reduction and making the code reusable with a minimum amount of effort. We created only one type of user control in the previous set of examples, but it is easy to image a situation where you would be creating more than one type type of control and in this scenario it would be handy not to have to write a massive amount of code for each control but use generic methods.
So that is what we are going to do in this post, some good old fashioned code refactoring. First we will look at the interface that is being used by user control and how it can be made generic. The original code for the interface is as follows
We can change the interface to a generic type like so

We will now change our existing PersonControl to use this new interface. The existing code is like this

And to implement the new interface we change it to the following

It is a simple as that. Any new control we want to use should use the new interface and whichever entity it is being used to represent.

We will now take a look at the method that enumerates the repeater items for the items values. Before we had this code

If we take a look at the code, we can make it generic by supplying a reference the type of entity in the list we want to return and also the name of the placeholder control and the repeater we want to use as parameters to a method. So by applying that logic we end up with this code.

As you can see now we have a nice reusable method. But we can further change this code and use some LINQ to replace the foreach statement. It will now look like this

To use the method we would replace the List<Person> CurrentPersonList as follows

Now we will look at changing the methods that add items to the repeater. Again we will take a look at the original code that was used in the last post.

If we look at what we could do here, we could change the code so that we supply the entity type that we want to use along with the current list of items, and the repeater that want to bind to. There is one slight additional change we need to do and that is when you use a generic method to create a new instance of a type, you must specify in the method declaration, the new constraint.

We can now replace the contents of the AddPerson method the the following

We look at the ItemCommand method which can generic the same way we have refactored the AddItem methods. The original code looks like this

Again we will look at the code and see what is reusable. If we supply the RepeaterItemEventArgs, a generic list of items and the repeater to bind to. So with that we end up like this

To use the method in the RemovePerson event, we change the contents of the repeater ItemCommand method to

Now the final piece of the puzzle is to change the ItemCreated event and the ItemLoad events. We can merge the ItemLoad into the ItemCreated event using a lambda expression. So the two methods before we start look like this.

If you use the lambda operator => we can merge the the ItemLoad and ItemCreated events into one method.

Now that we have it merged we can refactor this method into a generic method. We can replace the entity parts and if we pass in the name of the placeholder control and the path of the control to load as well as the RepeaterItemEventArgs we can re-use the method.

And there we have it, the code distilled down so that we can reuse certain methods if we have more than one repeater with different type of controls. So that is the end of the series of posts on dynamic controls. You can get the code bits from the post in the series

Tuesday, 6 April 2010

Dynamic User Controls – Part 4 – Data Bound User Controls

We have shown in the previous post how to dynamically add a standard ASP.NET control to a page. Now we are going to look at how to make dynamically add user controls to the page and also make them databound. First of all we need to look at our entities. We have a very simple Person class, which has a couple of properties.
 public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

As you can see it is very basic. The user control we will be using, models this in front end code.
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PersonControl.ascx.cs" Inherits="DynamicUserControls.Web.UserControls.PersonControl" %>
<table>
<tr>
<td>First Name</td>
<td><asp:TextBox ID="txtFirstName" runat="server" Width="300px"></asp:TextBox></td>
</tr>
<tr>
<td>Last Name</td>
<td><asp:TextBox ID="txtLastName" runat="server" Width="300px"></asp:TextBox></td>
</tr>
</table>

The textboxes just represent the the strings in the class. We also have an interface which we will implement in the user control.
public interface IPersonControl
{
Person Data { get; set; }
void DataBind();
}

To implement this in our user control we use the following code
public partial class PersonControl : UserControl, IPersonControl

If you are using ReSharper you can press ALT+Enter to implement the members, although I am fairly sure Visual Studio 2008 and above does this as well. The codebehind of the user control looks like this
public partial class PersonControl : UserControl, IPersonControl
{
private Person _data;

public override void DataBind()
{
if (Data != null)
{
txtFirstName.Text = Data.FirstName;
txtLastName.Text = Data.LastName;

_data = null;
}

base.DataBind();
}


public Person Data
{
get { return _data ?? (
_data = new Person
{
FirstName = txtFirstName.Text,
LastName = txtLastName.Text
}); }
set { _data = value; }
}
}

We have a new property called Data which allows us to get and set the information in the control. We also have a DataBind method which is fired when the control being databound.

The Data property when set, sets a private variable called _data which we will come back to later. When we DataBind our control, we set the values of the textboxes to the appropriate values of the class and we null the _data variable so that each time when we get Data property, we check to see if _data is null i.e. has been databound. We can then assign the values of the textboxes to a new instance of the class and return it. In the code above, I am using a null-coalescing operator (??) to shorten the code a bit.

That is all we need to do with the user control for the moment. We will now swap back to the Default.aspx page and add a new tab to the page.
<cc1:TabPanel ID="tabPeople" runat="server" HeaderText="Person User Control Demo">
<ContentTemplate>
<asp:Repeater ID="rptPeople" runat="server">
<ItemTemplate>
<asp:PlaceHolder ID="phPerson" runat="server"></asp:PlaceHolder>
</ItemTemplate>
</asp:Repeater>
<asp:Button ID="btnAddPerson" runat="server" Text="Add a person" CausesValidation="false" OnClick="AddPerson" />
</ContentTemplate>
</cc1:TabPanel>

As you can see it is very similar to the tab we created for the textbox example in the previous post. We will also create a new page property in the code behind file called CurrentPersonList
List<Person> CurrentPersonList
{
get
{
var items = new List<Person>();

foreach(RepeaterItem item in rptPeople.Items)
{
var control = (PersonControl) item.FindControl("phPerson").Controls[0];
items.Add(control.Data);
}
return items;
}
}

We can reference our Data property in our control by casting the control we access in the placeholder as a PersonControl. And because the Data property is a Person class we can add it to our generic list.

Adding a new control is a case of getting the current list, adding a new Person class, set the data source of the repeater to the new list and the binding the list again.
protected void AddPerson(object sender, EventArgs e)
{
var currentData = CurrentPersonList;
currentData.Add(new Person());
rptPeople.DataSource = currentData;
rptPeople.DataBind();
}

As you can see we take a copy of the current data, add a new one and rebind the repeater. Now we need to override the ItemCreated event of the repeater and change it so that it loads a user control with the correct data.
private void RptPeopleItemCreated(object sender, RepeaterItemEventArgs e)
{
var data = (Person) e.Item.DataItem;
if(!Equals(data,default(Person)))
{
var placeholder = (PlaceHolder) e.Item.FindControl("phPerson");
var control = (PersonControl) LoadControl("~/UserControls/PersonControl.ascx");
control.Data = data;
placeholder.Controls.Add(control);
}
else
{
e.Item.Load += PersonItemLoad;
}
}

First we cast the DataItem as a Person object and then we check to see if its a blank object i.e. default. If its not a blank object, we need to load the control and set its Data property. So we need to get the placeholder control in the ItemTemplate. We need to then load an instance of our user control and set the Data property with the casted DataItem. We add the control to the Controls collection of the placeholder. On the other hand if the object is blank we just want to load the control with no Data property set. So we use the Item.Load event to achieve this.
var literal = (RepeaterItem)sender;
var placeHolder = literal.FindControl("phPerson");
var control = (PersonControl)LoadControl("~/UserControls/PersonControl.ascx");
placeHolder.Controls.Add(control);

So its very similar to the ItemCreated event. We can now run the page and it will add a user control to the page when you click the button. That’s the first part of the story, the second part is being able to remove the item from the page. There is no point in being able to add controls if you cannot remove them-

To do this we are going to add a LinkButton to the user control so that it now looks like this
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PersonControl.ascx.cs" Inherits="DynamicUserControls.Web.UserControls.PersonControl" %>
<table>
<tr>
<td>First Name</td>
<td><asp:TextBox ID="txtFirstName" runat="server" Width="300px"></asp:TextBox></td>
</tr>
<tr>
<td>Last Name</td>
<td><asp:TextBox ID="txtLastName" runat="server" Width="300px"></asp:TextBox>&nbsp;<asp:LinkButton ID="lnkRemove" runat="server" OnClick="RemoveItem" Text="Remove this item"></asp:LinkButton></td>
</tr>
</table>

With the new link button we have a handler that will raise an event which we can handle in our repeater. We will send the command “Remove” to whatever handler is listening.


     protected void RemoveItem(object sender, EventArgs e)
{
RaiseBubbleEvent(this, new CommandEventArgs("Remove", null));
}

To handle this event, we need to use the ItemCommand event the repeater. The ItemCommand event is fired whenever a command is received by the repeater.



if (e.CommandName != "Remove") return;
var index = e.Item.ItemIndex;
var currentData = CurrentPersonList;
currentData.RemoveAt(index);
rptPeople.DataSource = currentData;
rptPeople.DataBind();

We check to see if the CommandName is the correct one, and if not we will just break out of the event. We find the ItemIndex of the item that raised the event and because we are using a list, it will be in the same position in that list. We can then remove the item at that position.

In the next post we will look at refactoring the code and making it generic so that you can use the same methods for all the repeaters etc.

Sunday, 4 April 2010

Dynamic User Controls – Part 3 – Using a repeater

Continuing on from my previous posts (parts 1 and 2) this post will detail how to use a repeater to add a textbox control dynamically.

First off we will create a new tab that will contain a button control and a repeater that will contain a placeholder control


The idea are working with is the following. The placeholder will contain only 1 control at a time, be a composite user control or in this example a textbox control. We will bind a generic list of strings to the repeater and using the ItemCreated event of the repeater, we will program it to create a textbox and put the contents of the string item into the text property of the textbox.

We will also create a page property that returns a generic list of strings. This will be got from the repeater but looping through each item in the Items collection and finding the textbox in the placeholder control and getting its text property. Each time we want to add a new textbox, we will use this page property to get all the current values and then add a new blank string to this list. After we have done that, we will rebind the repeater with the new list. By using this method to create our textboxes, we can store the values easily and manage state easily as well.



So now the page property

What we are doing here, is firstly creating a new list on line 5. Then we are looping through all the RepeaterItems in the Items collection of the Repeater. In the item, we are finding the textbox that we have added. We do this by using the FindControl method of the RepeaterItem to find the specified placeholder control and then accessing its Controls collection and casting that object as a textbox control. We dont need to cast the placeholder control because it inherits from the Control object which has a Controls collection. Once we have that textbox we add the value of the Text property to our list. Once we have looped through all the RepeaterItems we will return the list.

Now to the OnClick event handler of the button which will add a new textbox

So we first create a new list called currentData and assign it to the value of CurrentTextBoxData which is the page property we created just before. We then add a blank string to the list. Once we have done that, we set the repeater’s data source to the new list and then databind it.

Why do we create a new list and what doesn’t the CurrentTextBoxData property have a setter?. The answer to both these questions is the following. Firstly we are using the repeater as a date store to store the current values in the textbox. Each time we want to add to it, we should get a copy of the current values and add the new value to this copied list. Finally we will replace the current values with the new copy of the values. So in this way, there is no need for a setter per se because the databind event acts as the setter since it places all the values in the repeater.



We will continue with the ItemCreated event of the repeater. You will need to either manually add the event to the repeaters declaration in the ASPX page or add it in the Page.OnInit entry in the code behind.

Firstly, we cast the DataItem to a string. We know this is a string because that is what are binding to the repeater control. We then create a new instance of a textbox control and set its text property to the string in the DataItem. Now we create a placeholder and assign it to the placeholder in the repeater item using the FindControl method. We then add our newly created textbox to the placeholder. This how we dynamically create our controls.

Running this will now allow you to click the button and that will add a new control. You can edit the values of the textboxes and they will survive postback because each time we need to create new ones, we get the current values and then recreate them all again.

The next post will deal with adding dynamic databound user controls and also how to remove dynamic controls as well as bubbling events up from your user control to be handled by the repeater.

The code bits for all these posts can be found in the first post here

Friday, 2 April 2010

Dynamic User Controls – Part 2 – The Wrong Way

Following on from my session intro post, this post will deal with the “wrong” way to add controls to the page. Its wrong in the traditional sense, it just doesn’t provide as much control as you would like. Also it makes it difficult to manage the number of controls and events on the page. So lets get into it. Some prerequisites. I am using the AJAX Control Toolkit for my demos. Specifically I am using the AJAX Tab control to show off the demo. If you are not using one of the versions I uploaded, you will need this toolkit to follow the code I will be writing. Or you can just not use it and take out the tabContainer code :) Starting off, I have the standard Default.aspx page with a ScriptManager control on it. Also on the page I have an instance of the AJAX Control Toolkit TabControl. In the TabControl I have one tabPanel with an ASP PlaceHolder control and an ASP Button Control
<cc1:TabPanel ID="tabStart" runat="server" HeaderText="Start">
<ContentTemplate>
<asp:PlaceHolder ID="phControls" runat="server" />
<br />
<asp:Button ID="btnAddControl" runat="server" Text="Add a new text box"
CausesValidation="false" OnClick="AddDemoControl"/>
</ContentTemplate>
</cc1:TabPanel>

In the code behind of the page I have the Page_Load event and the event handler for the OnClick event of the ASP button.
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack) return;
//Add 2 new textboxes to the specified placeholder
phControls.Controls.Add(new TextBox());
phControls.Controls.Add(new TextBox());
}

protected void AddDemoControl(object sender, EventArgs e)
{
phControls.Controls.Add(new TextBox());
}

When the page is loaded for the first time, 2 textbox controls are added to the page. When you click the button, only one textbox is shown. This is because the check to see if the page is a postback stops the first two textboxes from being loaded. Removing this line will mean that when you click the button three textboxes are shown on screen.

If you keep clicking the button, only three textboxes will be shown. This is because the controls do not survive the postback event and so are recreated new every time. So in this way this is one of the reasons I will use the repeater model to create dynamic controls.

How to do this using a basic textbox control will be covered in the next post.

Wednesday, 24 March 2010

Setting a TreeView node in focus

Lately I have been working on a couple of projects and one of them involved a fairly long ASP.NET TreeView control. The requirement was to have it so that when a user clicked on a node, the TreeView would have that selected node in focus after the postback. This proved to be a little troublesome as there is a built in method called HideSelection in the Windows Forms version however that property does not exist in the ASP.NET version of the control.
So while browsing the web, I noticed a couple of people were having the same issue with this control. I managed to hack together some JavaScript using some of the existing script provided by the .NET framework. The following code sets the currently selected TreeViewNode into focus.
function SetSelectedTreeNodeVisible(controlID) {
var theForm = document.forms['aspnetForm'];
if (!theForm) {
theForm = document.aspnetForm;}
var selectedID = theForm.elements[controlID];
if (selectedID != null) {
var selectedNode = document.getElementById(selectedID.value);
if (selectedNode != null) { selectedNode.scrollIntoView(true); }
}
}

To use this code in your page, just call the function like so
SetSelectedTreeNodeVisible('<%= TreeViewName.ClientID %>_SelectedNode');

Replace TreeViewName with the name of your TreeView control. This code should be called at the end of the page so that the treeView has had time to load.