Thursday, December 15, 2011

Learning MVC: SQL CE Membership Provider for ASP.NET

I tried uploading my MVC application on the web server for the first time. That proved to be slightly more complicated compared to using a server in the local network. I chose www.discountasp.net as a provider due to several positive reviews I've found. Registration, of course, was easy, and accessing the "control panel" too. The machine I develop on, however, is behind the proxy and the corporate firewall, so for deployment I had to choose publish to local PC and then copy the files over to my home pc and then to the provider's ftp. Doing the application part was really easy - just copied the files to the ftp, deleted the default "index.htm" page, stopped and started the web site from the control panel, and I could see the application main page straight away. The troubles, as I expected, started with database. I tried modifying connection strings to point correctly to the corresponding folders on the host, but sometimes I got back the 500 error, and sometimes something slightly more meaningful like that one:

I was slowly going through the host's manuals and the knowledge base, but what solved all my issues was the SQL Compact ASP.NET Membership, Role and Profile provider. Essentially, that's just four files that need to be added to the project's App_Code folder. Next, the web.config file has to be modified according to instructions on Codeplex or in the author's blog.

And then it just works - no need for SQL Express, no additional configuration - just copied the files over to the host. Extremely positive experience.

by . Also posted on my website

Friday, December 9, 2011

Learning MVC: User Data in a Partial View

After spending some time trying to apply a somewhat decent layout to the site, I decided to put some common information in the sidebar. This information would be the same on every page, but will be visible only to logged in users. Initially I rendered it as a section, but that's clearly inefficient as the section tag has to be added to each and every view. That's against the DRY principle of MVC - Do not Repeat Yourself. Much more obvious decision is to place the common part into the partial view, and render this view right in the master page layout. I had to overcome a few obstacles, however, before I achieved the desired result. Here's the outcome:

First step was to create a partial view, which I called "Stats.cshtml" and placed under "Shared" folder together with other common views. Here is the initial Stats.cshtml:

<div id="sidebar" role="complementary">
<div class="callout">
<h3>Stats:</h3>
</div>
<section class="tbl">
<table cellspacing="0">
<caption>Your Things To Do</caption>
<tr>
<th scope="col">
In Basket Items:
</th>
<th scope="col">

</th>
</tr>
</table>
</section>
</div>

Which can be rendered from the _layout.cshtml using several methods, such as Render or RenderPartial. More about which one to choose in references below. Let's say I use RenderPartial the following way:

<content>
<div id="main" role="main">
@RenderBody()
</div>

@if(Request.IsAuthenticated){
Html.RenderPartial("Stats");
}
</content>

This solves the bit where the sidebar is rendered properly, and only visible to the logged in users. Now to the second part - how to display user-specific information? Currently the layout knows nothing about it. One of the ways is to create a controller for the partial view. I created a controller "StatsController" and placed some pretty basic code in it:

public class StatsController : Controller
{
private modelGTDContainer db = new modelGTDContainer();
private InBasketRepository repository = new InBasketRepository();
//
// GET: /Stats/

[Authorize]
public PartialViewResult Stats(Users user)
{
var inbasket = repository.FindUserInBasketItems(user.UserID);
return PartialView(inbasket.ToList());
}
}

Note the use of PartialViewResult instead of the usual ViewResult. When I used a ViewResult I got a fairly nasty infinite loop. Now my RenderPartial is not so useful anymore, because it does not accept a controller as a parameter. So I have to change it to either Html.Action or Html.RenderAction. One of the overloads accepts a view name and a controller name, this is the one I need:

<content>
<div id="main" role="main">
@RenderBody()
</div>

@if(Request.IsAuthenticated){
Html.RenderAction("Stats", "Stats");
}
</content>

And that's about it. The last bit is to specify the model in the Stats.cshtml and to pass some output to the page - at least a simple label:

@model IEnumerable<GTD.Models.InBasket>

@if (Request.IsAuthenticated)
{
<div id="sidebar" role="complementary">
<div class="callout">
<h3>Stats:</h3>
</div>
<section class="tbl">
<table cellspacing="0">
<caption>Your Things To Do</caption>
<tr>
<th scope="col">
In Basket Items:
</th>
<th scope="col">
@Html.Label(Model.Count().ToString())

</th>
</tr>
</table>
</section>
</div>
}

And that's what the user will see:

References:

When to use Html.RenderPartial and Html.RenderAction in ASP.NET MVC Razor Views
ASP.NET MVC 3 _Layout.cshtml Controller
Passing data from View to Partial View
ASP.NET MVC3 Razor syntax help - I'm getting stuck in an infinite loop by . Also posted on my website

Tuesday, December 6, 2011

Learning MVC: Scalable Navigation

Today I tried applying a lesson from "Bulletproof Web Design" to scalable navigation. The goal, essentially is to avoid code wherever possible, avoid using images and allow the size of the tabs to be scalable.

The "bulletproof" approach, in short, is to use the nav element from HTML5 and wrap a list of tabs into it. Here's the whole of the HTML:

<nav role="navigation">
<ul id="menu">
<li id="nav-home">@Html.ActionLink("Home", "Index", "Home")</li>
<li id="nav-about">@Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav>

And here's the css that I applied following the book (and had to adjust some things here and there):

nav[role="navigation"] 
{
display: block;
margin: 0;
padding: 10px 0 0 0px;
list-style: none;
background: #FFCB2D;
}

nav[role="navigation"] li
{
float: left;
margin: 0 1px 0 0;
padding: 0;
font-family: "Lucida Grande", sans-serif;
font-size: 80%;
}

nav[role="navigation"] ul
{
float:left;
width:100%;
margin: 0;
padding: 10px 0 0 0px;
list-style: none;
background: #FFCB2D url(images/nav_bg.gif) repeat-x bottom left;
}

nav[role="navigation"] ul li a {
float: right;
display: block;
margin: 0;
padding: 4px 8px;
color: #333;
text-decoration: none;
border: 1px solid #9B8748;
border-bottom: none;
background: #F9E9A9 url(img/off_bg.gif) repeat-x top left;
}

nav[role="navigation"] ul li a:hover
{
color: #333;
padding-bottom: 5px;
border-color: #727377;
background: #FFF url(images/on_bg.gif) repeat-x top left;
}

And if anyone's interested, the gif files I used are as follows:


