I just registered for the free Xceed DataGrid for WPF, and there’s only 3 days left to do it. http://xceed.com/freegrid. Get it now before it becomes the Standard Edition priced at $300 and get a free upgrade to the Standard Edition!
Testing Private Members in Visual Studio
Wednesday, 10 June 2009 .net , attached properties , c# , CodeProject , dependency properties , testing , Visual StudioI’m currently working on a CommandBehaviour class to enable me to fire commands in response to arbitrary routed events on elements that don’t natively support commands, inspired by Sacha Barber’s post WPF: Attached Commands.
Whilst trying to apply some unit tests to what I’m writing I came across the age old problem of how to test things which are private by design. Under normal circumstances I would probably have made them protected and written a TestableCommandBehaviour class in the test project that inherits from CommandBehaviour and exposes the protected members that I wanted to test.
However, in this particular instance, I’d already written the code so I used the Create Unit Tests feature in Visual Studio to generate some unit tests for me to get me started. What I didn’t realise before was that when you do this, it creates a “Test Reference”, which provides you with a private accessor that you use to access the private code*.
Here’s a cut down version of the CommandBehaviour class:
1: namespace DerekLakin.Libraries.Presentation
2: {
3: public class CommandBehaviour
4: {
5: public static readonly DependencyProperty CommandProperty =
6: DependencyProperty.RegisterAttached(
7: "Command",
8: typeof(ICommand),
9: typeof(CommandBehaviour),
10: new UIPropertyMetadata(null));
11:
12: public static readonly DependencyProperty CommandParameterProperty =
13: DependencyProperty.RegisterAttached(
14: "CommandParameter",
15: typeof(object),
16: typeof(CommandBehaviour),
17: new UIPropertyMetadata(null));
18:
19: public static readonly DependencyProperty EventNameProperty =
20: DependencyProperty.RegisterAttached(
21: "EventName",
22: typeof(string),
23: typeof(CommandBehaviour),
24: new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(EventNameChanged)));
25:
26: private static readonly DependencyProperty CommandBehaviourProperty =
27: DependencyProperty.RegisterAttached(
28: "CommandBehaviour",
29: typeof(CommandBehaviour),
30: typeof(CommandBehaviour),
31: new UIPropertyMetadata(null));
32:
33: private readonly WeakReference sourceElement;
34: private EventInfo eventInformation;
35: private Delegate targetDelegate;
36:
37: private CommandBehaviour()
38: {
39: }
40:
41: private CommandBehaviour(DependencyObject source)
42: {
43: this.sourceElement = new WeakReference(source);
44: }
45: ...
46: }
47: }
A CommandBehaviour instance is created when the EventName attached property is set and the targetDelegate member is set when the relevant event has been hooked. In my unit test, I wanted to check that this member was actually being set and here’s how I did it using the accessor:
1: [TestMethod]
2: [DeploymentItem("DerekLakin.Libraries.Presentation.dll")]
3: public void RemoveEventHandlerTest()
4: {
5: Grid source = new Grid();
6: source.SetValue(
7: CommandBehaviour.CommandProperty,
8: ApplicationCommands.Open);
9: source.SetValue(
10: CommandBehaviour.EventNameProperty,
11: "MouseLeftButtonUp");
12: CommandBehaviour real = (CommandBehaviour)
13: source.GetValue(
14: CommandBehaviour_Accessor.CommandBehaviourProperty);
15: CommandBehaviour_Accessor target =
16: new CommandBehaviour_Accessor(
17: new PrivateObject(real));
18: Assert.IsNotNull(target.targetDelegate);
19: target.RemoveEventHandler();
20: Assert.IsNull(target.targetDelegate);
21: }
First, I create a Grid instance and set the Command and EventName attached properties on it. Next, I get the CommandBehaviour instance from the Grid instance by using the accessor’s CommandBehaviourProperty (which is normally private). Then, I create a CommandBehaviour_Accessor instance that wraps the CommandBehaviour instance by using the PrivateObject class. Finally, I use regular Assert statements against the accessor to check the private members. Job done!
* For more information about testing private methods, see How to: Test a Private Method on the MSDN web site.
Cloud-Based Source Control using Live Mesh and Git
Monday, 8 June 2009 add-in , cloud , CodeProject , Git , Live Mesh , source control , Visual StudioI’ve been working on a couple of projects recently where I’ve been storing the source in the cloud using Live Mesh so that I could access the solution from any of the PCs and laptops that litter the house. On the whole this approach has worked fairly well, although it results in a lot of changes through Mesh and occasionally locks files. The other drawback is that there is no file versioning and I like to be able to rollback some changes if I decide the approach I’ve taken isn’t working out for me.
I’ve had prior history of working with a number of different source control solutions and though that Subversion might be a good fit for a Mesh-based repository. Unfortunately, after creating a repository in the Mesh, AnkhSVN wouldn’t recognise the Mesh folder as a valid repository, so I couldn’t connect to it. Having given up on the possibility of getting working version control in the Mesh, I found Ten steps on how to store Git repositories in Live Mesh! by Pål Fossmo. I’d seen a lot of buzz about Git, but never actually tried it, so it though I’d give it a whirl. I was also interested in some kind of integration in Visual Studio and found the Git Extensions, which include an add-in for Visual Studio, so I modified the original approach slightly and came up with my own:
Ten Steps to Storing Git Repositories in the in Live Mesh
- Install.
Download and install Git Extensions from http://code.google.com/p/gitextensions (I used 1.62, but there is a version 1.64 as I write this post), which includes Git 1.6.2.2, by using the default values during installation. - Mesh Account. Create a Live Mesh account if you don’t already have one at http://www.mesh.com.
Mesh Folder. Create a folder in the Mesh and synchronise it with the required devices. I called mine Repository. - Local Repository. To create a local repository, navigate to the folder containing your source code, right-click, click Git GUI Here, and then click Create New Repository. In the Git Gui dialog, select the target folder, and then click Create. Git displays a dialog showing the files that will be added.
- Exclude Files. To exclude files that you don’t want to be version controlled, edit the file named exclude in: <New repository>\.git\info\. My exclude file uses the defaults for Visual Studio plus a few extras, such as *.vsmdi.
- Commit Initial Version. In the Git Gui dialog (from Step 4), click Rescan to update the list of files to be added, type “Initial version.” into the Commit Message box, and then click Commit.
Clone the Repository into the Mesh. Open your solution in Visual Studio, and then open a file (any source file it doesn’t matter*). On the Git menu, click Clone repository. In the Clone dialog, make sure the Repository to clone folder is correct, select the destination folder, which is your Mesh folder, enter the name of the Subdirectory to create, select Central repository, no working dir (--bare), and then click Clone.
Push Changes to the Mesh. Commit local changes as usual by using the Commit button on the Git Extensions toolbar. When you are ready to push some changes into the Mesh: in Visual Studio, click Push changes to remote repository on the Git Extensions toolbar. In the Push dialog, select Url, click Browse to select the Mesh folder to push to, select master from the Branch to push list, and then click Push. - Get Source Files on Different PC. Install Git Extensions as described in Step 1. Create a folder to contain the source files, and then create a local repository as described in Step 4. Update the exclude file as described in Step 5.
- Pull the Source From the Repository. Right-click the folder for the new repository, and then click Git Bash Here. In the command prompt, type:
git pull C:/Users/<user>/Desktop/Repository/<SubFolder> master
You now have the same source files on both PCs. You can use the push and pull buttons on the Git Extensions toolbar in Visual Studio to transfer changes via the Mesh.
* The Git Extensions add-in for Visual Studio is a regular add-in, not a source control provider. For any command to work, you must have a source file open.
Side Note
If you want to see your own name in the version history, click Settings on the Git Extensions toolbar, and enter your details on the Global Settings tab. You can also change the merge tool on this tab (I personally prefer SourceGear’s DiffMerge).
Fractured Metacarpal
Tuesday, 21 April 2009 broken , flickr , fracture , personal
Not wanting to be outdone by my Mum who broke her arm in New Zealand recently, yesterday (after 33 years with no broken bones) I managed to break the left little finger metacarpal.
I was cycling back from town when the chain slipped off the gears throwing the bike to the left and me into some iron railings. My little finger hyper flexed into my palm and snapped the metacarpal in the process.
I'm now bandaged up (no cast) and typing with one hand plus two fingers and a thumb!
Defining the Default Style for a Lookless Control
Tuesday, 14 April 2009 .net , c# , CodeProject , custom , styles , UI , WPF , XAMLMicrosoft introduced the concept of “lookless” controls in WPF, which means that the control defines its behaviour without any information about how it actually looks, which is where templates and styles come in. However, all of the default controls provide a default look, which is in keeping with the current Windows theme (Aero in Windows Vista, Luna, Metallic, or Homestead in Windows XP and so on). If you are creating lookless controls in a class library for general consumption, it would be helpful to also provide a default look for your controls. To achieve this, there are three things that you need to do.
Note: This article refers specifically to custom controls (i.e. those that inherit from Control, FrameworkElement or similar, not user controls that inherit from UserControl). For more information about creating controls for WPF, see Control Authoring Overview on the MSDN web site.
1. Override the Metadata for the DefaultStyleKey Property
In a static constructor for your control, set the default style key to the type name, which is used to do style lookups for your control. The following code example shows the static constructor for a Wizard class that I’m creating.
1: static Wizard()
2: {
3: DefaultStyleKeyProperty.OverrideMetadata(
4: typeof(Wizard),
5: new FrameworkPropertyMetadata(typeof(Wizard)));
6: }
2. Define Your Default Style
You can create a default style for each of the different Windows themes. To do this you need to create resource dictionary with a specific name in a Themes folder that is a subfolder of the folder that contains your control in your class library. The following table provides the file names of the theme-specific resource dictionaries.
| Resource dictionary | Windows theme |
| Classic.xaml | “Classic” Windows 9x/2000 look on Windows XP |
| Luna.NormalColor.xaml | Default blue theme on Windows XP |
| Luna.Homestead.xaml | Olive theme on Windows XP |
| Luna.Metallic.xaml | Silver theme on Windows XP |
| Royale.NormalColor.xaml | Default theme on Windows XP Media Center Edition |
| Aero.NormalColor.xaml | Default theme on Windows Vista |
You don’t need to provide a style for every theme. If there is no theme-specific resource, then the generic resource is used for the control, which is defined in the Themes\generic.xaml resource dictionary.
The following XAML code example shows the default style in generic.xaml for the Wizard class described above.
1: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2: xmlns:local="clr-namespace:DerekLakin.Libraries.Presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
4: <Style TargetType="{x:Type local:Wizard}">
5: <Setter Property="Template">
6: <Setter.Value>
7: <ControlTemplate TargetType="{x:Type local:Wizard}">
8: <DockPanel LastChildFill="True">
9: <WrapPanel DockPanel.Dock="Bottom"
10: HorizontalAlignment="Right"
11: Orientation="Horizontal"
12: Margin="10">
13: <Button x:Name="PART_Back"
14: Content="Back" />
15: <Button x:Name="PART_Next"
16: Content="Next"
17: Margin="4,0,0,0" />
18: <Button x:Name="PART_Cancel"
19: Content="Cancel"
20: Margin="4,0,0,0" />
21: <Button x:Name="PART_Finish"
22: Content="Finish"
23: Margin="4,0,0,0" />
24: </WrapPanel>
25: <Frame x:Name="PART_Frame"
26: NavigationUIVisibility="Hidden" />
27: </DockPanel>
28: </ControlTemplate>
29: </Setter.Value>
30: </Setter>
31: </Style>
32: </ResourceDictionary>
3. Add the ThemeInfo Attribute to AssemblyInfo
The final part is to publicise the fact that your assembly contains control-specific resources, which you do by using the ThemeInfo attribute. The GenericDictionaryLocation property defines where the generic resources are and the ThemeDictionaryLocation property defines where the themed resources are. The value for both properties is a ResourceDictionaryLocation enumeration, which is None, SourceAssembly, or ExternalAssembly.
The following code example shows the ThemeInfo attribute for the class library project that contains the Wizard class described previously, which has no theme-specific resources, but does specify a generic resource.
1: [assembly: ThemeInfo(ResourceDictionaryLocation.None,
2: ResourceDictionaryLocation.SourceAssembly)]
Side Note
If your custom control has specific parts that are key to the operation of the control (such as the buttons and frame in the Wizard example described previously), then the convention is to name them using the “PART_” prefix in the control template. For each required part in your template, you should add a TemplatePart attribute to the class definition as shown in the following code example.
1: [TemplatePart(Name = "PART_Frame", Type = typeof(Frame))]
2: [TemplatePart(Name = "PART_Back", Type = typeof(Button))]
3: [TemplatePart(Name = "PART_Cancel", Type = typeof(Button))]
4: [TemplatePart(Name = "PART_Finish", Type = typeof(Button))]
5: [TemplatePart(Name = "PART_Next", Type = typeof(Button))]
6: public class Wizard : Window
You should get a reference to these named template parts in the OnApplyTemplate override of your class because this is the point where the control template has actually been applied. It is common not to specifically handle missing template parts so that an exception (typically a NullReferenceException) gets raised at runtime, which clearly indicates that something is broken.
The following code example shows the OnApplyTemplate override for the Wizard class described previously.
1: public override void OnApplyTemplate()
2: {
3: base.OnApplyTemplate();
4:
5: // Get the template parts.
6: this.navigationFrame = this.GetTemplateChild("PART_Frame") as Frame;
7: this.back = this.GetTemplateChild("PART_Back") as Button;
8: this.cancel = this.GetTemplateChild("PART_Cancel") as Button;
9: this.finish = this.GetTemplateChild("PART_Finish") as Button;
10: this.next = this.GetTemplateChild("PART_Next") as Button;
11: }
For more information about designing lookless controls, see Guidelines for Designing Stylable Controls on the MSDN web site.
Appeal for IT Professional
Thursday, 26 March 2009In a departure from my usual posts (if you can call not posting for nearly six months "usual"!), I'd like to make an appeal on behalf of my wife's company for someone with IT infrastructure/system adminstration experience who would be willing to help them out. The specification for the work is as follows:
We are a cutting edge Technology Company based on the Cambridge Science Park. We have been at this facility for five years in October 2009 and now require a business analysis to be carried out on our existing business processes and information systems.
We are therefore seeking an Independent IT Professional who is NOT tied to an agency or IT company. This is strictly for an independent review of our business systems to enable us to go out to the market for quotes for the necessary work to be carried out.
We will be looking for recommendations on processes and information system improvements to carry us through the next five years. Presently our IT is out-sourced to a local company and aided internally by our Office Administrator with the Direction of our Director of HR & Administration who has responsibility for this area of the business.
Project Detail
- To conduct a business analysis with input from key people within the business
- Recording and analysis of interviews with key staff
- Analysis of the use of existing business process and systems
- An appropriate costed solution to meet the long term requirements and business challenges of the organisation
- A GAP analysis
- A proposal for the solution to meet future business needs
If this mini-project is of interest to you, please contact: Julie Lakin, Director of HR & Administration at Julie.lakin@SmartHolograms.com or by telephone on 01223 393400.
Migrating a Legacy WPF Application to Prism - Part 2
Monday, 6 October 2008 .net , c# , CodeProject , composite , Microsoft , Prism , refactor , screen shot , UI , WPF , XAML
After my initial successes in moving to a Prism-style bootstrapper in Part 1 it was time to move onto the next step. But in the case of a legacy application, what's the next step?
Application Structure Overview
Our legacy application consists primarily of a WPF user interface, a WCF service that provides access to a SQL Server database and a few support libraries. One of those support libraries provides some classes that deal with the interaction between the user interface and the WCF service and is my prime candidate for making some improvements using the Prism guidance.
There are two main entities, one that relates to a Project that is handled by the application and one that relates to a Pages, which make up the contents of a Project. So these seemed to me to be good choices for modules. But, the existing infrastructure merges the roles of Service and ViewModel into a single class, which I'd like to separate.
So, my plan (so far at least) is to create Prism Modules for the Project and Page and to create the relevant Views, ViewModels, Services, and Controllers that are required to implement the functionality. Rather than having to fully re-write the underlying code that handles UI/WCF Service interaction, I'm planning on writing a Prism-facade that will simply call out to the existing infrastructure, which I can eventually replace as time progresses. This gives me the testability advantages of Prism without having to rip out the heart of the application and re-write it completely, which would be a costly and time consuming task.
Getting Started
For my next step I'm focusing on the Project aspects of the application, so (as you can see in the screen shot above) I've created a Project module containing the ProjectModule, a ProjectService, ProjectTreeView, ProjectTreeViewModel and a ProjectController, which will handle the interactions between the different views of a project (when I get that far).
Each of these classes implements an interface, which enables me to easily mock these implementations to test specific aspects of the application. At this stage my implementations are very simple in that they just initialise the relevant Views, ViewModels, etc. The View itself is simply a UserControl that contains a root Grid element with a pink background. The important thing is to get it loaded and displayed first.
Implementation Specifics
In the application's shell, the existing user control was removed and replaced with the following region definition.
<ContentControl x:Name="_navigation" cal:RegionManager.RegionName="{x:Static inf:RegionNames.Navigation}" DockPanel.Dock="Left" />
The "cal" XML namespace resolves to "http://www.codeplex.com/CompositeWPF" and the "inf" XML namespace resolves to an infrastructure assembly that contains constants for the region names.
The ProjectController class retrieves the relevant region that is the destination of the View and adds the View for the associated ViewModel to that region and then activates it as shown in the following code example.
public void Run() { IRegion region = this._regionManager.Regions[RegionNames.Navigation]; region.Add(this._projectTreeViewModel.View); region.Activate(this._projectTreeViewModel.View); }
The call to the Activate method is essential if you want to be able to see your View. Until the Activate method is called, you will not be able to see your View.
The Run method of the ProjectController is called in the Initialize method of the ProjectModule after registering the required views and services, as shown in the following code example.
#region Private Methods private void RegisterViewsAndServices() { // NOTE: The ContainerControlledLifetimeManager ensures that the same // instance is returned by any call to Resolve or ResolveAll. this._container.RegisterType<IProjectService, ProjectService>(new ContainerControlledLifetimeManager()); this._container.RegisterType<IProjectController, ProjectController>(new ContainerControlledLifetimeManager()); this._container.RegisterType<IProjectTreeView, ProjectTreeView>(); this._container.RegisterType<IProjectTreeViewModel, ProjectTreeViewModel>(); } #endregion Private Methods #region IModule Members public void Initialize() { RegisterViewsAndServices(); IProjectController controller = this._container.Resolve<IProjectController>(); controller.Run(); } #endregion
As you can see in the screen shot above, apart from writing all the tests, putting some simple content in the ViewModel, a bit of work on the infrastructure and doing a lot of head scratching, that's it! Granted it's just a pink rectangle at the moment, but I now have a View that is loaded into the right place.
My next step is to put some useful UI code into the View and try to gradually incorporate some of the visual functionality of it's predecessor.Migrating a Legacy WPF Application to Prism - Part 1
Thursday, 2 October 2008 .net , c# , CodeProject , composite , Microsoft , Prism , refactor , UI , Visual Studio , WPF , XAMLThis is the first of several posts on my journey migrating a legacy application to use the Composite Application Guidance for WPF, which (like Brian Noyes) I prefer to call Prism, mainly because it's shorter, but it's also cooler!
I've watched the "Brian Noyes on Prism" dnr TV episode intently, read through the guidance documentation, I've done the hands-on lab, all of the quick starts and dissected and analysed the StockTraderRI sample application, so now I figure I'm ready to start cranking some Prism code for our legacy application1.
1By "legacy" I mean that it's a regular WPF application that was not created using the Prism (or any other specific) guidance, so it's not a green field project.
Understanding the Terminology
I don't pretend to be completely clued up on all aspects of design patterns, but I'm aware of the key concepts behind Model-View-Controller (MVC) and it's numerous variants, so when I opened up the StockTraderRI sample application I was initially a bit confused to see a combination of Presenters from Model-View-Presenter (MVP; also referred to as Supervising Controller in the guidance), Controllers from MVC, and PresentationModels from Presentation Model, or Model-View-ViewModel (MVVM) to give it its more WPF-focused name.
After a bit of digging it was clear that the Prism team were using a mixture of approaches and for good reason (of course), which I thought I'd just quickly explain for my own recording purposes if nothing else.
- Model: The underlying business entities that represent the data in your system (pretty much standard throughout all of the MV* patterns). E.g.
StockTraderRI.Infrastructure.Models.NewsArticleclass in the StockTraderRI sample. - View: The user interface representation of the underlying Model (also pretty much standard throughout all of the MV* patterns). E.g.
StockTraderRI.Modules.News.Article.ArticleViewclass in the StockTraderRI sample. - Presenter: When the Model can provide the data that the View requires without any complex manipulation or transformation, then the View binds directly to the Model and the Presenter handles the state of the View in the cases where complex user interface logic is required, such as changing colours of controls and showing/hiding controls. E.g.
StockTraderRI.Modules.News.Article.NewsReaderPresenterclass in the StockTraderRI sample. TheNewsReaderView binds directly to properties of theNewsArticleModel class, which is supplied by theNewsReaderPresenter. - PresentationModel: When the View cannot bind directly to the Model, a PresentationModel class is used to provide the Model in a format more easily consumed by the View acting as a façade. The PresentationModel class also manages UI-specific state and behaviour. E.g.
StockTraderRI.Modules.News.Article.ArticlePresentationModelclass in the StockTraderRI sample. TheArticleViewclass binds Articles property exposed by theArticlePresentationModelclass. Note: Given that MVVM is a WPF-specific variation of Martin Fowler's Presentation Model pattern, I prefer the term ViewModel to PresentationModel; if nothing else it's shorter :) - Controller: The StockTraderRI sample application uses Controller classes to manage the interaction between multiple views. E.g.
StockTraderRI.Modules.News.Controllers.NewsControllerclass, which manages interactions between theArticleViewand theNewsReaderView via theArticlePresentationModelandNewsReaderPresenter.
There is a discussion about the patterns used in Prism (and hence the sample application) in the guidance documentation, but the above helped to put things straight in my mind.
Getting Started
The first thing I did to get me started down the road to a Prism-based application was to implement the Bootstrapper, which I did as follows:
- Added references to the following Prism assemblies in my main (shell) project:
- Microsoft.Practices.Composite.dll
- Microsoft.Practices.Composite.Wpf.dll
- Microsoft.Practices.UnityExcetions.dll
- Microsoft.Practices.Unit.dll
- Microsoft.Practices.ObjectBuilder.dll
- Added a new class called
MyApplicationBootstrapper2, which inherits fromUnityBootstrapperas shown in the following code example.public class MyApplicationBootstrapper : UnityBootstrapper { protected override DependencyObject CreateShell() { MainWindow window = new MainWindow(); window.Show(); return window; } protected override IModuleEnumerator GetModuleEnumerator() { return new StaticModuleEnumerator(); }
} - Removed the
StartupUriproperty from App.xaml. - Created an instance of in the
Application.Startupevent handler as shown in the following code example.
private void Application_Startup(object sender, StartupEventArgs e) { MyApplicationBootstrapper bootstrapper = new MyApplicationBootstrapper(); bootstrapper.Run(); }
2 I've substituted "MyApplication" for the name of the actual application to protect the interests of my employer.
So far so good. The application runs as it did before but now it uses a Bootstrapper. Nothing dramatic and no big benefits yet, but it works. Next step, start writing some tests and a some modules to contain the existing UI controls as views in a more Prism-like approach.
Note: I welcome (encourage even) any corrections/comments/etc about the approach that I'm taking or on any of the detail that I've included in this post (and future posts).
Changing the Caret Colour in WPF
Tuesday, 16 September 2008 CodeProject , custom , WPF , XAMLIn WPF, you don’t have direct control over the colour of the caret in a TextBox or a RichTextBox. What the framework does is to use the inverse of the colour that you choose for the Background property. I’ve seen a few examples of styles that explicitly set the Background property to {x:Null} to completely remove the background from the TextBox, which enables you to use the same style when then the TextBox is used on top of different backgrounds that you want to show through.
Because the framework uses the inverse of the Background colour to set the colour of the caret, if you set the Background property to {x:Null}, then you will end up with the default black caret, which on a black background can be particularly difficult to see!
The Solution
The simple solution to this problem is to use actually specify a value for the Background property. In the case where you have a black background and want a white caret, then you can set the value of the Background property to #00000000, which is completely transparent black (if that makes sense!). The framework appears to ignore the opacity component of the colour so you end up with a transparent background and a white caret!
Taking it One Step Further
I haven’t tried this yet, but I assume that you can take this approach one step further to customise the colour of your caret by setting the Background to different colours with an opacity value of zero. This would then be a simpler solution that completely overriding the style as described by Lester in his Changing caret color in (Rich)TextBox post.
Determining the Relative Path in XLinq with Extension Methods
Monday, 21 July 2008 .net , c# , extension method , TitanIntroduction
In Titan, I’m edging closer and closer to releasing a beta 1 for public consumption and one of the tasks that I’m working on to get me there is a lazy-loading folder browser dialog box that enables you to browse through the folder structure of a particular service without having to load the whole folder structure in one go. This is used specifically for specifying new service configurations and is of particular concern for the local file system provider. Have you ever tried to retrieve your whole hard disk structure in one go!?
My service methods were already using some XLinq to parse the XML response from the Box.net service call and the local file system provider, but for the Box.net service provider I needed a way of finding a particular folder based on it’s relative path. The contents of a fictitious Box.net folder structure could look like the following XML example.
<folder id="0" name="" shared="0"> <tags></tags> <files></files> <folders> <folder id="4384" name="Incoming" shared="0"> <tags> <tag id="34" /> </tags> <files> <file id="68736" file_name="cows.w3g" keyword="" shared="0" size="232386" created="1129537520" updated="1129537520"> <tags> </tags> </file> <file id="68737" file_name="silver.html" keyword="" shared="0" size="15805" created="1129537520" updated="1129537520"> <tags> <tag id="35" /> </tags> </file> </files> <folders> <folder id="1234" name="Test" shared="0"> <folders> <folder id="2345" name="wibble" shared="0"> <folders /> </folder> <folder id="5432" name="wobble" shared="0"> <folders /> </folder> </folders> </folder> <folder id="4321" name="Test2" shared="0"> <folders /> </folder> </folders> </folder> </folders> </folder>
The Problem
So, if I want to retrieve the folder contents of the folder named Test, which has a relative path of “\Incoming\Test”, how do I find that in XLinq? Well, I looked at all kinds of different approaches using the Descendants and Ancestors methods, but none of them managed to produce the goods, because I needed a single string that I could compare against.
The Solution
To solve the problem I decided to use an extension method to produce a single string from a collection. The results of an XLinq query are IEnumerable<T>, where T depends on the exact nature of the query. In my case, I was pulling out the name attribute, so I had IEnumerable<string>. The extension method shown in the following code example uses a StringBuilder to build up the single string.
/// <summary> /// Converts the IEnumerable<string> to a single string. /// </summary> /// <param name="source">The source IEnumerable<string>.</param> /// <returns>A single string that represents the contents of the IEnumberable<string></returns> public static string ToOneString(this IEnumerable<string> source) { StringBuilder builder = new StringBuilder(); // Add each item. foreach (string item in source) { if (item.Length > 0) { builder.Append(item); } } return builder.ToString(); }
This didn’t quite solve the problem, because for my scenario described above, this produces a single string of “IncomingTest”, which isn’t quite a relative path. So, I added an overload for the method that accepts a string separator to produce the following.
/// <summary> /// Converts the IEnumerable<string> to a single string. /// </summary> /// <param name="source">The source IEnumerable<string>.</param> /// <returns>A single string that represents the contents of the IEnumberable<string></returns> public static string ToOneString(this IEnumerable<string> source) { return source.ToOneString(string.Empty); } /// <summary> /// Converts the IEnumerable<string> to a single string. /// </summary> /// <param name="source">The source IEnumerable<string>.</param> /// <param name="separator">A separator to be placed between each item in the string.</param> /// <returns>A single string that represents the contents of the IEnumberable<string></returns> public static string ToOneString(this IEnumerable<string> source, string separator) { StringBuilder builder = new StringBuilder(); // Add each item and the separator. foreach (string item in source) { if (item.Length > 0) { builder.Append(item); builder.Append(separator); } } // Remove the final separator. if (builder.Length >= separator.Length) { builder.Remove(builder.Length - separator.Length, separator.Length); } return builder.ToString(); }
In my reusable library, I’ve also created a version of ToOneString for IEnumerable<T>, which simply appends item.ToString() to the StringBuilder.
And Finally
So, my final XLinq query that builds up the relative path and returns me the folders for a specific relative path looks like the following code example, where relativePath is a string parameter to the method that specifies the relative path to the folder that I want to retrieve the child folders.
var query = (from d in xRoot.DescendantsAndSelf("folder") where "\\" + (from a in d.AncestorsAndSelf("folder") select a.Attribute("name").Value).Reverse().ToOneString("\\") == relativePath select d.Element("folders").Elements("folder")).First();
If there are any folders in the specified folder, then I can enumerate the resulting XElement collection and build up instances of my File class.