Saturday 28 November 2009

Silverlight: how to decode a uri and pass the arguments to your method

This post has moved to http://www.scottleckie.com/2009/11/silverlight-how-to-decode-a-uri-and-pass-the-arguments-to-your-method/
The problem is that you need to pass your parameters to your Silverlight class and hopefully deal with them. First and foremost, how do you recognise that you’ve been called? Well, via the OnNavigatedTo event… this event is fired for every Silverlight page.
OK, so we captured the OnNavigatedTo event. What next? Well, next we try to decode the uri that was passed to the SilverLight app. Let’s pretend for a moment that we were called via “http:<YourSource>?title=myTitle&search=special”
We now have two session variables set; title=myTitle and search=special
So, how do we decode these? The easiest way is to use the SilverLight libraries to query the NavigationContext object;
if (this.NavigationContext.QueryString.ContainsKey("title"))
                this.Title = this.NavigationContext.QueryString["title"];
if (this.NavigationContext.QueryString.ContainsKey("search"))
                searchType = this.NavigationContext.QueryString["search"];

Wednesday 7 October 2009

Silverlight 3; Building a navigation tree from static and dynamic data

This post has moved to http://www.scottleckie.com/2009/10/silverlight-3-building-a-navigation-tree-from-static-and-dynamic-data/

The problem

Build a navigation tree that allows users to navigate to specific static pages, from static entries in the menus, and to pages that are built dynamically based on selections from the menu.

The Solution in a picture

image

The solution in code

Build the static menu selections in xaml, being sure to name each of the major headings with “Tag” entries;
<Grid x:Name="LayoutRoot">
<controls:TreeView x:Name="treeview"
BorderThickness="0" 
Margin="0" 
SelectedItemChanged="treeview_SelectedItemChanged">
<controls:TreeView.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="AntiqueWhite" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</controls:TreeView.Background>
<controls:TreeViewItem Header="Machines" Tag="Machines">
<controls:TreeViewItem Header="By type" x:Name="machinesByTypeTree">
</controls:TreeViewItem>
<controls:TreeViewItem Header="By O/S" x:Name="machinesByOsTree">
</controls:TreeViewItem>
</controls:TreeViewItem>
<controls:TreeViewItem Header="Software" Tag="Software">
<controls:TreeViewItem Header="By product" x:Name="softwareByProductTree">
</controls:TreeViewItem>
</controls:TreeViewItem>
<controls:TreeViewItem Header="Manufacturers" Tag="Manufacturers">
</controls:TreeViewItem>
<controls:TreeViewItem Header="Audit log">
<controls:TreeViewItem Header="Today" Tag="AuditToday" />
<controls:TreeViewItem Header="This week" Tag="AuditThisWeek" />
<controls:TreeViewItem Header="This month" Tag="AuditThisMonth" />
</controls:TreeViewItem>
</controls:TreeView>
</Grid>





Note that the “Tag=” entries define the major menu categories.


Next, build the dynamic entries, ensuring that we fill the planned dynamic menus. In the above example, we are going to complete the menus for “machinesByTypeTree” and “machinesByOsTree”;


internal class DynamicMenuEntry
{
public string MainMenuName;
public Object Dto;
}
private OperatingSystemTypeDomainContext osdc = new OperatingSystemTypeDomainContext();
private DeviceTypeDomainContext dtdc = new DeviceTypeDomainContext();
public TreeviewNavigator()
{
InitializeComponent();
osdc.Load(osdc.GetOperatingSystemTypesQuery(), LoadOsTypes, null);
dtdc.Load(dtdc.GetDeviceTypesQuery(), LoadDeviceTypes, null);
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void LoadOsTypes(LoadOperation lo)
{
machinesByOsTree.Items.Clear();
if (lo.Entities == null || lo.Entities.Count() < 1)
return;
foreach (OperatingSystemTypeDto os in lo.Entities)
{
TreeViewItem i = new TreeViewItem()
{
Header = os.Vendor + " " + os.Name + " " + os.Version,
Tag = new DynamicMenuEntry { MainMenuName = "MachinesByOsType", Dto = os }
};
machinesByOsTree.Items.Add(i);
}
}
private void LoadDeviceTypes(LoadOperation lo)
{
machinesByTypeTree.Items.Clear();
if (lo.Entities == null || lo.Entities.Count() < 1)
return;
foreach (DeviceTypeDto dt in lo.Entities)
{
TreeViewItem i = new TreeViewItem()
{
Header = dt.Name,
Tag = new DynamicMenuEntry { MainMenuName = "MachinesByDeviceType", Dto = dt }
};
machinesByTypeTree.Items.Add(i);
}
}


Note that, for each of the dynamic entries, we are recording a custom type “DynamicMenuEntry” that defines what the major menu category is. In other words, that is how we decide which menu to display, and the dynamic data is the parameter we pass to it.


The next stage is that we need to act on when the user clicks on a menu entry. We do this via the SelectedItemChanged event on the TreeView control which, in XAML, looks like this;


<controls:TreeView x:Name="treeview"
BorderThickness="0" 
Margin="0" 
SelectedItemChanged="treeview_SelectedItemChanged">
<controls:TreeView.Background>


And the code behind looks like this;


private void treeview_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
try
{
TreeView view = sender as TreeView;
if (view == null)
return;
TreeViewItem item = (TreeViewItem)view.SelectedItem;
if (item == null)
return;
if (item.Tag == null)
return;
if (item.Tag is string)
{
RunMenu((string)item.Tag, null);
return;
}
if (item.Tag is DynamicMenuEntry)
{
DynamicMenuEntry dme = (DynamicMenuEntry)item.Tag;
RunMenu(dme.MainMenuName, dme.Dto);
return;
}
}
catch (Exception exc)
{
ChildWindow err = new ErrorWindow("Unable to process navigation menu command", exc.Message);
err.Show();
return;
}
}


Here, we decode which menu we need to call, based on whether the tag is a string (“this is the page I need you to run”) or whether it is a DynamicMenuEntry (“this is the page I need you to run, and this is the parameter to pass”).


So, there we have it. For static menu entries, we use “.tag” to define the single page to call and for dynamic entries, the .tag defines the page to call and the parameter to pass (typically some kind of entity ID).

Silverlight 3 – Binding POCO objects to XAML

This post has moved to http://www.scottleckie.com/2009/10/silverlight-3-%e2%80%93-binding-poco-objects-to-xaml/

Obvious, really, but I spent ages chasing down how to bind some kind of POCO to a XAML display;
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name" Grid.Row="1" Grid.Column="1"/>
<TextBlock Text="Gender" Grid.Row="2" Grid.Column="1"/>
<TextBlock Text="Age" Grid.Row="3" Grid.Column="1"/>
<TextBlock Text="DOB" Grid.Row="4" Grid.Column="1"/>
<Border BorderThickness="1" BorderBrush="Black" Background="AliceBlue" CornerRadius="10" Grid.Row="1" Grid.Column="2">
<TextBlock x:Name="name" Text="{Binding Name}" Margin="5,5,5,5" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Background="AliceBlue" CornerRadius="10" Grid.Row="2" Grid.Column="2">
<TextBlock x:Name="gender" Text="{Binding Gender}" Margin="5,5,5,5" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Background="AliceBlue" CornerRadius="10" Grid.Row="3" Grid.Column="2">
<TextBlock x:Name="age" Text="{Binding Age}" Margin="5,5,5,5" />
</Border>
<Border BorderThickness="1" BorderBrush="Black" Background="AliceBlue" CornerRadius="10" Grid.Row="4" Grid.Column="2">
<controls:DatePicker x:Name="dob" Text="{Binding DOB}" IsEnabled="False" Margin="5,5,5,5" />
</Border>
</Grid>


And I could not figure out why it was refusing to recognise the object I was passing to it. Then I found that it is impossible to bind to a data source in XAML. You need to bind the data context in code;


public partial class MainPage : UserControl
{
Person person = new Person { Name = "Scott", Gender = 'M', Age = 43, DOB=new DateTime(1966, 7, 12) };
public MainPage()
{
InitializeComponent();
LayoutRoot.DataContext = person;
}
}





Ho hum. Three hours down the drain…

Silverlight 3 – handling exceptions in page views

This post has moved to http://www.scottleckie.com/2009/10/silverlight-3-%e2%80%93-handling-exceptions-in-page-views/

How do you handle the situation where your new Silverlight page needs to shout a warning or error, and then die? No idea, but this is how I do it…
Originally, I was quite scared of my Silverlight pages throwing an exception and then I realised that this was just another object and if we throw an exception, hopefully let the user know, and bail out, then that’s fine.
So the pattern I am now working with is this;
  1. Constructor – set up the load operations
  2. Page_Loaded events – bind the XAML objects to the retrieved data
    • On any error; display a meaningful message, in a semi-modal window
    • Navigate back to a safe place (e.g. /home)
What do I mean by “semi-modal”? Well, from the user’s perspective, it should be modal; display a message then abort. Silverlight, however, can’t display a truly modal message so we need to take account of that in exception handling… (more of which in a mo…)
So, step one, is to define a rather apt method called “ScreamAndExit()” which looks like this;
protected void ScreamAndExit(string msg)
{
ChildWindow err = new ErrorWindow("Could not load the requested machine list", msg);
err.Show();
NavigationService.Navigate(new Uri("/Home", UriKind.Relative));
}


Note that ErrorWindow is defined in the Silverlight 3.0 Navigation Framework.


How do we call this? (Relatively) easy;


// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
activityDisplay.IsActive = true;
int id = -1;
string searchType = "";
try
{
switch (searchType.ToLower())
{
// Magic
default: ScreamAndExit("Invalid request type");
return;
}
}
catch (Exception ex)
{
activityDisplay.IsActive = false;
ScreamAndExit(ex.Message);
return;
}
}


And that’s almost it. The “almost” refers to the “semi-modal” discussion; Silverlight windows display asynchronously and so if you simply call “ScreamAndExit” and carry on, then the rest of the code will be executed. Make sure you call ScreamAndExit and the return from each call…

Silverlight 3 Navigation – navigating from a UserControl

This post has moved to http://www.scottleckie.com/2009/10/silverlight-3-navigation-%e2%80%93-navigating-from-a-usercontrol/

I’ve been toying with Silverlight over the last couple of weeks and, I have to say, it’s much more complete than ASP.Net ever was (**). Lots of new paradigms to get my head around, of course, but overall it’s a very complete solution for an RIA.
Anyway, to the point of this posting; the new (in Silverlight 3.0) Navigation Framework is great; it allows you to present a common interface and have new pages display in a know part of the browser window, supports browser back/forward, and URL rewrites. See here for more information.
If you are inside a page that is controlled by the framework, then you are able to call on the NavigationService.Navigate method to divert control to a new XAML page. For example;
this.NavigationService.Navigate( new Uri( String.Format( "/Views/Item.xaml?title={0}&type={1}", ...), UriKind.Relative ) );


This is all well and good if you inherited from the Page class within the Navigation Framework, but what if you are a user control, out on a limb, with no knowledge of the Navigation Framework? In my case, I have a user control that displays a treeview context list to the user. Some of the options are static (“select all machines”, “select all software”, etc) and some is dynamic (“select a machine of type…”). Once the user clicked on their desired option, we did some menu calculations and then finally asked the Silverlight Navigation Framework to display the required page in the Content frame. Except we couldn’t… a UserControl has no knowledge of the Navigation Framework and so can’t instruct it to navigate to a specific page.


Darn.


The ultimate solution is that the application’s “main page” (which we can find down the Application.Current map) is able to influence the navigation, so stage one was to add a “public bool NavigateTo(Uri uri)” method to the MainPage.xaml file;


public partial class MainPage : UserControl
{
public bool NavigateTo(Uri uri)
{
return this.ContentFrame.Navigate(uri);
}





Now, we just need to call the NavigateTo method from our non-navigational UserControl. To do this, we need to find the Main Page, which we do by tracking back to the “RootVisual” element in the MainPage.xaml file (if you renamed any of this, then you’ll need to change some of the references, below);


private void RunMenu(string requestedMenu, object o)
{
MainPage mp = ((MainPage) Application.Current.RootVisual as MainPage);
switch (requestedMenu.ToLower())
{
case "machines": mp.NavigateTo(new Uri("/MachineList", UriKind.Relative)); break;
case "software": mp.NavigateTo(new Uri("/Home", UriKind.Relative)); break;


Here, we find a reference to the Main Page via Application.Current.RootVisual (remember MainPage.xaml?) Once we’ve done that, we call the main page object and ask it to “NavigateTo” a new page.


To put this into context, here’s the application browser view;


image





(** only in terms of the fact that ASP.Net always seemed to be a halfway house to me – you needed to jump through a lot of hoops to persuade the browser to do what you want it to do. Silverlight, of course, was designed as an RIA from conception so it seems to fit more easily than a native HTML / ASP.Net app. Of course, the trade-off is that you need to present a whole new runtime environment to the user, in terms of the Silverlight VM, but I can live with that for a) more rapid development and b) less concern over what the browser is going to do with our code.)

Sunday 12 July 2009

Iterating through a bunch of folders and files

This post has moved to http://www.scottleckie.com/2009/07/iterating-through-a-bunch-of-folders-and-files/

So you want to start at a top level folder, and then process all the folders beneath… Maybe you really do want to look at every file (maybe count the total size of the folder), maybe you want to process all the XML files there. The most obvious route is to recursively search through each folder;
static void Main(string[] args)
{
string startFolder = @"C:\temp";
List<string> contents = new List<string>();
foreach (string dir in Directory.GetDirectories(startFolder))
ProcessFolder(dir, contents);
foreach (string fileName in contents)
Console.WriteLine(fileName);
Console.ReadKey();
}
static void ProcessFolder(string folder, IList<string> theList)
{
foreach (string file in Directory.GetFiles(folder))
theList.Add(folder + "\\" + file);
foreach (string dir in Directory.GetDirectories(folder))
ProcessFolder(dir, theList);
}


All well and good but, at some point (probably due to the depth of the file system) you will run out of stack space.  A better way to traverse the folder structure is to do an iterative search;


private void ProcessFolder(string startingPath)
{
int iterator = 0;
List<string> dirList = new List<string>();
dirList.Add(startingPath);
string parentFolder = startingPath;
// Every new folder found is added to the list to be searched. Continue until we have
// found, and reported on, every folder or the calling thread wants us to stop
while (iterator < dirList.Count && !(workerThreadInfo.StopRequested))
{
parentFolder = dirList[iterator];       // Each FileTreeEntry wants to know who its parent is
try
{
foreach (string dir in Directory.GetDirectories(dirList[iterator]))
{
AddFolder(parentFolder, dir, dir);
dirList.Add(dir);
}
foreach (string filename in Directory.GetFiles(dirList[iterator]))
{
FileInfo file = new FileInfo(filename);
AddFile(parentFolder, file.Name, file.Length);
}
}
// There are two *acceptable* exceptions that we may see, but should not consider fatal
catch (UnauthorizedAccessException)
{
}
catch (PathTooLongException)
{
}
iterator++;
}
}


Now, we iterate through each discovered folder and for each discovered file, we call an external routine (in this case “AddFile()”. Note the two caught exceptions which can occur but which, in the author’s opinion, are not important in this context;


  • UnauthorizedAccessException
    • OK; ya got me. I’m not allowed in here, so let’s continue and not break the calling app
  • PathTooLongException
    • This is a funny one. Win200x sets a maximum path length of 255 characters. Create a big and complex structure (especially a Java one), zip it and then unravel it under a folder that is maybe 100 characters long. Windows is happy to unzip this, and even display it in the folder view. But try and open the file and you’ll be stuffed





So, ignoring these, this routine will handle any files within a structure, irrespective of how deep that structure gets.

Validating XML Files against XSD Schemas (especially for files that don’t reference the schema)

This post has moved to http://www.scottleckie.com/2009/07/validating-xml-files-against-xsd-schemas-especially-for-files-that-don%e2%80%99t-reference-the-schema/

Wow, XML is a pig, isn’t it? Don’t get me wrong; it does everything I need it to do in describing multi-faceted data, but it’s a pretty steep learning curve.
At first, I used XPath and a lot of coded validation. Then I finally invested time in learning XML Schemas (XSDs) and that helped a lot because I could validate the entire document and, only when I knew it was valid, start pulling data out of it. At around the same time, I stumbled upon LINQ to XML and this shortened the code substantially.  Now, all I had to do was validate the document against the XSD and, if it passed, get to decoding it via LINQ and we’re done and dusted in a few lines of code.
The XSD validation, by the way, looks like this;
private static readonly string SCHEMA = "http://schemas.axiossystems.com/DDI/SnmpMappings/";
private static bool ValidateFile(string file, string schemaFile)
{
if (file == null || schemaFile == null)
throw new ArgumentNullException("Must supply non-null file and Schema file to MappingFilesParser.ValidateFile()");
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(SCHEMA, schemaFile);
XDocument doc = XDocument.Load(file);
doc.Validate(schemas, (o, e) =>
{
throw new XmlSchemaValidationException(string.Format("{0} validating {1}", e.Message, file));
});
return true;
}


So, here you define the schema used in your XML file in the static readonly string called “SCHEMA”, then call ValidateFile(name of XML File, name of schema file).


The function either returns or throws ArgumentNullException or XmlSchemaValidationException.


All good so far. Then I realised that, if you load any random XML file that does not reference your schema file then the .Validate() method will still complete successfully.


What I mean by this is that every XML file has to have an xmlns namespace declaration similar to this;


<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="http://schemas.axiossystems.com/DDI/SnmpMappings/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ddi="http://schemas.axiossystems.com/DDI/SnmpMappings/"
elementFormDefault="qualified"
attributeFormDefault="unqualified">


If your XML file does not refer to the XSD then the validation passes, which is not what I intended. The solution, as outlined in Scott Hansleman’s blog is to check the namespaces in the XML file and confirm that it does reference our XSD, then run through the validation. So, now the ValidateFile() method looks like this;


private static readonly string SCHEMA = "http://schemas.axiossystems.com/DDI/SnmpMappings/";
private static bool ValidateFile(string file, string schemaFile)
{
if (file == null || schemaFile == null)
throw new ArgumentNullException("Must supply non-null file and Schema file to MappingFilesParser.ValidateFile()");
logger.InfoFormat("Validating {0} against schema; {1}", file, schemaFile);
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(SCHEMA, schemaFile);
XPathDocument x = new XPathDocument(file);
XPathNavigator nav = x.CreateNavigator();
nav.MoveToFollowing(XPathNodeType.Element);
IDictionary<string, string> schemasInFile = nav.GetNamespacesInScope(XmlNamespaceScope.All);
bool foundOurSchema = false;
foreach (KeyValuePair<string, string> namespaces in schemasInFile)
{
if (namespaces.Value.CompareTo(SCHEMA) == 0)
foundOurSchema = true;
}
if (!foundOurSchema)
throw new XmlSchemaValidationException(string.Format("The file {0} does not reference the required schema; {1}",
file, SCHEMA));
XDocument doc = XDocument.Load(file);
doc.Validate(schemas, (o, e) =>
{
throw new XmlSchemaValidationException(string.Format("{0} validating {1}", e.Message, file));
});
return true;
}


Here, we first confirm that the XML file references the XSD, then validate against the XSD.

Monday 8 June 2009

FileTreeView – a SequioaView-like Application

This post has moved to http://www.scottleckie.com/2009/06/filetreeview-%e2%80%93-a-sequioaview-like-application/

I've long been a huge fan of the SequoiaView application released by Technische Universiteit Eindhoven, which displays disk utilization in a beautiful squarified cushion treemap format.
This was released in 2002 and does a great job of showing exactly what's eating the space on your disk, but it has one major drawback; if you point it at a 2TB volume with a million files, but you only want to see what's taking the space in a small corner of the disk, it reads the entire volume before displaying what you originally asked it to.
So, I decided to write a C# alternative to SequoiaView, partly to help us find the big files in specific folders really quickly, and partly just as an exercise.
Article
You can download the setup from here. Do be aware that this requires the dotNet Framework 2.0 or higher and, while the setup is supposed to go fetch this if required, it does seem a little flakey. In other words, it’s probably safer to ensure you have the .Net Framework 2.0 or higher installed before starting.
You also need to install the Microsoft Data Visualization Components, which are available here.

Source

I’m going to be uploading the source, plus a background article, to CodeProject shortly. I’ll come back and update this link then.

Using FileTreeView

Refer to the following picture for each of the components within FileTreeView;
application_guide
  1. Enter the path or folder that you want to display here, or press the "..." button to browse, then press "Go"
  2. The number of folders and files found so far will be displayed here. You can press "Cancel" if you get bored and it will display what it has so far
  3. This rather nifty set of colours (actually seven little label controls) allows you to set the node colours
  4. By default, the names of the files and folders will be displayed at all depths (which means from the top folder down to the deepest folder). Use this slider to de-clutter the display by displaying labels only to the depth of your choosing
  5. This is the TreeMap control, that displays all discovered folders and their contents, grouped by the relative size of each folder
  6. Each folder or file is a node. You can hover over a node to see its details, double-click to drill down or right-click for more options
  7. Right clicking a node allows you to display it in Windows Explorer, open it, or zoom in and out of the tree structure

Licence

The FileTreeView application is open source, under the CodeProject Open License. Note that the Microsoft Data Visualization Components are free to use for non-commercial use only. See here for specific licence terms.

Help

If you have any suggestions or issues, please post a comment to this article.