And I got to the point where the tabs were functional and nicely displayed fairly quickly.

Now I got to the point where I had to make the tab "stick" in the selected state, so it would be visible which tab is currently selected. And the way it was done in the book was by adding an id element to the body and assigning a value to it. The css was then modified like this:

nav[role="navigation"] ul li a:hover, body#home #nav-home a, body#about #nav-about a, body#inbasket #nav-inbasket a
{
color: #333;
padding-bottom: 5px;
border-color: #727377;
background: #FFF url(images/on_bg.gif) repeat-x top left;
}

So in this case the hovering and selection is combined in one css declaration. However, the problem I had was that I could not just go to individual pages and set the correct id elements in page bodies. The way the Razor engine works, of course, is by rendering all the common HTML in the _Layout.cshtml, including the body tag. To achieve my goal, I had to modify the body tag after the page was rendered. That was not as hard a I expected - I wrote a small HTML helper which added a couple of javaScript lines to the page

public static IHtmlString BodyTagUpdate(this HtmlHelper helper, string text)
{
return new HtmlString(@"<script type=""text/javascript"">
document.body.id ='" + text + "';" +
"</script>");
}

and then I added a call to this helper on any page that I had to.

@Html.BodyTagUpdate("about")

And it worked. Now I was at this stage.

The last thing I added was displaying certain tabs only for the users that are logged on. This turned out to be extremely easy. This is the modified HTML of a navigation element:

<nav role="navigation">
<ul id="menu">
<li id="nav-home">@Html.ActionLink("Home", "Index", "Home")</li>
<li id="nav-about">@Html.ActionLink("About", "About", "Home")</li>
@if(Request.IsAuthenticated) {
<li id="nav-inbasket">@Html.ActionLink("In Basket", "../InBasket/Index")</li>
}
</ul>
</nav>

And the very last thing was to get rid of the built-in Log On/Log Off div and move it into the last tab. It involved writing a couple extra HTML helpers - one to render correct text, and the other is essentially an extension to the ActionLink which allows to pass HTML in and out so the link can be formatted. It is not critical but may become more useful later.

public static IHtmlString LogOnOff(this HtmlHelper helper, string text, bool isLogon)
{
if (isLogon)
{
text = "Log On";
}
else
{
text = @"<strong>" + text + @"</strong> - Log Off";
}

return new HtmlString(text);
}

public static IHtmlString ActionHTML(this HtmlHelper helper, string action, string controller, string text)
{
var url = new UrlHelper(helper.ViewContext.RequestContext);

var linkWriter = new HtmlTextWriter(new StringWriter());
linkWriter.AddAttribute(HtmlTextWriterAttribute.Href, url.Action(action, controller));
linkWriter.RenderBeginTag(HtmlTextWriterTag.A);
linkWriter.Write(text);
linkWriter.RenderEndTag(); //A

return new HtmlString(linkWriter.InnerWriter.ToString());
}

The partial view _LogOnPartial is now a bit simplified:

@if(Request.IsAuthenticated) {
string s = @User.Identity.Name;
IHtmlString t = Html.LogOnOff(s, false);
@Html.ActionHTML("LogOff", "Account", t.ToString())
}
else {
@Html.ActionHTML("LogOn", "Account", "Log On")
}

And the div that was rendering it into the _Layout.cshtml has now moved into the navigation area:

<nav role="navigation">
<ul id="menu">
<li id="nav-home">@Html.ActionLink("Home", "Index", "Home")</li>
<li id="nav-about">@Html.ActionLink("About", "About", "Home")</li>
@if(Request.IsAuthenticated) {
<li id="nav-inbasket">@Html.ActionLink("In Basket", "../InBasket/Index")</li>
}
<li id="nav-log">@Html.Partial("_LogOnPartial")</li>
</ul>
</nav>

Reference:

Bulletproof Web Design: Improving flexibility and protecting against worst-case scenarios with HTML5 and CSS3 (3rd Edition) by . Also posted on my website

Saturday, December 3, 2011

Learning MVC: Custom Html Helpers

Html helpers are used to render HTML and, in most cases, return a string which is then rendered as part of web page. The underlying idea is to reduce development work, decrease the amount of typing and generally provide a readable markup. However, I quickly found that the helpers provided with the MVC framework are small in number. Fortuantely, it is relatively easy to write your own html helpers. There are several ways to do it, but so far I liked extending the HtmlHelper class the best. There are a couple of "gotchas" there, but after that it looks elegant and efficient.

I started with creating a folder in my solution called "HtmlHelpers" and creating the Helpers.cs class there. The class is part of the namespace "HtmlHelpers". The first "gotcha", or two, is to add the namespace to the Web.config file. The second, smaller "gotcha" is that it may not be enough to add it to the main Web.config class. If, for example, the helper is used by the view which resides in the Views folder, then the Web.config in the Views folder (if it exists) also needs to have the namespace registered.

<pages>
<namespaces>
<add namespace="System.Web.Helpers"/>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
<add namespace="System.Web.Mvc.Html"/>
<add namespace="System.Web.Routing"/>
<add namespace="System.Web.WebPages"/>
<add namespace="HtmlHelpers"/>
</namespaces>
</pages>

Both the class and the extension methods have to be static. The type of the extension method can not be "string" - that's another "gotcha". The engine will escape all the HTML tags by default. This is why the HtmlString should be returned. That was my last "gotcha". Here's the whole class:


namespace HtmlHelpers
{
public static class Helpers
{
public static IHtmlString BulletedList(this HtmlHelper helper, string data)
{
string[] items = data.Split('|');

var writer = new HtmlTextWriter(new StringWriter());
writer.RenderBeginTag(HtmlTextWriterTag.Ul);

foreach (string s in items)
{
writer.RenderBeginTag(HtmlTextWriterTag.Li);
writer.Write(helper.Encode(s));
writer.RenderEndTag();
}

writer.RenderEndTag();
return new HtmlString(writer.InnerWriter.ToString());
}

public static IHtmlString Paragraph(this HtmlHelper helper, string text)
{
return new HtmlString("<p>" +text + "</p>");
}
}
}

The first method takes strings delimited by "|" character and creates a bulleted list from them. Here I used HtmlTextWriter. In the second method I've shown that the HtmlTextWriter is not absolutely necessary - it is possible to just add tags "by hand".

This kind of usage

@Html.Paragraph("A custom paragraph")
@Html.BulletedList("Item 1|Item 2|Item 3|Item 4")

Provides the following output

References:

Custom HtmlHelper Renders Text and not Markup
Understanding HTML Helpers by . Also posted on my website

Friday, December 2, 2011

Learning MVC: Adding Autocomplete Dropdown to the Input Element.

I added the autocomplete function to one of the views. Turned out to be a simple task if I use the jQuery-provided function. Initially I tried to use Dylan Verheul's plugin, but inevitably ended up with the "Object does not support this property or method" the reason for which I still did not find. Very frustrating. Anyway.

First I added the following line to the _Layout.shtml under Views/Shared

Next, I used the Named Sections to place the javascript that I want to be in my view. In the _Layout.shtml file I added the following line:

@RenderSection("JavaScript", required: false)

Now I can add the "JavaScript" section to any view in the following manner:

@section JavaScript
{
<script type="text/javascript">
$(function () {
$("#Title").autocomplete({
source: "/InBasket/Find",
minLength: 1,
select: function (event, ui) {
if (ui.item) {
$("#Title").val(ui.item.value);
}
}
});
});
</script>}

and the section will be added to the view when it's rendered. The script above binds the autocomplete function to the Title input field. The "/InBasket/Find" calls the Find method in the InBasketController (which I'll write a bit later). The minLength specifies how many characters have to be in the box before the autocomplete fires. If my database is large, I may set it to 3, 5 or more to avoid huge responses where everything starting with "a" is returned. But for now I just want to test the functionality, so I set it to "1". And then, when I select an item from the autocomplete list, it sets the value of my Title input box to this item.

So that's the View part, now the Controller part. I started in my repository and added a function to return all user's InBasket item titles that start with a particular string, and the results should not be case-sensitive.

//used by autocomplete: return all inbasket items where Title starts with a string provided
public IQueryable FindUserInBasketItemsByTitle(string title, int userID)
{
var inBaskets = db.InBaskets.Where(item => item.UserID == userID);
inBaskets = inBaskets.Where(item => item.Title.ToLower().StartsWith(title.ToLower()));
return inBaskets;
}

Next, I added the Find method to the View. The method gets the value from the Title input box and returns the JSON array of values. One thing to note: it is important that the string parameter is called "term". I didn't know that initially and was wondering for a while why my parameter "s" is always null. Here is the whole Find method:

public JsonResult Find(string term, Users user)
{
var inBaskets = repository.FindUserInBasketItemsByTitle(term, user.UserID);
var titles = new List();
foreach(InBasket inBasket in inBaskets)
{
titles.Add(inBasket.Title);
}
return Json(titles, JsonRequestBehavior.AllowGet);
}

And that's all - it only takes a dozen or two lines of code. The result is horribly ugly at the moment, but it's a proof of concept:

References:

Autocomplete dropdown with jQuery UI and MVC
ASP.NET MVC 3: Layouts and Sections with Razor
ASP.Net MVC 3 Razor: Include js file in Head tag by . Also posted on my website

Thursday, December 1, 2011

Learning MVC: Installing Application on IIS.

Today I tried installing the MVC application on IIS 7.

It was not an extremely complicated task, as long as understood the prerequisites. I installed the .NET Framework 4 on the server that has IIS7. I then registered the .NET Framework with IIS by running the registration tool (aspnet_regiis.exe)

Before publishing a project, it is a good idea to use the "Add Deployable Dependencies" to make sure all required libraries are added to the published application. Initially I missed this step and started getting errors like the one below.

Initially I tried to solve them by manually copying the required dlls to the bin folder of the application, but got tired quickly and found a better solution, as mentioned above.

It generated a rather long list of files, it probably would not be a good idea to copy them all manually one by one.

Next, I published the project to a file system.

Created a directory on the server that is running IIS under wwwroot/test, copied the published application to the server, created the virtual directory on the Default Web Site and pointed it to my wwwroot/test folder. That was enough to be able to start the application and see the welcome page. Unfortunately, that is not the end of it. At this point I can navigate to the "Register" page, but the App_Data folder does not yet exist and also when I try to go my InBasket page directly, I get a server error. Additionally, my local version of the application, which worked fine, now displays the same behaviour. Quite a few things to fix!

References

Bin deploy required dependencies for MVC 3 projects with Visual Studio 2010 SP1
ASP.NET MVC on IIS 6 Walkthrough
ASP.NET IIS Registration Tool (Aspnet_regiis.exe)
BIN Deploying ASP.NET MVC 3 with Razor to a Windows Server without MVC installed by . Also posted on my website

Monday, November 28, 2011

Learning MVC: Model Binders

A model binder is a powerful concept in MVC. Implementing a model binder allows to pass an object to the controller methods automatically by the framework. In my case, I was looking for a way to get the id of the currently logged in user. This has to be checked any time user-specific data is accessed - which is, essentially, always. Placing extra code into each and every method of every controller does not look like a good solution. Here's how a model binder can be used:

First, implement IModelBinder. I only need it to return an int, and I get this int from my Users table.

public class IUserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
int userID = 0;
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
if (bindingContext == null)
{
throw new ArgumentNullException("bindingContext");
}
if (Membership.GetUser() != null)
{
MembershipUser member = Membership.GetUser();
string guid = member.ProviderUserKey.ToString();

using (modelGTDContainer db = new modelGTDContainer())
{
userID = db.Users.Single(t => t.UserGUID == guid).UserID;
}
}
return userID;
}
}

Next, I need to register the model binder when the application starts - in the global.asax.cs I only need to add one line to Application_Start

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders[typeof(int)] = new IUserModelBinder();
}

And finally, I add an int parameter to any controller method that needs a userID to be passed. And, by the magics of MVC, it is there! This is a huge saving of work by adding just a few lines of code.

//
// GET: /InBasket/
[Authorize]
public ViewResult Index(int userID)
{
var inbasket = repository.FindUserInBasketItems(userID);
return View(inbasket.ToList());
}

What's bad - I have to hit a database every time I access any data to get the user ID, and then hit a database again when I select the actual data for the user. How to fix it? Well, one way is to use the user Guid as a primary key for the Users table. However, it is a string, not an int. Performance may suffer anyway, and the indexing will be affected. But, thanks to a model binder, if I want to change this in the future, it will only have to be done in one place.

References:

IPrincipal (User) ModelBinder in ASP.NET MVC for easier testing

Multi User App with MVC3, ASP.NET Membership - User Authentication / Data Separation by . Also posted on my website

Sunday, November 27, 2011

Learning MVC: A Quick Note on Validation

What is the easiest way to validate class properties that have to be stored in a database - for example, if a database field "Title" has a limit of 50 characters, how do I enforce it best? I could set a "Required" attribute directly on the class property, but the Visual Studio Designer that generated this class may not like it. And anyway, if ever need to change the model and regenerate the database, the attribute is likely to be wiped anyway.

A better idea may be to specify a special class that will handle validation ("buddy class" - funny name which seems to be an official term). I can add a partial declaration to the existing class which will not be wiped if the model changes, and in this declaration I will specify the class that handles validation. As long as the property names of the buddy class exactly match those of the actual class, I should be fine and the valiation will be handled for me by my model!

The code looks like that:

[MetadataType(typeof(InBasket_Validation))]
public partial class InBasket
{

}

public class InBasket_Validation
{
[Required(ErrorMessage = "Title is Required")]
[StringLength(100, ErrorMessage = "Title can not be longer than 100 characters")]
public string Title { get; set; }

[Required(ErrorMessage = "Content is Required")]
[StringLength(5000, ErrorMessage = "Content can not be longer than 5000 characters")]
public string Content { get; set; }
}

The Metadata attribute specifies the buddy class, and the buddy class specifies validation requirements. The partial InBasket class is empty cause I don't want to add anything to the actual class functionality. The code builds (why wouldn't it? It's more important if it works), and I'll test it when I'm done with the views.

by . Also posted on my website

Learning MVC: A Repository Pattern.

A repository is just a place where data querying is encapsulated. There are several main reasons for a repository:

  • Avoid repetition. If I need to write a query, I will first check the repository - maybe it was already implemented
  • Encapsulation. Keep all data related code in the same place. Makes refactoring easier and separates logic from data
  • Unit testing. Tests can be written against the repository and, if necessary, in such way that the real database is not required

For the purpose of my sample application, which I explain later, I will now add a repository for the "In Basket". It's extremely simple: each user can have multiple items in the basket. A user can view, edit and delete any of his items. So I need a small number of methods:

public class InBasketRepository
{
private modelGTDContainer db = new modelGTDContainer();

//return all in basket items for a certain user
public IQueryable FindUserInBasketItems(int userID)
{
return db.InBaskets.Where(item => item.UserID == userID);
}

public InBasket GetInBasketItem(int id)
{
return db.InBaskets.Single(item => item.InBasketID == id);
}

public void AddInBasketItem(InBasket item)
{
db.InBaskets.AddObject(item);
}

public void DeleteInBasketItem(InBasket item)
{
db.InBaskets.DeleteObject(item);
}

//persistence
public void Save()
{
db.SaveChanges();
}
}

It seems logical for the repository to exist in the Models folder.

And that's it for now - the next step is to create view(s) which will use the repository.

by . Also posted on my website

Wednesday, November 23, 2011

Learning MVC: A multi-user application concept.

As a first experiment with MVC framework, I decided to consider the application that has multiple users where each user has some information stored in the local database. I.e. his "To Do List", to which no one else should have access. The problem, then, is to find a way to uniquely identify the user when he logs on (and, on a later stage, to select data that belongs to this user). Here's a bit of a naive first approach.

Create a database to hold the users, with the GUID being the primary key and ID being an identity and autoincremental. I used SQL CE 4.

App_Data -> Add -> New Item -> SQL Server Compact 4.0 Local Database -> dbUsers.sdf

Tables -> Create Table

Create a model from database. Project -> Add New Item -> Data -> ADO.NET Entity Data Model -> modelUsers.edmx -> Add -> Generate From Database -> dbUsers.mdf -> specify the tblUsers table and Finish.

Create a Controller to work with the Users class

Some useful bits of code in the controller:

To create a user

[HttpPost]
public ActionResult Create(tblUser tbluser)
{
if (ModelState.IsValid)
{
db.tblUsers.AddObject(tbluser);
db.SaveChanges();
return RedirectToAction("Index");
}

return View(tbluser);
}

To get user details

public ViewResult Details(int id)
{
tblUser tbluser = db.tblUsers.Single(t => t.UserID == id);
return View(tbluser);
}

Next, I'm going to try and stick some code into the AccountController.cs provided by the MVC application template. I want to insert a new user into my database table when the new user is registered and I want to get the user ID from the database when the user is authenticated successfully. In the future, probably, user ID may not be required at all and I can make the User GUID a primary key.

So that's how it looks in the Register method of the AccountController:

if (createStatus == MembershipCreateStatus.Success)
{
//Insert a user into the database

tblUser user = new tblUser();

MembershipUser mUser = Membership.GetUser(model.UserName);
if (mUser != null)
{
user.UserGUID = mUser.ProviderUserKey.ToString();

using (dbUsersEntities db = new dbUsersEntities())
{
db.tblUsers.AddObject(user);
db.SaveChanges();
}
}

FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}

And this is in the LogOn method of the AccountController:

if (Membership.ValidateUser(model.UserName, model.Password))
{
//user is valid, find his ID in the tblUsers
tblUser tbluser;
using (dbUsersEntities db = new dbUsersEntities())
{
MembershipUser mUser = Membership.GetUser(model.UserName);
if (mUser != null)
{
string guid = mUser.ProviderUserKey.ToString();
tbluser = db.tblUsers.Single(t => t.UserGUID == guid);
}
}

FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}

And a quick Index view for the UsersController to verify that the users are actually inserted in the database:

@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.UserID)
</td>
<td>
@Html.DisplayFor(modelItem => item.UserGUID)
</td>
</tr>
}

Register a user

And then verify that a user with that ID and GUID is now present in the tblUsers.

The concept looks feasible, now on to refining and improving it.

by . Also posted on my website

Tuesday, November 22, 2011

NuGet, Entity Framework 4.1 and DbContext API

NuGet is a "Package Manager" that can and should be used with Visual Studio 2010 because it makes installing and updating the libraries, frameworks and extensions so much easier. To install NuGet, I go to Tools -> Extension Manager within Visual Studio and search for the NuGet in the online gallery. In the search results, all I have to do is click "Install".

Now, what if I have Entity Framework installed and want to update version 4 to 4.2? I don't have to search it somewhere on download.microsoft.com or elsewhere. Right within my project I go to References, right-click and select "Add library package reference".

The Entity Framework is installed, but it's version 4.1.

I select "Updates" from the menu on the left and immediately see that 4.2 is available. I select "Update", accept terms and conditions and I'm done.

Steps that are not required: searching for an update package, manual download of the package, manual uninstall of the previous version, manual install of the new version, verifying that I save the downloaded package in an easily accessible location in case anyone in my team also needs it ... Time saved per package update: anywhere between 3 and 30 minutes.

However, it does not always go smoothly. Just today I tried to add a package "on the fly". Right-click my model, go to "Add code generation item", select "ADO.NET C# DbContext Generator" and click "Install". And here Visual Studio stopped responding.

I killed it, repeated the sequence of actions and it stopped responding again. So I started it and added the package through the Tools -> Extension Manager as described above and it worked perfectly. So, don't ask too much from your favourite IDE.

by . Also posted on my website

Friday, November 18, 2011

Tortoise SVN for Windows and Checking Out Code from Google.

While I did not have a chance to properly configure my GitHub access yet (I think my corporate network is blocking some connections, so I'll try from home) I needed to checkout some code from code.google.com.

Following the advice, I searched around for a Windows SVN client and downloaded Tortoise SVN

Tortoise SVN

It does not have a UI as such.

It is integrated into Windows Explorer and displays the menu on the right-click. To get code, I have to select "SVN Checkout".

The checkout screen is quite self-explanatory.

However, my first attempt was unsuccessful.

I immediately suspected the corporate proxy server. Tortoise SVN has settings that are accessed through Program Files, so after some digging around I came up with the correct network settings.

Things went smoothly from there on.

Much easier than GitHub/GitExtensions so far! From zero knowledge about the application (Tortoise SVN) to a checked out solution in, probably, about 10 minutes - about as much as this post took, and even less if I was accessing it from home without any proxies. Next time I'll try to add some of my code to code.google.com

Reference:

How do I download code using SVN/Tortoise from Google Code?

by . Also posted on my website

Monday, November 14, 2011

Generating a C# Class Based on the Underlying SQL Server Database Table

If a class structure is based on the underlying database, it may be useful to be able to automatically generate a class "stub" based on the SQL Server table. I looked up the ways to do it without too much upfront time investment, and decided to follow one of the approaches.

First, a table have to be created to define the types in SQL Server and corresponding types in the language of choice - C# for me. The table is created by the following script:

/****** Object:  Table [dbo].[DbVsCSharpTypes]    Script Date: 03/20/2010 03:07:56 ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[DbVsCSharpTypes]')
AND type in (N'U'))
DROP TABLE [dbo].[DbVsCSharpTypes]
GO

/****** Object: Table [dbo].[DbVsCSharpTypes] Script Date: 03/20/2010 03:07:56 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[DbVsCSharpTypes](
[DbVsCSharpTypesId] [int] IDENTITY(1,1) NOT NULL,
[Sql2008DataType] [varchar](200) NULL,
[CSharpDataType] [varchar](200) NULL,
[CLRDataType] [varchar](200) NULL,
[CLRDataTypeSqlServer] [varchar](2000) NULL,

CONSTRAINT [PK_DbVsCSharpTypes] PRIMARY KEY CLUSTERED
(
[DbVsCSharpTypesId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET NOCOUNT ON;
SET XACT_ABORT ON;
GO

SET IDENTITY_INSERT [dbo].[DbVsCSharpTypes] ON;
BEGIN TRANSACTION;
INSERT INTO [dbo].[DbVsCSharpTypes]([DbVsCSharpTypesId], [Sql2008DataType], [CSharpDataType], [CLRDataType], [CLRDataTypeSqlServer])
SELECT 1, N'bigint', N'short', N'Int64, Nullable', N'SqlInt64' UNION ALL
SELECT 2, N'binary', N'byte[]', N'Byte[]', N'SqlBytes, SqlBinary' UNION ALL
SELECT 3, N'bit', N'bool', N'Boolean, Nullable', N'SqlBoolean' UNION ALL
SELECT 4, N'char', N'char', NULL, NULL UNION ALL
SELECT 5, N'cursor', NULL, NULL, NULL UNION ALL
SELECT 6, N'date', N'DateTime', N'DateTime, Nullable', N'SqlDateTime' UNION ALL
SELECT 7, N'datetime', N'DateTime', N'DateTime, Nullable', N'SqlDateTime' UNION ALL
SELECT 8, N'datetime2', N'DateTime', N'DateTime, Nullable', N'SqlDateTime' UNION ALL
SELECT 9, N'DATETIMEOFFSET', N'DateTimeOffset', N'DateTimeOffset', N'DateTimeOffset, Nullable' UNION ALL
SELECT 10, N'decimal', N'decimal', N'Decimal, Nullable', N'SqlDecimal' UNION ALL
SELECT 11, N'float', N'double', N'Double, Nullable', N'SqlDouble' UNION ALL
SELECT 12, N'geography', NULL, NULL, N'SqlGeography is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack.' UNION ALL
SELECT 13, N'geometry', NULL, NULL, N'SqlGeometry is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack.' UNION ALL
SELECT 14, N'hierarchyid', NULL, NULL, N'SqlHierarchyId is defined in Microsoft.SqlServer.Types.dll, which is installed with SQL Server and can be downloaded from the SQL Server 2008 feature pack.' UNION ALL
SELECT 15, N'image', NULL, NULL, NULL UNION ALL
SELECT 16, N'int', N'int', N'Int32, Nullable', N'SqlInt32' UNION ALL
SELECT 17, N'money', N'decimal', N'Decimal, Nullable', N'SqlMoney' UNION ALL
SELECT 18, N'nchar', N'string', N'String, Char[]', N'SqlChars, SqlString' UNION ALL
SELECT 19, N'ntext', NULL, NULL, NULL UNION ALL
SELECT 20, N'numeric', N'decimal', N'Decimal, Nullable', N'SqlDecimal' UNION ALL
SELECT 21, N'nvarchar', N'string', N'String, Char[]', N'SqlChars, SqlStrinG SQLChars is a better match for data transfer and access, and SQLString is a better match for performing String operations.' UNION ALL
SELECT 22, N'nvarchar(1), nchar(1)', N'string', N'Char, String, Char[], Nullable', N'SqlChars, SqlString' UNION ALL
SELECT 23, N'real', N'single', N'Single, Nullable', N'SqlSingle' UNION ALL
SELECT 24, N'rowversion', N'byte[]', N'Byte[]', NULL UNION ALL
SELECT 25, N'smallint', N'smallint', N'Int16, Nullable', N'SqlInt16' UNION ALL
SELECT 26, N'smallmoney', N'decimal', N'Decimal, Nullable', N'SqlMoney' UNION ALL
SELECT 27, N'sql_variant', N'object', N'Object', NULL UNION ALL
SELECT 28, N'table', NULL, NULL, NULL UNION ALL
SELECT 29, N'text', N'string', NULL, NULL UNION ALL
SELECT 30, N'time', N'TimeSpan', N'TimeSpan, Nullable', N'TimeSpan' UNION ALL
SELECT 31, N'timestamp', NULL, NULL, NULL UNION ALL
SELECT 32, N'tinyint', N'byte', N'Byte, Nullable', N'SqlByte' UNION ALL
SELECT 33, N'uniqueidentifier', N'Guid', N'Guid, Nullable', N'SqlGuidUser-defined type(UDT)The same class that is bound to the user-defined type in the same assembly or a dependent assembly.' UNION ALL
SELECT 34, N'varbinary ', N'byte[]', N'Byte[]', N'SqlBytes, SqlBinary' UNION ALL
SELECT 35, N'varbinary(1), binary(1)', N'byte', N'byte, Byte[], Nullable', N'SqlBytes, SqlBinary' UNION ALL
SELECT 36, N'varchar', N'string', N'String, Char[]', N'SqlChars, SqlStrinG SQLChars is a better match for data transfer and access, and SQLString is a better match for performing String operations.' UNION ALL
SELECT 37, N'xml', NULL, NULL, N'SqlXml'
COMMIT;
RAISERROR (N'[dbo].[DbVsCSharpTypes]: Insert Batch: 1.....Done!', 10, 1) WITH NOWAIT;
GO

SET IDENTITY_INSERT [dbo].[DbVsCSharpTypes] OFF;

Here is what results from the script:

Next, a function that will return the C# type when the SQL Server type is passed to it will be required. It will take it from that table that was just created. This is the script for the function:

/****** Object:  UserDefinedFunction [dbo].[funcGetCLRTypeBySqlType]    
Script Date: 03/23/2010 15:25:09 ******/
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[funcGetCLRTypeBySqlType]')
AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [dbo].[funcGetCLRTypeBySqlType]
GO
/****** Object: UserDefinedFunction [dbo].[funcGetCLRTypeBySqlType]
Script Date: 03/23/2010 15:25:09 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[funcGetCLRTypeBySqlType]
(@SqlType [nvarchar] (200))
RETURNS [varchar](200)
WITH EXECUTE AS CALLER
AS
BEGIN
declare @ClrType varchar(200)
SET @ClrType = ( SELECT TOP 1 CSharpDataType FROM DbVsCSharpTypes
WHERE Sql2008DataType= @SqlType)
-- Return the result of the function
RETURN @ClrType END
/*Used for automatic conversation between tsql and C# types */
GO

Sample of the usage - nothing hard yet.

A small function just because I want my private variable start from a lower case character

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

CREATE FUNCTION [dbo].[lowerCaseFirstCharacter]
(@Input [nvarchar] (200))
RETURNS [varchar](200)
WITH EXECUTE AS CALLER
AS
BEGIN
declare @Result varchar(200)
declare @Len int
SET @Len = LEN(@Input)
SET @Result = LOWER(SUBSTRING(@Input, 1, 1)) + SUBSTRING(@Input, 2, @Len-1)

RETURN @Result
END

And, finally, the stored procedure that generates some C# code:

IF  EXISTS (SELECT * FROM sys.objects 
WHERE object_id = OBJECT_ID(N'[dbo].[procUtils_GenerateClass]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[procUtils_GenerateClass]
GO
/****** Object: StoredProcedure [dbo].[procUtils_GenerateClass]
Script Date: 03/20/2010 13:10:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[procUtils_GenerateClass]
@TableName [varchar](50)
WITH EXECUTE AS CALLER
AS
BEGIN -- proc start
SET NOCOUNT ON;
DECLARE @DbName nvarchar(200 )
select @DbName = DB_NAME()
declare @strCode nvarchar(max)
set @strCode = ''

BEGIN TRY --begin try
set @strCode = @strCode + 'namespace ' + @DbName + '.Gen' + CHAR(13) + '{' + CHAR(13)
set @strCode = @strCode + CHAR(9) + 'public class ' + @TableName + CHAR(13) + CHAR(9) + '{ ' + CHAR(13)

DECLARE @ColNames TABLE
(
Number [int] IDENTITY(1,1), --Auto incrementing Identity column
ColName [varchar](300) , --The string value ,
DataType varchar(50) , --the datatype
IS_NULLABLE nvarchar(5) , --should we add =null in front
CHARACTER_MAXIMUM_LENGTH INT
)
--Decalre a variable to remember the position of the current delimiter
DECLARE @CurrentDelimiterPositionVar INT
DECLARE @PkColName varchar(200)
set @PkColName = ''
declare @ColumnName varchar(200)
--Decalre a variable to remember the number of rows in the table
DECLARE @Count INT

INSERT INTO @ColNames
SELECT column_name , Data_type , IS_NULLABLE , CHARACTER_MAXIMUM_LENGTH
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME=@TableName
--Initialize the looper variable
SET @CurrentDelimiterPositionVar = 1
--Determine the number of rows in the Table
SELECT @Count=max(Number) from @ColNames
--A variable to hold the currently selected value from the table
DECLARE @ColName varchar(300);
DECLARE @DataType varchar(50)
DECLARE @IS_NULLABLE VARCHAR(5)
DECLARE @CHARACTER_MAXIMUM_LENGTH INT
--Loop through until all row processing is done
WHILE @CurrentDelimiterPositionVar <= @Count --1st loop
BEGIN
--Load current value from the Table
SELECT @ColName = ColName FROM @ColNames
WHERE Number = @CurrentDelimiterPositionVar
SELECT @DataType = DataType FROM @ColNames
WHERE Number = @CurrentDelimiterPositionVar
SELECT @IS_NULLABLE = IS_NULLABLE FROM @ColNames
WHERE Number = @CurrentDelimiterPositionVar
SELECT @CHARACTER_MAXIMUM_LENGTH = CHARACTER_MAXIMUM_LENGTH FROM @ColNames
WHERE Number = @CurrentDelimiterPositionVar
-- get the C# type based on the passed sqlType, )( needs the DbVsCSharpTypes table )
set @DataType =( SELECT dbo.funcGetCLRTypeBySqlType(@DataType) )
IF @IS_NULLABLE = 'YES'
set @DataType = @DataType + '?'
DECLARE @varPrivate nvarchar(200)
set @varPrivate = '_' + dbo.lowerCaseFirstCharacter(@ColName)

--GENERATE THE PRIVATE MEMBER
SET @StrCode = @strCode + CHAR(9)+ CHAR(9) + 'private ' + @DataType + ' ' + @varPrivate + ';' + CHAR(13) + CHAR(13)
-- GENERATE THE PUBLIC MEMBER
SET @StrCode = @strCode + CHAR(9)+ CHAR(9) + 'public ' + @DataType + ' ' + @ColName + CHAR(13) + CHAR(9)+ CHAR(9) + '{' + CHAR(13)
SET @StrCode = @strCode + CHAR(9) + CHAR(9) + CHAR(9) + 'get { return ' + @varPrivate + ' } ' + CHAR(13)
SET @strCode = @strCode + CHAR(9) + CHAR(9) + CHAR(9) + 'set { ' + @varPrivate +' = value ; }' + CHAR(13)
SET @strCode = @strCode + CHAR(9) + CHAR(9) + '}' + CHAR(13)

if @CurrentDelimiterPositionVar != @Count
SET @StrCode = @StrCode + ''
IF @DataType != 'timestamp'

set @strCode = @strCode + char(13)
SET @CurrentDelimiterPositionVar = @CurrentDelimiterPositionVar + 1;
END
set @strCode = + @strCode + char(9) + ' } //class ' + @TableName + CHAR(13)
set @strCode = + @strCode + ' } //namespace ' + CHAR(13)

PRINT @strCode
END TRY
BEGIN CATCH
print ' Error number: ' + CAST(ERROR_NUMBER() AS varchar(100)) +
'Error message: ' + ERROR_MESSAGE() + 'Error severity: ' +
CAST(ERROR_SEVERITY() AS varCHAR(9)) +
'Error state: ' + CAST(ERROR_STATE() AS varchar(100)) +
'XACT_STATE: ' + CAST(XACT_STATE() AS varchar(100))
END CATCH
END --procedure end
/* Generates a C# class base on DataType conversion*/
GO

Here's a test table I used to check the results:

Here's the stored procedure output sample:

References

How can I programatically convert SQL data-types to .Net data-types?

how-to generate classes based on tables in ms sql 2008

Function to get the C# type based on the tsql type

by . Also posted on my website

Friday, November 11, 2011

Understanding Git, GitHub and GitExtensions - Part I.

At this stage I would like learn how to use the GitHub.

GitHub - Social Coding

This is useful in case I want to make my code accessible to anyone over the Internet. Let's say I want to show someone the example of my work. I create a GitHub account, use Git as my code repository and somehow synchronise it with GitHub. Then any other person will be able to "get the latest version" (or should I say "pull"?) of my code via GitHub. Okay, I just summed up all my knowledge about Git and GitHub until today.

My goal is to be able to commit and update a Visual Studio project, so I looked and found out that everyone recommends GitExtensions.

Git Extensions

This tool allows to control Git without the command line (a handy thing for a long-time Windows user like me!), works well under Windows and has a plugin for Visual Studio. So far, sounds like a perfect tool for my purposes.

The first thing I did was created a GitHub account.

And created a new repository

Next, I installed the GitExtension using default settings, briefly checked the 58-page manual and started the gui. The program automatically checks my settings on startup and it looks all was good - I just had to create a username for myself.

Looked around a little bit and found how to start the Git gui, then look at my repository.

Found out how to check the items in. Go a rather scary message but decided to ignore it for now and see if it affects anything later.

For now it looks like my files are in. Next time, I will explore the checking in and out.

by . Also posted on my website

Wednesday, November 9, 2011

Flash in WPF Application the MVVM way (the easy part)

Now I have to use my WFPFlashLibrary I created in the last post in my main WPF view. I have to add the namespace for the project that contains my Flash control.

xmlns:Flash="clr-namespace:WPFFlashLibrary;assembly=WPFFlashLibrary"

I place my control in the WPF markup and bind Movie and Play.

<Grid>
<Flash:FlashControl Width="400" Height="400" Movie="{Binding Movie,UpdateSourceTrigger=PropertyChanged}" Play="{Binding Play,UpdateSourceTrigger=PropertyChanged}" />
</Grid>

This is the sample code which should be placed in the view. The Init will contain any code that needs to run on the ViewModel creation and will return the instance of the ViewModel. The PlayFlash will be then called right in the constructor of the view for simplicity, but of course it does not have to be there - it can be triggered whenever necessary.

public partial class TestFlashView : System.Windows.Controls.UserControl
{
public TestFlash(IUnityContainer container)
{
InitializeComponent();

DataContext = container.Resolve().Init(container);
(DataContext as TestFlashViewModel).PlayFlash();
}
}

And this is the implementation of the ViewModel. As soon as the PlayFlash() assigns values to the Movie and Play, the control will play the Flash animation (assuming the file is in the specified location!).

public class TestFlashViewModel : ViewModelBase
{
public TestFlashViewModel(IUnityContainer container):base(container)
{

}

virtual public TestFlashViewModel Init(IUnityContainer container)
{
//initialization - login etc.
return this;
}

//*****************************************************************************************

#region properties

string _movie;
public string Movie
{
get { return _movie; }
set { OnPropertyChanged(ref _movie , value,"Movie"); }
}

bool _play;
public bool Play
{
get { return _play; }
set { OnPropertyChanged(ref _play, value, "Play"); }
}

#endregion

public void PlayFlash()
{
Movie = @"c:\flash\flash.swf";
Play = true;
}
}

And that's the end of my small investigation. Unfortunately I found out that the plans have changed, the scope has been reduced and the flash movie is not required any longer. So I won't play with this control for anymore for now and move on to other things. Still was worth the effort.

by . Also posted on my website

Thursday, November 3, 2011

Flash in WPF Application the MVVM way (the hard part)

The Flash ActiveX control can not be added directly to the XAML file. Okay, the solution is well-known - just like with the Windows Forms control, you can use WindowsFormsHost to, well, host it - that's what the host is for. Then we add some code to the code-behind to load the movie and play it, and everything works. Right? Sort of. What if I'm trying my best to do the things the MVVM way? My code-behind file is usually empty, and all the business logic happens in the ViewModel. My XAML file does not have any button1_click event handlers, but rather is linked to the ViewModel by binding and is notified when something changes by means of OnPropertyChanged. What to do? The hard part is to come up with something that can be placed into the XAML file. The easy part is to place that something into the XAML file and bind it to the ViewModel. I'll start with the hard part, and use Visual Studio 2010.

Let's assume that the user controls are in a separate project. So I create a new project using the "WPF User Control Library" template and call it WPFControlLibrary. Let's delete UserControl1.xaml so it doesn't get in the way. First I'll add references to the COM components I may need.

Now I add a User Control (not the User Control - WPF yet!) and call it FlashWrapper. I add the AxShockwaveFlash control to it and call it axShockwafeFlash. For now let's worry only about loading and playing a movie. That's all the code I'll need:

using System.Windows.Forms;

namespace WPFControlLibrary
{
public partial class FlashWrapper : UserControl
{
public FlashWrapper()
{
InitializeComponent();
}

public void LoadMovie(string movie)
{
axShockwaveFlash.Movie = movie;
}

public void Play()
{
axShockwaveFlash.Play();
}

public void Stop()
{
axShockwaveFlash.Stop();
}
}
}

Now is the time to add the User Control - WPF. I call it FlashWPF.xaml. The XAML file is where the WindowsFormsHost comes to play - here I will host my FlashWrapper. Don't forget to add a reference to WindowsFormsIntegration!

<UserControl x:Class="WPFControlLibrary.FlashWPF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPFControlLibrary"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid >
<WindowsFormsHost >
<WindowsFormsHost.Child>
<local:FlashWrapper x:Name="FlashPlayer" />
</WindowsFormsHost.Child>
</WindowsFormsHost>
</Grid>
</UserControl>

And still not much of C# code yet.

using System.Windows.Controls;

namespace WPFControlLibrary
{
public partial class FlashWPF : UserControl
{
public FlashWPF()
{
InitializeComponent();
}

public void LoadMovie(string movie)
{
FlashPlayer.LoadMovie(movie);
}

public void Play(bool value)
{
if (value)
FlashPlayer.Play();
else
FlashPlayer.Stop();
}
}
}

Last, and the most complex and cryptic step is to add a custom control. This will be a link between the MVVM application and the WPF control which hosts the wrapper which wraps the ActiveX control.

This is the ViewModel.
That is bound to the WPF custom control.
That hosts the C# wrapper.
That wraps the ActiveX control.
That plays the movie.

I think I got carried away a bit.

Ok, let's add a Custom Control - WPF and call it FlashControl. That's how it looks if all was done correctly:

public class FlashControl : Control
{
static FlashControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlashControl), new FrameworkPropertyMetadata(typeof(FlashControl)));
}
}

I have to modify it to expose three DependencyProperties: Movie, Play, and finally itself so it can be created in the MVVM application. And this is the end result:

public class FlashControl : Control
{
static FlashControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlashControl), new FrameworkPropertyMetadata(typeof(FlashControl)));
}

public FlashControl()
{
FlashPlayer = new FlashWPF();
}

//*****************************************************************************************

//Movie property definition

public static readonly DependencyProperty MovieProperty = DependencyProperty.RegisterAttached("Movie", typeof(string), typeof(FlashControl), new PropertyMetadata(MovieChanged));

private static void MovieChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as FlashControl).Movie = (string)e.NewValue;
}

//Play movie property definition
public static readonly DependencyProperty PlayProperty = DependencyProperty.RegisterAttached("Play", typeof(bool), typeof(FlashControl), new PropertyMetadata(PlayChanged));

private static void PlayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as FlashControl).Play = (bool)e.NewValue;
}

//Flash player WindowFormHost
public static readonly DependencyProperty FlashPlayerProperty = DependencyProperty.RegisterAttached("FlashPlayer", typeof(FlashWPF), typeof(FlashControl), new PropertyMetadata(FlashPlayerChanged));

private static void FlashPlayerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as FlashControl).FlashPlayer = (FlashWPF)e.NewValue;
}

