A Simple Silverlight Configurator
Live example:
The application allows your users to query a collection (over web services), using drop downs and sliders. The results appear in a animated display.
You may ask, why not just use Microsoft Pivot? The main reasons are:
- This one uses View Model / MVVM so the user interface can be completely designed by a designer.
- There is not a lot of code, you can adapt this for your own use quickly.
- This does not focus on images. Microsoft Pivot is designed to display high quality images. However, you can adapt this to display images.
- This is a smaller application and it loads faster.
- This is designed to work with your data coming directly from a database.
If the above does not apply to you, you may wish to use Microsoft Pivot instead.
The Application
Basically you choose Gender, Weight, and Age, and the people who fall into the selected criteria display as boxes that are animated, and re-order themselves.
With help from Adam Kinney
Funny story when I was hanging out on the Microsoft campus. I ran into Adam Kinney (Former Microsoft Evangelist). I told him about a idea I was working on for a Silverlight configurator, and if he had any ideas on how to best display the data. He suggested using the Silverlight Wrap Panel with the FluidMoveBehavior. He started to describe it to me, and I started taking notes, then he felt sorry for me, and since he had his laptop, he whipped up some code and emailed it to me.
The boxes flying around, and fading in and out, is from his example. It is all done with no code, and everything can be easily altered using Microsoft Expression Blend.
Building The Application – The Web Application
First, we we create a People collection:
public static class People
{
public static List<Person> GetPeople()
{
List<Person> colPeople = new List<Person>();
colPeople.Add(new Person { Name = "Tom", Description = "Cook", Age = 44, Weight = 224, Gender = "M" });
colPeople.Add(new Person { Name = "Mary", Description = "Taxi Driver", Age = 22, Weight = 114, Gender = "F" });
colPeople.Add(new Person { Name = "Jane", Description = "Nurse", Age = 28, Weight = 109, Gender = "F" });
colPeople.Add(new Person { Name = "Lex", Description = "Doctor", Age = 51, Weight = 194, Gender = "M" });
colPeople.Add(new Person { Name = "Paul", Description = "Saleman", Age = 23, Weight = 208, Gender = "M" });
colPeople.Add(new Person { Name = "Frank", Description = "Butler", Age = 32, Weight = 184, Gender = "M" });
colPeople.Add(new Person { Name = "Joe", Description = "Programmer", Age = 30, Weight = 290, Gender = "M" });
colPeople.Add(new Person { Name = "John", Description = "Clerk", Age = 25, Weight = 202, Gender = "M" });
colPeople.Add(new Person { Name = "Mark", Description = "Student", Age = 14, Weight = 102, Gender = "M" });
colPeople.Add(new Person { Name = "Paula", Description = "Student", Age = 12, Weight = 98, Gender = "F" });
colPeople.Add(new Person { Name = "Beth", Description = "Student", Age = 18, Weight = 119, Gender = "F" });
colPeople.Add(new Person { Name = "Jack", Description = "Butcher", Age = 31, Weight = 114, Gender = "M" });
colPeople.Add(new Person { Name = "Bob", Description = "Student", Age = 13, Weight = 114, Gender = "M" });
colPeople.Add(new Person { Name = "Robert", Description = "Student", Age = 12, Weight = 99, Gender = "M" });
colPeople.Add(new Person { Name = "Tony", Description = "Student", Age = 10, Weight = 105, Gender = "M" });
colPeople.Add(new Person { Name = "Ray", Description = "Lawyer", Age = 35, Weight = 212, Gender = "M" });
return colPeople;
}
}
Next, we create a web service that will query this collection:
public class WebService : System.Web.Services.WebService
{
[WebMethod]
public List<Person> SearchPeople(string Gender, int AgeHigh, int AgeLow, int WeightHigh, int WeightLow)
{
List<Person> colPeople = new List<Person>();
var result = from objPeople in People.GetPeople().AsQueryable()
where objPeople.Age <= AgeHigh
where objPeople.Age >= AgeLow
where objPeople.Weight <= WeightHigh
where objPeople.Weight >= WeightLow
select objPeople;
if (Gender != "All")
{
colPeople = (from finalresult in result
where finalresult.Gender == Gender
select finalresult).ToList();
}
else
{
colPeople = result.ToList();
}
return colPeople;
}
}
Note, this is where you can have your web service simply connect to your database.
That's it! Hopefully you find all this easy so far, and can see where you can easily alter this to pull in your own data.
Building The Application – The Silverlight Application
The Model, that calls the web service, is very simple:
public class Model
{
#region SearchPeople
public static void SearchPeople(string Gender, int AgeHigh, int AgeLow, int WeightHigh, int WeightLow,
EventHandler<SearchPeopleCompletedEventArgs> eh)
{
WebServiceSoapClient WS = new WebServiceSoapClient();
WS.Endpoint.Address = new EndpointAddress(GetBaseAddress());
WS.SearchPeopleCompleted += eh;
WS.SearchPeopleAsync(Gender, AgeHigh, AgeLow, WeightHigh, WeightLow);
}
#endregion
#region GetBaseAddress
private static Uri GetBaseAddress()
{
string strBaseWebAddress = App.Current.Host.Source.AbsoluteUri;
int PositionOfClientBin =
App.Current.Host.Source.AbsoluteUri.ToLower().IndexOf(@"/clientbin");
strBaseWebAddress = Strings.Left(strBaseWebAddress, PositionOfClientBin);
Uri UriWebService = new Uri(String.Format(@"{0}/WebService.asmx", strBaseWebAddress));
return UriWebService;
}
#endregion
}
The View Model, contains all the logic for the application. Hopefully, this demonstrates the power of View Model / MVVM style programming and how you can end up using less code than if you coded this using code behind.
First, we create a property to hold the main collection. This collection is the only thing that Adam Kinney's code is looking at. All the rest of the code in the application is simply changing this collection:
#region ColPeople
private ObservableCollection<Person> _ColPeople = new ObservableCollection<Person>();
public ObservableCollection<Person> ColPeople
{
get { return _ColPeople; }
private set
{
if (ColPeople == value)
{
return;
}
_ColPeople = value;
this.NotifyPropertyChanged("ColPeople");
}
}
#endregion
Next, we have Properties to hold the other parameters. These parameters are what the Sliders and the ComboBox are bound to:
#region AgeLow
private int _AgeLow = 10;
public int AgeLow
{
get { return _AgeLow; }
set
{
if (AgeLow == value)
{
return;
}
_AgeLow = value;
this.NotifyPropertyChanged("AgeLow");
}
}
#endregion
#region AgeHigh
private int _AgeHigh = 100;
public int AgeHigh
{
get { return _AgeHigh; }
set
{
if (AgeHigh == value)
{
return;
}
_AgeHigh = value;
this.NotifyPropertyChanged("AgeHigh");
}
}
#endregion
#region WeightLow
private int _WeightLow = 90;
public int WeightLow
{
get { return _WeightLow; }
set
{
if (WeightLow == value)
{
return;
}
_WeightLow = value;
this.NotifyPropertyChanged("WeightLow");
}
}
#endregion
#region WeightHigh
private int _WeightHigh = 300;
public int WeightHigh
{
get { return _WeightHigh; }
set
{
if (WeightHigh == value)
{
return;
}
_WeightHigh = value;
this.NotifyPropertyChanged("WeightHigh");
}
}
#endregion
#region Gender
private string _Gender = "All";
public string Gender
{
get { return _Gender; }
set
{
if (Gender == value)
{
return;
}
_Gender = value;
this.NotifyPropertyChanged("Gender");
}
}
#endregion
Now, for the complex part (it is not really complex, that is my point). This is the method that calls the Model. Note, that it does not take parameters, it simply passes to the Model, whatever the values are set for the various properties:
#region GetPeopleFromModel
private void GetPeopleFromModel()
{
Model.SearchPeople(Gender, AgeHigh, AgeLow, WeightHigh, WeightLow, (Param, EventArgs) =>
{
if (EventArgs.Error == null)
{
var DeletedPeople = (from people in ColPeople
where !EventArgs.Result.Select(x => x.Name).Contains(people.Name)
select people).ToList();
foreach (var Person in DeletedPeople)
{
ColPeople.Remove(Person);
}
var NewPeople = from people in EventArgs.Result
where !ColPeople.Select(x => x.Name).Contains(people.Name)
select people;
foreach (var Person in NewPeople)
{
ColPeople.Add(Person);
}
}
});
}
#endregion
This class implements INotifyPropertyChanged, so we use this method to call the GetPeopleFromModel() method whenever one of the properties changes:
#region MainPageModel_PropertyChanged
void MainPageModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (
e.PropertyName == "AgeLow" ||
e.PropertyName == "AgeHigh" ||
e.PropertyName == "WeightLow" ||
e.PropertyName == "WeightHigh" ||
e.PropertyName == "Gender")
{
GetPeopleFromModel();
}
}
#endregion
That's it!
If you are not used to View Model Style programming, you may be wondering “how do the properties for things like Age and Weight get set?”. The answer is, that we will bind controls in the View to these properties. All of that is done in Microsoft Expression Blend, using no code at all.
The View looks like this in Expression Blend:
This diagram shows what is bound to what:
Note, that the Range Sliders are from and article posted by Josh Twist.
Further Reading
Adam Kinney suggested I pass these URL’s on if you want to know how he did the animated list: