6 Jun 2012

Override the ASP.NET MVC 3 Nullable Boolean EditorFor EditorTemplate

If you use EditorFor() in your MVC View code and pass it a nullable boolean (bool?), you'll see that MVC helpfully renders a tri-state dropdown list. However, the three choices in the dropdown are "Not Set", "True", and "False" which seem a bit unfriendly to some users.

Save the following template as Boolean.cshtml under Views / Shared / EditorTemplates in your project and you'll see a more friendly widget by default.

@model bool?
@using System.Web.Mvc

@{
    var selectList = new List<SelectListItem>();
    selectList.Add(new SelectListItem { Text = "", Value = "" });
    selectList.Add(new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value });
    selectList.Add(new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && !Model.Value });
}

@Html.DropDownListFor(model => model, selectList)


Now you can customise it as you please.

If you don't want to override the standard template, bear in mind that you can save it with a different filename e.g. FriendlyBool.cshtml and then call it explicitly like so:

@Html.EditorFor(model => model.myNullableBool, "FriendlyBool")

28 May 2012

jquery - dynamic load of html fragments with script

Sometimes I want to load a fragment of HTML into the current page.

Often this is a blob of HTML that came in as part of a JSON object from AJAX. Also, I only might to extract part of the HTML that's returned - because for example sometimes the incoming fragment may be a whole partial view and I only want part of it.

( Note : this is similar to the jQuery $('#target').load('mypage.aspx #divToExtract') syntax )

Well, if you want to do it WITHOUT running script from the incoming fragment, do this:

                        var frag = $(jsonObj.HtmlFragment);
                        frag = frag.find('#divToExtract');
                        $('#target').html(frag.html());
But if you do want to run any script in the fragment, do this:
                        var frag = $('<div />');
                        frag.html( jsonObj.HtmlFragment );
                        frag = frag.find('#divToExtract');
                        $('#target').html(frag.html());

11 May 2012

Simple jQuery Navigation Menu


<script type="text/javascript">

    $(function () {
        $('.MyNavHolder .SubSection').hide();
        $('.MyNavHolder .Section').hover(function () { $(this).find('.SubSection').toggle('fast'); });
    });

</script>