//*****************************************************************************************

public string Movie
{
get { return (string)this.GetValue(MovieProperty); }
set
{
this.SetValue(MovieProperty, value);
FlashPlayer.LoadMovie(value);
}
}

public bool Play
{
get { return (bool)this.GetValue(PlayProperty); }
set
{
this.SetValue(PlayProperty, value);
FlashPlayer.Play(value);
}
}

public FlashWPF FlashPlayer
{
get { return (FlashWPF)this.GetValue(FlashPlayerProperty); }
set { this.SetValue(FlashPlayerProperty, value); }
}
}

And the XAML file (which is for some reason called Generic.xaml and also when I tried to rename it I started getting errors, so I decided to leave the name alone) - I modified it slightly, but that's a matter of personal preference:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFControlLibrary">
<Style TargetType="{x:Type local:FlashControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:FlashControl}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentControl Content="{TemplateBinding FlashPlayer}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

And the hard part is over!

by . Also posted on my website

Small Things Learned Today

While writing the previous post, I had to find out how to show the special HTML characters, like &nbsp;

Because if I just type &nbsp; I will only see the space. The browser will automatically convert the &nbsp; to space, the &amp; to an ampersand and so on. But I wanted to display exactly what I had in my XML.

So, to display &nbsp; just as it is, I have to type &amp;nbsp; in my post. &amp; gets converted to ampersand and the nbsp; part is just displayed as it is.

What did I type to display &amp;nbsp; in the line above? Well, &amp;amp;nbsp; of course. I'd better stop here before I confuse myself and everyone else.

by . Also posted on my website