• Blog

  • About Me

  • < Older Posts

    Skype

    21-8-2008

    How to get the screen dimensions across all monitors in WPF

    I seem to be needing this code in every WPF I start at the moment, so here it is in a nice reproducable form. You need to add references to System.Windows.Forms and System.Drawing in order to do this though. Shame that dependency is required - I don't see how getting screen information is strictly related to forms, so it'd be cool to be able to do this without requiring the System.Windows.Forms reference. Anyway, stick this somewhere in your app (I put it in the App class and assign it to a static member):

    27 private static Rect GetTotalScreenArea()

    28 {

    29 Rect screenArea = new Rect();

    30 foreach (Screen s in Screen.AllScreens)

    31 {

    32 if (s.Bounds.Height > screenArea.Height)

    33 screenArea.Height = s.Bounds.Height;

    34

    35 screenArea.Width += s.Bounds.Width;

    36 }

    37 return screenArea;

    38 }

     

    So my App.xaml.cs looks like this:

     

    1 using System;

    2 using System.Collections.Generic;

    3 using System.Configuration;

    4 using System.Data;

    5 using System.Linq;

    6 using System.Windows;

    7 using System.Windows.Forms;

    8

    9 namespace MyProject

    10 {

    11 ///

    12 /// Interaction logic for App.xaml

    13 ///

    14 public partial class App : Application

    15 {

    16 public static Rect TotalScreenArea;

    17

    18 protected override void OnStartup(StartupEventArgs e)

    19 {

    20 TotalScreenArea = GetTotalScreenArea();

    21

    22 base.OnStartup(e);

    23 }

    24

    25 private static Rect GetTotalScreenArea()

    26 {

    27 Rect screenArea = new Rect();

    28 foreach (Screen s in Screen.AllScreens)

    29 {

    30 if (s.Bounds.Height > screenArea.Height)

    31 screenArea.Height = s.Bounds.Height;

    32

    33 screenArea.Width += s.Bounds.Width;

    34 }

    35 return screenArea;

    36 }

    37 }

    38 }

    1-8-2008

    Load ASP.Net MVC Routes dynamically at runtime from a repository

    A zip of source is at the end of this post.

    Today I read Ian Suttle's post about loading MVC routes dynamically from a SQL database. I think it's a cool idea, and I wanted to see if I could do the same thing with XML or a configuration file. I also wanted to introduce the factory design pattern so that people can plug in their own route repositories.

    My solution comprises the following classes:

    • MvcRoute - main route DTO
    • MvcRouteParam - route parameter DTO
    • IRouteService - interface defining a service which loads route data from a repository
    • RouteServiceBase - abstract class handles most common route service operations
    • ConfigRouteService - extends RouteServiceBase,  is responsible for reading configuration settings and constructing an IRouteService
    • DynamicRoutesConfigurationHandler and DynamicRoutesConfiguration - parse the configuration

    A couple of caveats:

    • It's not complete, it's a proof of concept
    • It doesn't handle 'ignore' routes

    Settings your routes looks like this:

    24 protected void Application_Start()

    25 {

    26 IRouteService routeService = new ConfigRouteService();

    27 routeService.SetAppRoutes(RouteTable.Routes);

    28 RegisterRoutes(RouteTable.Routes);

    29 }

     

    IRouteService is implemented thusly:

     

    11 namespace DynamicRoutes

    12 {

    13 public interface IRouteService

    14 {

    15 List<MvcRoute> GetConfiguredRoutes();

    16 RouteCollection ResetAppRoutes(RouteCollection RouteTable);

    17 RouteCollection SetAppRoutes(RouteCollection RouteTable);

    18 RouteCollection SetAppRoutes(RouteCollection RouteTable,

    19 List<MvcRoute> ConfiguredRoutes);

    20 }

    21 }

     

     

    Most importantly you have a method to get route data from your chosen repository (GetConfiguredRoutes) and a method to add the routes to your application RouteTable (SetAppRoutes).

     

    The RouteServiceBase class implements all methods except GetConfiguredRoutes, and this should be the only method you need to implement yourself in order to set up a new route repository. For example, here's the implementation for ConfigRouteService:

     

     

    9 namespace DynamicRoutes

    10 {

    11 public class ConfigRouteService : RouteServiceBase

    12 {

    13 public override List<MvcRoute> GetConfiguredRoutes()

    14 {

    15 DynamicRoutesConfigurationSection configuration =

    16 ConfigurationManager.GetSection("dynamicRoutes") as

    17 DynamicRoutesConfigurationSection;

    18

    19 return configuration.Routes;

    20 }

    21 }

    22 }

     

     

    And here are the configuration handler classes:

     

     

    7 namespace DynamicRoutes

    8 {

    9 public class DynamicRoutesConfigurationHandler : IConfigurationSectionHandler

    10 {

    11 public object Create(object parent, object configContext, System.Xml.XmlNode section)

    12 {

    13 DynamicRoutesConfigurationSection config = new DynamicRoutesConfigurationSection();

    14 config.LoadRoutesFromConfig(section);

    15 return config;

    16 }

    17 }

    18 }

    8 namespace DynamicRoutes

    9 {

    10 public class DynamicRoutesConfigurationSection : ConfigurationSection

    11 {

    12 public List<MvcRoute> Routes { get; set; }

    13 public string TypeName { get; set; }

    14 public void LoadRoutesFromConfig(XmlNode section)

    15 {

    16 Routes = new List<MvcRoute>();

    17 TypeName = section.Attributes["type"].Value;

    18 foreach (XmlNode node in section.ChildNodes)

    19 {

    20 if (node.Name == "route" && node.Attributes["type"].Value == "map")

    21 Routes.Add(new DynamicRouteConfiguration().LoadRouteMappingFromConfig(node));

    22 }

    23 }

    24 }

    25

    26 public class DynamicRouteConfiguration

    27 {

    28 internal MvcRoute LoadRouteMappingFromConfig(XmlNode routeNode)

    29 {

    30 MvcRoute route = new MvcRoute();

    31

    32 route.routeName = routeNode.Attributes["name"].Value;

    33 route.routePattern = routeNode.Attributes["pattern"].Value;

    34 route.routeParams = new List<MvcRouteParam>();

    35

    36 foreach (XmlNode node in routeNode.ChildNodes)

    37 {

    38 if (node.Name == "param")

    39 route.routeParams.Add(LoadParamFromConfig(node));

    40 }

    41

    42 return route;

    43 }

    44

    45 internal MvcRouteParam LoadParamFromConfig(XmlNode paramNode)

    46 {

    47 MvcRouteParam param = new MvcRouteParam();

    48

    49 param.paramKey = paramNode.Attributes["key"].Value;

    50 param.paramValue = paramNode.Attributes["defaultValue"].Value;

    51

    52 return param;

    53 }

    54 }

    55 }

     

     

    Finally, here's what's in my web.config to set up my default route:

     

     

    11 <configSections>

    12 <section name="dynamicRoutes" type="DynamicRoutes.DynamicRoutesConfigurationHandler, DynamicRoutes"/>

    13 configSections>

    14

    15 <dynamicRoutes>

    16 <route type="ignore" url="{resource}.axd/{*pathInfo}"/>

    17 <route type="map" name="Default" pattern="{controller}/{action}/{id}">

    18 <param key="controller" defaultValue="Home"/>

    19 <param key="action" defaultValue="Index"/>

    20 route>

    21 dynamicRoutes>

     

    This all probably sounds like nonsense so download the complete source here.

     

     

    29-7-2008

    How cool is Cuil?

    So there's buzz around this new search engine, but I want to know if it's good. I like the interface, but I want to know if it's something I'd actually use. So I performed a couple of tests, and I wasn't overly impressed. Here are my results with handy links if you want to compare for yourself.

    Search Results

    Recent content: Search term = 'asp.net mvc preview 4'

    Cuil: http://www.cuil.com/search?q=asp.net%20mvc%20preview%204&sl=long

    The first page gives me eleven links to content, none of which is specifically to do with the latest build of the ASP.Net MVC Framework, let alone a link to a download page. Some of the pages are tutorials relevant to older versions of the framework but may still be applicable to the new version.

    Google: http://www.google.co.uk/search?q=asp.net+mvc+preview+4&sourceid=navclient-ff&ie=UTF-8&rlz=1B3GGGL_enGB260GB260

    The striking thing to notice in Google's results page is that, of the ten results, nine of them actually have the words 'preview 4' in the title. From this I can immediately gather that their results are more relevant to me. Also, three of the pages contained links to the MVC project page or the installer on CodePlex, and one of the pages was a direct link to the CodePlex project page.

    Social networking content: Search term = 'twitter andrew myhre'

    Cuil: http://www.cuil.com/search?q=twitter%20andrew%20myhre&sl=long

    Interestingly, Cuil's first page provides a list of people I follow/who follow me, but none of the results is a link to me or my Twitter page. Most likely that's because noone would deliberately link to my feed, but those links do exist on the pages in Cuil's search result.

    Google: http://www.google.co.uk/search?hl=en&rlz=1B3GGGL_enGB260GB260&q=twitter+andrew+myhre&btnG=Search&meta=

    Google gives a somewhat better result. The first link is to a tweet within the feed of someone who follows me, and the second is a link to their profile. The third and fourth links are to my FriendFeed and the fifth is to a much-less-up-to-date mirror of this blog. The rest are barely or not at all related to me. Somewhat better than Cuil but I still expected better somehow.

    Most interesting is when I search using my actual Twitter username 'andrew_myhre':

    http://www.google.co.uk/search?hl=en&rlz=1B3GGGL_enGB260GB260&q=twitter+andrew_myhre&btnG=Search&meta=

    http://www.cuil.com/search?q=twitter%20andrew_myhre&sl=long

    Cuil actually returns zero results. Disappointing.

    Wikipedia content: Search term = 'wiki bicycle pump'

    Google: http://www.google.co.uk/search?hl=en&q=wiki+bicycle+pump&btnG=Google+Search&meta=

    As expected, the first result is the exact record I'm looking for - the Wikipedia entry on bicycle pumps.

    Cuil: http://www.cuil.com/search?q=wiki%20bicycle%20pump&sl=long

    Cuil earnestly presents a number of options but comes up short. No links to Wikipedia to be found.

    Summary

    Generally, Cuil doesn't give me information that's as relevant as what Google does. The problem seems to lie in the relationships between content nodes - why can't Cuil knit my blog and social networking sites together to provide a seemingly complete 'whole' in the way that Google does?

    On top of this the value-adds that Google provides like postcode parsing, shopping information and natural language parsing of terms like time zones, weather and money (interesting that they don't parse Zimbabwe dollars) I just get a lot more out of Google's results.

    29-7-2008

    Howto: "Object not set to an instance of an object" in Visual Studio 2008 when trying to add a Silverlight project to a solution

    I installed Visual Studio 2008 SP1 Beta, .Net 3.5 SP1 Beta and the new Silverlight Tools Beta 2, and when I tried to add a new Silverlight project to my solution I got a dialog in Visual Studio with the message "Object not set to an instance of an object", after which nothing happens and my project was not added.

    If you're having the same problem try these steps:

    1. Open Visual Studio Command Prompt
    2. devenv /resetskippkgs [return]
    3. Close VS
    4. devenv /setup [return]
    5. devenv [return]

    Hopefully now you'll be able to add a Silverlight project to your solution.

    Source of this solution here.

    29-7-2008

    Performing a case-sensitive search and replace in SQL 2000/2005

    If you need to perform a case sensitive search and/or replace in an SQL 2000 or SQL 2005 database you need to use the correct collation method. In the situation I had today I needed to change some copy throughout a website, all of which is in a string resource table, but I had to be careful to maintian case used, i.e: 'Shipping' had to change to 'Delivery', but 'shipping' had to change to 'delivery'.

    Your database may have been set up to use a case-sensitive collation method, or it may not have, or you may not have been involved in setup. I don't know which collation method is the smartest in the world to use - I'm not a DBA - but here's how to find out which collation you're working with.

    Execute this query in a query analyser:

    select charindex('If the result is 0 you are in a case-sensitive collation mode', 'RESULT')

    A 0 return result means you're working with a case-sensitive collation mode. This means you can write your search/replace query (I'll give an example below) without specifying the collation method you want to use and your query will consider 'RESULT' different to 'result'.

    If you DO have to specify a collation method you just have to declare it after the column name you're interested in. Here's an example:

    update StringResource
    set ConfigValue = replace(ConfigValue COLLATE Latin1_General_BIN, 'Shipping', 'Delivery')
    from StringResource
    where charindex('Shipping', configvalue COLLATE Latin1_General_BIN) > 0
    
    update StringResource
    set ConfigValue = replace(ConfigValue COLLATE Latin1_General_BIN, 'shipping', 'delivery')
    from StringResource
    where charindex('shipping', configvalue COLLATE Latin1_General_BIN) > 0

    This query replaces the word 'Shipping' with 'Delivery' in the ConfigValue column in the StringResource table. There are two seperate statements, one for each case I'm replacing, because I need to specify the casing I'm concerned with explicitly. The important part of the collation type is the _BIN part, which specifies that I want to compare strings as binary data. More information about aspects of collation sorting here. This method may not be the smartest in the world so I would appreciate any comments.

    29-7-2008

    Howto: Only include part of a project workspace in a TFS 2008 Team Build

    I'm working on a website with a large design aspect, so we have a lot of .psd files (about 200mb) kicking about the place. We want them source controlled, so they live in the TFS 2008 team project folder in source control. But then our Team Build includes them when downloading the source files, so that quickly fills up our build server's drive and makes a single build take up to 11 minutes, which is far too long for a basic eCommerce website.

    So I trimmed these files from our build process. Here's what I did:

    1. Copy the .psd files and any other files that aren't required for a build into a seperate folder in the root of the team project. (All our website/source files are in another folder called 'Current', as in the 'current version', as opposed to 'Phase 1', 'Phase 2')
    2. In Team Explorer, right click the build you want to edit and selected Edit Build Definition...
    3. In the Workspace tab, enter the path within source control where you want to start downloading files from. In our case I chose the $/[project]/Current folder, so the /PSDs folder will be ignored and not downloaded.

    Now, thanks to the above steps my build history folders are 1/10th the size they were to begin with, and the build process takes less than half the time!

    < Older Posts
    andrew myhre