<style>
    .MyNavHolder {background-color: #f00; overflow:visible; height: 30px; padding: 0px; position: relative;  }
    .MyNavHolder .MyNav {  position: absolute; right: 0; }
    .MyNavHolder a { color: #fff; text-decoration: none; display: block; }
    .MyNavHolder ul { margin: 0; padding: 0; list-style-type: none;}
    .MyNavHolder .Section { float: left;  background-color: Olive; width: 200px; cursor: pointer; }
    .MyNavHolder .Section .Header { height: 30px; border-left: 1px solid black;  }
    .MyNavHolder .Section .Header div { padding: 5px;  }
    .MyNavHolder .SubSection li { background-color: Fuchsia; padding: 5px; }
    .MyNavHolder .SubSection li:hover { background-color: Blue; }
</style>

<div class="MyNavHolder">
    <ul class="MyNav">
        <li class="Section">
            <div class="Header">
                <div><a href="">About Us</a></div>
            </div>
            <ul class="SubSection">
                <li><a href="">Contact Us</a></li>
                <li><a href="">History</a></li>
            </ul>
        </li>
        <li class="Section">
            <div class="Header">
                <div><a href="">McCormack</a></div>
            </div>
            <ul class="SubSection">
                <li><a href="">Account Settings</a></li>
                <li><a href="">Log Out</a></li>
            </ul>
        </li>
    <ul>
</div>

4 May 2012

Entity Framework 4 Set Default SQL Values On Save Changes

EF doesn't use the default values that you may have specified in your SQL Table schema.

So stuff like "DateCreated" fields that have getutcdate() as their default SQL value won't get set automatically when a record is saved from Entity Framework.

To solve that particular problem, I use the following approach. I enclose two styles, one for use with EF DbContext (POCO / CodeFirst), and one with EF ObjectContext (the default used in EDMX generation).

Note that you put this code within a partial extension of your EF Context class i.e. public partial class MyContext : DbContext or public partial class MyContext : ObjectContext

DbContext
public override int SaveChanges()
{
	this.ChangeTracker.DetectChanges();

	ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;

	List<ObjectStateEntry> objectStateEntryList = ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted)
																		.ToList();

	foreach (ObjectStateEntry entry in objectStateEntryList)
	{
		if (!entry.IsRelationship)
		{
			switch (entry.State)
			{
				case EntityState.Added:
					if (entry.Entity.HasProperty("DateCreated"))
						entry.Entity.GetType().GetProperty("DateCreated").SetValue(entry.Entity, DateTime.UtcNow, null);
					if (entry.Entity.HasProperty("DateModified"))
						entry.Entity.GetType().GetProperty("DateModified").SetValue(entry.Entity, DateTime.UtcNow, null);
					break;

				case EntityState.Modified:
					if (entry.Entity.HasProperty("DateModified"))
						entry.Entity.GetType().GetProperty("DateModified").SetValue(entry.Entity, DateTime.UtcNow, null);
					break;
			}
		}
	}

	return base.SaveChanges();
}
ObjectContext
public override int SaveChanges(SaveOptions options)
{
	this.DetectChanges();

	foreach (var insert in this.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added))
	{
		if (insert.Entity.HasProperty("DateCreated"))
			insert.Entity.GetType().GetProperty("DateCreated").SetValue(insert.Entity, DateTime.UtcNow, null);
		if (insert.Entity.HasProperty("DateModified"))
			insert.Entity.GetType().GetProperty("DateModified").SetValue(insert.Entity, DateTime.UtcNow, null);
	}

	foreach (var update in this.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified))
	{
		if (update.Entity.HasProperty("DateModified"))
			update.Entity.GetType().GetProperty("DateModified").SetValue(update.Entity, DateTime.UtcNow, null);
	}

	return base.SaveChanges(options);
}

13 Apr 2012

ASP.NET MVC 3 - Html.TextBoxFor not showing model value

I have a Controller that passes a Model to a View.
The Controller receives the Model in a postback and modifies one of the Model properties before passing it to the View.
The View uses the Html.HiddenFor HtmlHelper to include the modified Model property in a Form.

BUT!!!! The HTML shows that the View is not rendering the modified Model property value! Instead it's rendering whatever the property value was when it was POSTed.

In short: HtmlHelpers use the posted form collection values in preference to the explicitly set model value. To me that's counter-intuitive.

Anyway, the best workaround is to use hand-made <input /> tags instead of using the HtmlHelpers. Feels hacky but works.

This guy has more info.

12 Apr 2012

Free license for VMWare ESXi 5 / Hypervisor

I hate the 60 day license when the free unlimited license is available - when you know where it is!

Go here: https://www.vmware.com/tryvmware/index.php?p=free-esxi5&lp=default

Click the tab midway down the page called "License and Download". Create an account and they'll give you a license code.

Then go into vSphere Client Inventory, click on the host server that you wish to license, and select Configuration > Software > Licensed Features > Edit


Hope that helps someone.

29 Feb 2012

Get / Show Entity Framework (EF) Generated SQL

Before I forget AGAIN , when you have some linq from EF and you want to see the generated SQL, cast it to ObjectQuery and call ToTraceString().

20 Feb 2012

Using iTextSharp to print Avery Labels PDF with ASP.NET MVC and C#

Tasked with printing labels nicely on Avery L7992 label sheets (or any other type of labels really) from a web application, I did the usual messing around with HTML + CSS in different browsers before deciding to start again and try using PDF generation as a means of getting more control. After a few hours, a bunch of googling, finding tips here and there, I got it to work.

To produce a PDF file, your best bet is to use an existing library. iTextSharp is a powerful PDF generation library for .NET (it's a port of the Java iText library). One downfall is a paucity of good free documentation (although Mike was very helpful: http://www.mikesdotnetting.com).

So, with a reference to itextsharp.dll and a couple of usings in the code (iTextSharp.text and  iTextSharp.text.pdf), this is basically all you need to generate PDF label sheets from your MVC Controller:


 
public ActionResult LabelsPdf()
{
	// Open a new PDF document

	const int pageMargin = 5;
	const int pageRows = 5;
	const int pageCols = 2;
	
	var doc = new Document();
	doc.SetMargins(pageMargin, pageMargin, pageMargin, pageMargin);
	var memoryStream = new MemoryStream();

	var pdfWriter = PdfWriter.GetInstance(doc, memoryStream);
	doc.Open();

	// Create the Label table

	PdfPTable table = new PdfPTable(pageCols);
	table.WidthPercentage = 100f;
	table.DefaultCell.Border = 0;

	var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, false);

	foreach (var Thing in YourCollectionOfThings)
	{
		#region Label Construction

		PdfPCell cell = new PdfPCell();
		cell.Border = 0;
		cell.FixedHeight = (doc.PageSize.Height - (pageMargin * 2)) / pageRows;
		cell.VerticalAlignment = Element.ALIGN_MIDDLE;

		var contents = new Paragraph();
		contents.Alignment = Element.ALIGN_CENTER;

		contents.Add(new Chunk(string.Format("Thing #{0}\n", Thing.ThingId), new Font(baseFont, 11f, Font.BOLD)));
		contents.Add(new Chunk(string.Format("Thing Name: {0}\n", Thing.Name), new Font(baseFont, 8f)));

		cell.AddElement(contents);
		table.AddCell(cell);

		#endregion
	}

	table.CompleteRow();
	doc.Add(table);

	// Close PDF document and send

	pdfWriter.CloseStream = false;
	doc.Close();
	memoryStream.Position = 0;
	
	return File(memoryStream, "application/pdf");
}

27 Jan 2012

Windows Server 2008 - MVC 3 - getting 404 errors from IIS 7

Well this one got us stumped for a long time this afternoon, and not even the mighty StackOverflow could help us!

We have an MVC 3 site that was running happily in dev, but when the client asked us to install it on their server (Windows 2008 SP2 (NOT R2), IIS 7.0), it wouldn't work; every View returned a 404.

We had just installed MVC 3 and ASP.NET 4, so we went though all the usual suspects:
  • Integrated pipeline
  • Permissions
  • runAllManagedModulesForAllRequests in system.webserver
No dice.

Eventually a random comment on some forum led us to the KB980368 Hotfix. That solved the problem!

VAG-COM / VCDS in Yorkshire for Audi A4 B7 Cabriolet

I wanted to change some settings on my A4 Cabriolet. The settings were:
  • Turn off selective locking (fob click only opens driver's door)
  • Turn off speed locking  (auto locking after 10mph)
  • Turn off seatbelt warning (module 17 penultimate code flip digit to 0) 
  • Enable telephone voice control (Electronics 2 - module 77 penultimate code flip digit from 0->2)  
To do this you need special software and a cable to the diagnostic port. The official VAS kit is crazy expensive, but the Ross-Tech VAG-COM/VCDS kit is £250. Still too much for me. I knew *what* to do from my research on the Audi forums, but I didn't have the kit.

So after an hour of waiting at Harrogate Audi in their swanky coffee area, they finally told me they had been unable to make the changes, and couldn't spare any more time looking at it. They also said not to believe everything you read on the Internet.

BAD HARROGATE AUDI, NO BISCUIT.

Well, just wanted to say that a good chap called Chris Dodgson from Auto Diagnostic Services drove his van round to my office today, plugged in his VCDS laptop and sorted all the changes in about 10 minutes.

It goes to show that Harrogate Audi is just a showroom, and their service department doesn't know how to do simple electronic changes on Audis. What a ridiculous situation.


If I helped you out today, you can buy me a beer below. Cheers!