Click here to Skip to main content
15,946,342 members
Articles / Desktop Programming / WPF
Article

Windows 7 / VS2010 demo app

Rate me:
Please Sign up or sign in to vote.
4.81/5 (89 votes)
23 Nov 2009CPOL22 min read 304.2K   13.6K   200   72
Using Windows7 / VS2010 and some of the new bits such as TaskBar/JumpList and Drag & Drop

Instructions : due to the size of the zip, I had to zip each project seperately. So please read instructions below carefully

  1. Create a new folder on your desktop called MefFX, and unzip the MefFX Solution.zip file to it.
  2. Unzip Lib.zip; & MEFExports.zip & MefFX.zip & PixelShaders.zip (which are the Lib solution folder and the three project files) to the the place you unzipped MefFX Solution.zip to.
  3. If there are missing references (which there may be as I cleaned the apps before zipping) you will have those in the Lib.zip file

Sorry about this, I just wanted to get it up here, so I had to split the files. Hopefully you can follow all this.

Contents

Introduction

I recently upgraded both my home PC and laptop to Windows7 from Vista, and I have to say I am so glad I did that. Not only did I manage to clean out a load of crap that I did not need on my machines, but I got rid of Vista, yey.

So since I have now got Windows7, I have been messing around with it, kind of getting the lay of the land so to speak, and seeing what may be possible to use in my own apps, you know things like the TaskBar/JumpLists

Image 1

As it also turns out another of my favourite past times www.codeproject.com also seem to want people to write about Windows7 / VS2010 technology, so I thought I would give it a crack, and write a small article that show cases some of the Windows7 features and also shows how these are natively supported by VS2010. I also wanted to write an article that used a few things I have not tried before, so what I decided to do was write an application that does the following:

  • Is written for Windows7 and VS2010, as such both of these are unfortunately pre-requisites (sorry about that)
  • Shows how to use the new Windows 7 TaskBar and Jumplist
  • Shows how to do drag and drop the Windows7 way
  • Uses the new Dynamic type in .NET 4.0
  • Uses the new Expando type in .NET 4.0
  • Uses the Managed Extensibility Framework (MEF), which I think will become part of the mainstream .NET runtime very soon.
  • Uses pixel shaders which are exported from one project and imported into the main project using MEF
  • Uses a true WPF WebBrowser which natively supports complex transforms and can be hosted on a 3D mesh, which if you have ever tried to use the standard .NET 3.5 SP1 WebBrowser control, know is just not possible

Pre-Requisites

As I stated already this article was written for Windows 7 and uses VS2010, so you WILL need both of these to run the code.

What Does It Do

I think the introduction said most of it, but I'll just over it once more, so there is no confusion about what this article does/doesn't do. In a nutshell what this articles code does is to use the Managed Extensibility Framework (MEF) to search several DLLs which reside in a particluar path (the obj\debug folder of the main application in this case) for any parts that have been marked as MEF exportable. These parts are then included as viable MEF imports into the main application. The code in the MEF imported parts is then fair game to be called from the main interface (the consumer of the MEF imported parts).

The MEF import parts are pretty simple, they simply wrap a particular pixel shader effect and provide some additional metadata about the shader, such as who made it, where is was downloaded from, it's original URL etc etc. Though the metadata is exposed using the new dynamic and ExpandoObject types of .NET 4.0

These imported pixel shaders are then used to create some ViewModels in the main interface which are then used to bind against ItemControl(s) which present the MEF'd in add-ins. The user may then click on the items in the ItemControl(s) and then a 3D panel will be shown that shows off the pixel shader that the Item within the ItemsControl is attempting to represent. The user may also flip the 3D panel to reveal more information about their chosen pixel shader.

Of course what I also decided to do was to showcase some of the new Windows7 / VS2010 goodness, so you are able to pick what picture the MEF'd in pixel shaders use using some new Windows 7 shell Drag&Drop code, and I also show how you can integrate your applications with the Windows7 TaskBar and how to interact with your own application using the Windows7 TaskBar. Additionally there is also some Windows7 JumpList stuff in there for good measure.

What I should point out is that my preferred UI technology is WPF, so the demo code uses WPF, but the techniques here-in could be used in Winforms.

What Does It Look Like

I actually think it looks pretty sweet, quite simple really, it starts out like this, where is has found all the MEF'd in pixel shaders, but does not know what to do yet, as it is waiting for you to supply an image.

Image 2

So you can now supply an image, by dragging one of your favourite images to the app, which will make it look as follows:

Image 3

So once you have picked an image, you can either examine some more about the pixel shader using the bottom, or the left hand side navigation methods, both of which are fully scrollable areas.

You can open an image which has had a pixel shader applied to it from either the left hand ItemsControl or by using the bottom ItemsControl which is actually an instance of Paul Talletts excellent FishEyePanel which you can find over at his orginal www.codeproject.com article link Panels.aspx

The example below shows what the demo app looks like when I double click on one of the pixel shader affected image items within the FishEyePanel.

Image 4

From here you can also flip this popup window over in 3D which is simliar to something that I did in an older article of mine called MyFriends where I had 2 user controls hosted on 3D meshes that could be flipped in 3D space. This was quite a ball ache to write the 1st time, luckily for all of us, Josh Smith revamped this and wrapped it all up in a nice WPF content control which is part of his Thriple 3D WPF Library. Nice one agent Smith.

You can see that in action right here, where I am 1/2 way though flipping the users chosen pixel shader item. What I am actually doing here is showing a web browser (note this is not the winforms one or the equally rubbish .NET 3.5 SP1 WebBrowser control, both of which suck and are in my opinion far from being 1st class WPF citizens) which is capable of being hosted and flipped through 3D space on a 3D mesh.

Image 5

Here is the WebBrowser control showing the details for the users selected pixel shader item.

Image 6

The other area of interest is the Windows7 TaskBar area, which looks like this

Image 7

You can see from the figure above that there are 2 custom Windows7 TaskBar buttons, you will learn more about this shortly. I have to say one thing I am mildly disappointed with here, is the quality of the images used for the button is extremely poor, which is a shame as the original images are actually very good quality and do not exhibit this bad rendering.

The thing I do like about the TaskBar is that I can trigger ICommands directly in my ViewModel which is really ace. Again I will show you this later, but for now just so you know I am not lying here is what is shown when I click the information button is the demo apps TaskBar entry.

Image 8

This is an About window which is shown as the result of running an ICommand directly in my ViewModel.

Another interesting point is that if you right click the TaskBar area in the attached demo app, you will see some JumpList entries. We can actually add custom program launches to the Windows7 JumpList, again I will go through this later.

Image 9

So I think that concludes how it looks, so I guess you now want to know how it works, which is fine by me, we can look into that right now.

How It Works

In the next couple of sub sections I will talk you through how all the bits and peices of the demo app work. Before we carry on let me just state that some of the sections you will read next may rely on a Microsoft Dll or 2 which have been released as the Microsoft.WindowsAPICodePack. The Microsoft.WindowsAPICodePack is really just a manged code Dll that targets Windows7 and has all the P/Invoke stuff nicely handled for you. It has things like shell operations/thumbnails/drag & drop/jumpLists and the new Windows7 taskbar.

I will specifically mention where I am using the Microsoft.WindowsAPICodePack, and where I am not, as some of the functions can be done quite easly using .NET 4.0 code without the need to use the Microsoft.WindowsAPICodePack.

Pixel Shaders

Now I am not saying I know loads about pixel shaders, but wikipedia says this:

A pixel shader is a computation kernel function that computes color and other attributes of each pixel. Pixel shaders range from always outputting the same color, to applying a lighting value, to doing bump mapping, shadows, specular highlights, translucency and other phenomena. They can alter the depth of the pixel (for Z-buffering), or output more than one color if multiple render targets are active. A pixel shader alone cannot produce very complex effects, because it operates only on a single pixel, without knowledge of a scene's geometry or of neighboring pixels.

So how does that impact on .NET? Well what Microsoft have done is to allow users to create their own Pixel Shaders using a high level language called the High Level Shader Language (HLSL)

The High Level Shader Language or High Level Shading Language (HLSL) is a proprietary shading language developed by Microsoft for use with the Microsoft Direct3D API. It is analogous to the GLSL shading language used with the OpenGL standard. It is very similar to the NVIDIA Cg shading language, as it was developed alongside it.[1]

HLSL programs come in three forms, vertex shaders, geometry shaders, and pixel (or fragment) shaders. A vertex shader is executed for each vertex that is submitted by the application, and is primarily responsible for transforming the vertex from object space to view space, generating texture coordinates, and calculating lighting coefficients such as the vertex's tangent, binormal and normal vectors. When a group of vertices (normally 3, to form a triangle) come through the vertex shader, their output position is interpolated to form pixels within its area; this process is known as rasterisation. Each of these pixels comes through the pixel shader, whereby the resultant screen colour is calculated.

Optionally, an application using a Direct3D10 interface and Direct3D10 hardware may also specify a geometry shader. This shader takes as its input the three vertices of a triangle and uses this data to generate (or tessellate) additional triangles, which are each then sent to the rasterizer.

How this translated into .NET code is something like this:

We have a FX file (the HLSL part)

C#
sampler2D input : register(s0); 
float someInput : register(c0); 

float4 main(float2 uv : TEXCOORD) : COLOR { 
	float4 color;
	uv.x = uv.x + cos((uv.x-someInput)*50)*0.02;
	uv.y = uv.y + sin((uv.y-someInput)*50)*0.02;
	color = tex2D(input, uv.xy); 
	
	// uncomment the line below to invert the red color
	//color.r = -color.r; 
	return color;    
}

And then we have to use the fxc compiler who knows what to do with this, where we can do something like this

C#
:: use /Gec option for compatibility mode (required by some .fx files)
echo ******* Compiling Pixel Shaders *******
"%DXSDK_DIR%\Utilities\bin\x86\fxc" /T ps_2_0 /E main /Fo
    "%1\MyEffect1\MyEffect1.ps" "%1\MyEffect1\MyEffect1.fx"
echo ******* Compiling Pixel Shaders Completed *******

And we can then have a managed C# or VB .NET wrapper around the raw pixel shader, which may be something like this:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Effects;
using System.Windows.Media;
using System.Windows;

namespace PixelShaders {
    public class MyEffect1 : ShaderEffect 
    {
        /// <summary>
        /// Pixel shader that this effect is using
        /// </summary>
        private static PixelShader _shader = new PixelShader() 
        { 
            UriSource = 
            Utilities.GetResourcePackUri("MyEffect1/MyEffect1.ps") 
        };

        public MyEffect1() 
        {
            PixelShader = _shader;

            // remember to call UpdateShaderValue() for all shader input
            // arguments here
            UpdateShaderValue(InputProperty);
            UpdateShaderValue(SomeInputProperty);
        }

        /// <summary>
        /// The default shader input - the visual on which the shader operates
        /// Note: you can add more texture inputs in a similar way as Input is added
        /// </summary>
        public Brush Input 
       {
            get { return (Brush)GetValue(InputProperty); }
            set { SetValue(InputProperty, value); }
        }

        /// <summary>
        /// The WPF dependency property that backs up the shader input,
        /// assigned to sampler register s0
        /// </summary>
        public static readonly DependencyProperty InputProperty = 
            ShaderEffect.RegisterPixelShaderSamplerProperty("Input",
            typeof(MyEffect1), 0);

        /// <summary>
        /// Some other input property that our shader will use
        /// </summary>
        public double SomeInput 
        {
            get { return (float)GetValue(SomeInputProperty); }
            set { SetValue(SomeInputProperty, value); }
        }

        /// <summary>
        /// Using Dependency property to store SomeInput in order to enable binding,
        /// animation and other
        /// use PixelShaderConstantCallback() to assing to a shader register -
        //// in this case it's register c0
        /// </summary>
        public static readonly DependencyProperty SomeInputProperty =
            DependencyProperty.Register("SomeInput", typeof(double), typeof(MyEffect1),
            new UIPropertyMetadata(0.0, PixelShaderConstantCallback(0)));
    }
}

There is a nice example of a Visual Studio template for Pixel Shader development over at http://www.nokola.com/sources/ShaderEffectTemplate.zip and there is also a great resource at http://wpffx.codeplex.com which also has a custom VS MSBuild task to ease the development of creating custom pixel shaders.

I should also point out the work of Walt Ritscher and his amazing Shazzam tool http://blog.wpfwonderland.com/2008/10/08/shazzam-wpf-pixel-shader-effect-testing-tool-now-available/

So once you have created your first pixel shader you are free to use them in your XAML such as the following

C#
<Image>
  <Image.Effect>
	<DropShadowEffect/>
  </Image.Effect>
</Image>

WPF only really comes with a couple of pre built Effects, such as DropShadowEffect. The Effects are not to be mistaken for the older BitmapEffects which are not hardware accelerated.

Exporting/Importing Via MEF

Writing extensable application is all the rage and loads of apps now support add-ins. Now for those in the know, what this ultimatley boils down to is implementing a particular interface, say some hypothetical IAddIn interface where one might use some reflection to search for all Types in all assemblies found in a particular directory and if you find a Type that implements said IAddIn interface import that Type into our app as an available addin. Easy enough.

There are also some formal addin frameworks, one came out with .NET 3.5 SP1, which uses the System.AddIn namespace which I wrote about some time ago in my http://www.codeproject.com/KB/dotnet/AddInModel.aspx article. Using the System.AddIn namespace is no mean feat, and there are at least 7 projects required which is shown in this figure.

Image 10

Now at the time I wrote about using the System.AddIn namespace, I could no help but think there was a little too much infrastructure required to do what is essentially what I stated above. Where we are looking for a Type that implements a particular interface and imports that Type into our app as an available addin.

Luckily help is at hand, you can use Managed Extensibility Framework (MEF) which makes the process almost as easy as the introductory sentence I wrote above. When UI first looked at MEF I thought I may not like it, as I was still soured after looking at the Managed Addin Framework (MAF using System.AddIn namespace), but I have to say I have been delighted with just how easy it is to use MEF. It really is easy.

We will go through how in just a minute, I just want to take a little time to explain how I am using MEF in the demo app, before I show you the code that does it all.

The basic idea behind this app is that I wanted to look into a few areas that I had not looked at before, and I like shiny pretty things so I immediately thought of pixel shaders, which we just went through. So how does the demo app work and what does it do again?

Well quite simply the demo app imports pixel shaders from 2 other projects PixelShaders and MEFExports, and any pixel shaders found in these two assemblies are imported into the main app MefFX using and MEF. Where for each IPixelShader (MEF part) found a new ShaderViewModel is created within the main app MefFX, which is then used to drive the main apps UI. We are slighltly jumping the gun here, but in essence what happens next is that for each ShaderViewModel there will be an image shown which has the Effect applied from the ShaderViewModel (which in turn came from the MEF import parts). The ShaderViewModel also contains some dynamic data which we will cover soon, which is part of the data that is imported for each IPixelShader MEF import parts.

This will make more sense when you see some code I guess.

Declaring Exportable Parts In MEF

To declare that you would like something to be exportable (ie used within some other code somewhere) you simply need to design a contract interface and have some concrete implementation of this interface and then mark up the concrete implementation with the MEF ExportAttribute. This is shown below.

The Contract Interface

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Media.Effects;

namespace MEFExports
{
    public interface IPixelShader
    {
        Effect ActualEffect { get; }
        dynamic ShaderDetails { get; }
    }
}

And here is the concrete class that implements the contract interface. As I stated earlier, the objective was to expose pixel shader effects and some additional data about the shader as a MEFable export. That is what the code below does

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


//MEF
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Windows.Media.Effects;
using System.Dynamic;

//PixelShaders
using PixelShaders;

//have a look at http://wpffx.codeplex.com/
using ShaderEffectLibrary; 


namespace MEFExports
{
    [Export(typeof(IPixelShader))]
    public class ColorKeyAlphaEffectPixelShader : IPixelShader
    {
        public Effect ActualEffect
        {
            get { return new ColorKeyAlphaEffect(); }
        }

        public dynamic ShaderDetails
        {
            get
            {
                dynamic shaderDetails = new ExpandoObject();
                shaderDetails.ShaderName = "ColorKeyAlphaEffect";
                shaderDetails.Description = "Color key alpha effect";
                shaderDetails.Attributes = new ExpandoObject();
                shaderDetails.Attributes.Author = "Microsoft";
                shaderDetails.Attributes.Url = "http://wpffx.codeplex.com/";
                return shaderDetails;
            }
        }
    }
}

Declaring Importable Parts In MEF

The next peice of the MEF puzzle is getting the Exportable MEFable parts to be imported into some other bit of code. That is also failry trivial all we do is mark up some property like this, where we can use the MEF ImportManyAttribute, where we say we want to Import a number of parts. These will be all the individual code parts that implement the original IPixelShader contract and are marked up with the MEF ExportAttribute.

C#
[ImportMany(RequiredCreationPolicy = CreationPolicy.Shared)]
public IEnumerable<IPixelShader> Shaders { get; set; }

It can be seen that we are expecting to Import a few items (one for each pixel shader effect) so we can use the MEF ImportManyAttribute

What actaully happens in the attached demo app is that these MEFable parts are used to create a MainWindowViewModel which is the DataContext for the MainWindow.

C#
public MainWindow()
{

    App.ComposeMEFContainer(this);
    mainWindowViewModel = new MainWindowViewModel(Shaders);
    this.DataContext = mainWindowViewModel;
    InitializeComponent();
}

Getting It All Wired Up

The Import code above only works thanks to a static method on the App class in the demo code, whos job it is to get the MEF container to configure. Let's have a look at that now:

C#
public static void ComposeMEFContainer(Object part)
{
    var directory = System.IO.Path.GetDirectoryName(
        Assembly.GetExecutingAssembly().Location);
    var container = new CompositionContainer(new DirectoryCatalog(directory));
    var batch = new CompositionBatch();
    batch.AddPart(part);
    container.Compose(batch);
}

And that is all there is to it. The nice thing about MEF is that it really is this easy to use.

Dynamic/ExpandoObject Type In .NET 4.0

The ExpandoObject represents an object whose members can be dynamically added and removed at run time. This comes with .NET 4.0 dynamic API and allows use to build complex object structures, where before we would have had to have created concrete classes at compile time.

Using an example or 2 from MSDN

This first example shows how you can eadily build up complex object structures

C#
class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // The following statement causes an exception
        // if you pass the employee object.
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.

This this example shows how you can easily add new methods to dynamic objects

C#
sampleObject.number = 10;
sampleObject.Increment = (Action)(() => { sampleObject.number++; });

// Before calling the Increment method.
Console.WriteLine(sampleObject.number);

sampleObject.Increment();

// After calling the Increment method.
Console.WriteLine(sampleObject.number);
// This code example produces the following output:
// 10
// 11

The attached demo app simply uses ExpandoObject to represent information about the pixel shaders, such as who the author was, its name, where to get more information about it. As we previously saw MEF was used to resolve the public IEnumerable<IPixelShader> Shaders { get; set; } property in the MainWindow where if we examine what is MEF'd in, we can see the following use of dynamic/ExpandoObject.

C#
public dynamic ShaderDetails
{
    get
    {
        dynamic shaderDetails = new ExpandoObject();
        shaderDetails.ShaderName = "ColorToneEffect";
        shaderDetails.Description = "Color tone effect";
        shaderDetails.Attributes = new ExpandoObject();
        shaderDetails.Attributes.Author = "Microsoft";
        shaderDetails.Attributes.Url = "http://wpffx.codeplex.com/";
        return shaderDetails;
    }
}

At first you may think this would be no good to use for WPF. But the WPF team has already enabled us to bind to dynamic perfectly easily, as ExpandoObject already implements all you need to bind to. And that is the most excellent INotifyPropertyChanged interface.

Image 11

So all we need to do, to bind to an Expando is as follows, easy right.

XML
<Image.ToolTip>
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <Label Content="Shader Name" Style="{StaticResource boldLabel}"/>
            <Label Content="{Binding Path=ShaderDetails.ShaderName}" 
                   Style="{StaticResource normalLabel}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="Shader Description" Style="{StaticResource boldLabel}"/>
            <Label Content="{Binding Path=ShaderDetails.Description}" 
                   Style="{StaticResource normalLabel}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="Shader Author" Style="{StaticResource boldLabel}"/>
            <Label Content="{Binding Path=ShaderDetails.Attributes.Author}" 
                   Style="{StaticResource normalLabel}"/>
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label Content="Shader Url" Style="{StaticResource boldLabel}"/>
            <Label Content="{Binding Path=ShaderDetails.Attributes.Url}" 
                   Style="{StaticResource normalLabel}"/>
        </StackPanel>
    </StackPanel>

</Image.ToolTip>

Image 12

Drag & Drop The Windows7 Way

Now most of us have probably done Drag & Drop in .NET using the DragEffects and DataObject etc etc. Well it seems that when using the Microsoft.WindowsAPICodePack things are a little different from normal. For example here is the standard Drag & Drop code that one might have done in the past to allow an image to be dragged to a PictureBox (this is Winforms code where as the attached app is WPF, but the idea is the same)

C#
public Form1()
{
	InitializeComponent();
        Image img = Image.FromFile(@"C:\pics\1.jpg");
        this.btnImage.Image = img;
        this.picBox.AllowDrop = true;
        this.btnImage.MouseDown += this. btnImage_MouseDown;
        this.picBox.DragDrop += this.pictureBox_DragDrop;
        this.picBox.DragEnter += this.pictureBox_DragEnter;
}



private void btnImage_MouseDown(object sender, MouseEventArgs e)
{
        Button btnPic = (Button)sender;
        btnPic.DoDragDrop(btnPic.Image, DragDropEffects.Copy);
}
 
private void pictureBox_DragEnter(object sender, DragEventArgs e)
{
	if (e.Data.GetDataPresent(DataFormats.Bitmap))
	{
        	e.Effect = DragDropEffects.Copy;
        }
        else
        {
                e.Effect = DragDropEffects.None;
        }
}

private void pictureBox_DragDrop(object sender, DragEventArgs e)
{
	PictureBox picbox = (PictureBox)sender;
        Graphics g = picbox.CreateGraphics();
        g.DrawImage((Image)e.Data.GetData(DataFormats.Bitmap), new Point(0, 0));
}

Which is all well and cool, I quite liked this way of working with Drag & Drop.

The demo app does not use the standard .NET Drag & Drop rather I have chosen to see how this is done using the Microsoft.WindowsAPICodePack, so let's have a look how this sort of thing is done using the Microsoft.WindowsAPICodePack.

The following code shows how to allow the user to Drag & Drop a single image from their file system into a WPF control that has AllowDrop="true" on.

So in the XAML I simply have something like this:

XML
<Grid AllowDrop="True" Drop="GridDrop".../>

And using Microsoft.WindowsAPICodePack, I support Drag & Drop onto this Grid by using the following code:

C#
private void GridDrop(object sender, DragEventArgs e)
{
    if (!inDragDrop)
    {
        string[] formats = e.Data.GetFormats();
        foreach (string format in formats)
        {
            // Shell items are passed using the "Shell IDList Array" format. 
            if (format == "Shell IDList Array")
            {
                ShellObject obj = ShellObjectCollection.FromDataObject(e.Data).First();
                txtImage.Visibility = Visibility.Collapsed;
                imgDrop.Source = obj.Thumbnail.BitmapSource;
                e.Handled = true;
                mainWindowViewModel.HasImages = true;
                return;
            }
        }
    }

    e.Handled = false;
}

OK so it is not so different from the Winforms code really, the thing to note is the ShellObjectCollection, which is a class in the Microsoft.WindowsAPICodePack, which has various properties that can be used to determine values about the Dragged objects shell information.

Windows7 TaskBar

The Windows7 TaskBar is a strange one, as it can be done using the Microsoft.WindowsAPICodePack, there is however native support for Windows7 TaskBars in .NET 4.0, using a new System.Windows.Shell namespace. I am not a complete moron, so when there is an inbuilt mechanism for doing something I will go for that every time over using a 3rd party DLL, even if the 3rd party DLL is a Microsoft one.

So I will be showing you how to create a TaskBar and how to interact with it, using the native .NET 4.0 coding methods. I should state though that I love WPF, so will be showing you this using WPF. If you are using WinForms the same rules will apply but you will obviously have no XAML.

It's quite easy and all goes of as follows:

We just declare a TaskBar in XAML as follows:

XML
<Window x:Class="MefFX.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MefFX" Height="600" Width="800">


    <Window.TaskbarItemInfo>
        <TaskbarItemInfo
            ProgressState="Normal" 
            Description="Opens the browser for a particular PixelShader inside MefFX"
            ThumbnailClipMargin="{Binding RelativeSource={RelativeSource FindAncestor, 
                AncestorType={x:Type Window}}, Path=BorderThickness}">
            <TaskbarItemInfo.ThumbButtonInfos>
                <ThumbButtonInfo
                   Click="SearchForPixelShadersInside_Click"
                    DismissWhenClicked="False"
                    ImageSource="Images/ie.png" />
                <ThumbButtonInfo Command="{Binding AboutCommand}"
                    DismissWhenClicked="False"
                    ImageSource="Images/about.png" />
            </TaskbarItemInfo.ThumbButtonInfos>
        </TaskbarItemInfo>


    </Window.TaskbarItemInfo>


....
....
....
....

</Window>

Image 13

Notice the two ThumbButtonInfo objects, one of which uses a Click, "SearchForPixelShadersInside_Click" which fires some code behind code, and the other uses a Command (Command="{Binding AboutCommand}") to bind to a ViewModel ICommand which allows you to fire code in your ViewModel when interacting with the TaskBar.

Here is the code behind for the Click, which simply shows the 3D panel, and flips it to show the WebBrowser control with the last viwed pixel shaders web url shown in the browser within the 3D panel.

C#
private void SearchForPixelShadersInside_Click(object sender, EventArgs e)
{
    if (thriplePanel.IsFrontInView)
    {
        ContentControl3D.RotateCommand.Execute(null, btnBack);
    }
    thriplePanel.DataContext = lastSelectedShaderVm;
    thriplePanel.Visibility = Visibility.Visible;

}

And just for completeness here is the ICommand that the 2nd ThumbButtonInfo calls inside of the MainWindowViewModel in the attached demo app.

C#
/// <summary>
/// Logic to determine if AboutCommand can execute
/// </summary>
private Boolean CanExecuteAboutCommand
{
    get
    {
        return true;
    }
}

/// <summary>
/// Executes the AboutCommand
/// </summary>
private void ExecuteAboutCommand()
{
    uiVisualizerService.ShowDialog("AboutWindow", null);
}

This ViewModel code uses my Cinch MVVM Framework to show custom dialogs using the IUIVisualizerService, which you can read more about using the Cinch MVVM Framework web site.

Windows7 JumpList And Interacting With It

As before a JumpList can either be done using the Microsoft.WindowsAPICodePack, or by using the new .NET 4.0 System.Windows.Shell namespace.

I have chosen to use the System.Windows.Shell namespace. So to create a JumpList we simply need to do the following.

C#
JumpList jumpList = new JumpList();
JumpList.SetJumpList(Application.Current, jumpList);

JumpTask jumpTask = new JumpTask();
jumpTask.Title = "IE";
jumpTask.CustomCategory = "Keep Notes";
jumpTask.ApplicationPath = @"C:\Program Files\Internet Explorer\iexplore.exe";
String systemFolder = Environment.GetFolderPath(Environment.SpecialFolder.System);
jumpTask.IconResourcePath = @"C:\Program Files\Internet Explorer\iexplore.exe";
jumpTask.IconResourceIndex = 0;
jumpTask.Arguments = "pixel shaders";
jumpList.JumpItems.Add(jumpTask);
jumpList.Apply();

So when you run the demo app, and right on its TaskBar icon, you will see the current JumpList with a new custom entry for the "Keep Notes" category shown in the code above. There will also be an IE link, that when clicked will show IE will have been passed the command line arguments "pixel shaders."

Image 14

Basically you can think of JumpLists as running external processes and TaskBar and ThumbButtonInfo as being able to interact with the current app.

A True WPF Browser

As I previously stated I am not the biggest fan of either of the available WebBrowser controls for WPF right now. The reason I am not that keen on them is that they are for my money, well let's not beat about the bush they are not WPF like enough, and are drawn using HWnd based control rendering, and not WPFs DirectX rendering engine. As a result they are always rectangluar, are always drawn on top and can not be used in LayoutTransforms or on 3D which is obviously not cool.

Luckily some clever fellas out there didn’t like this either and came up with some cool C++ DLL called Awesomium which you can get over at:

http://princeofcode.com/awesomium.php#download

And then to top that off Chris Cavanagh (Physics genius) wrapped it to make it WPF like. He calls this

WPF 3D Chromium Browser

Which you can find over at Chris Cavanaghs site using this URL

http://chriscavanagh.wordpress.com/2009/08/27/wpf-3d-chromium-browser/

So this code uses Chris Cavanaghs site Chromium Browser that supports 3D inside of WPF. So all I have to do is as follows:

  1. Reference the Cjc.AwesomiumWrapper.dll
  2. Reference the Cjc.ChromiumBrowser.dll
  3. Include the Awesomium.dll (the original C++ wrapper (non WPF)), and make sure this is copied to the output folder
  4. And then use the Browser in my code

I wanted to swap out the URL of the browser each time a new pixel shader item is clicked, so I create a ChromiumBrowser each time a new item is clicked, which is not the most efficent of code at all, but I think this has more to do with it being hosted in the way it is inside of Josh Smiths Thriple 3D WPF Library than the ChromiumBrowser. It worked fine for me using a single ChromiumBrowser and just navigating to new URLs. Hey ho, we are where we are, so what I do is NOT the most efficient, but it works for the sake of this demo app, and does show you that there is a WebBrowser out there that will work in 3D and supports WPF LayoutTransforms as seen here

Image 15

Here is the code to do this:

C#
private void CreateBrowser()
{
    if(thripleBackGrid.Children.Count == 3)
    {
        var oldBrowser =thripleBackGrid.Children[2];
        if (oldBrowser.GetType().Equals(typeof(Cjc.ChromiumBrowser.WebBrowser)))
            thripleBackGrid.Children.RemoveAt(2);
    }

    Cjc.ChromiumBrowser.WebBrowser newBrowser = new Cjc.ChromiumBrowser.WebBrowser();
    newBrowser.SetValue(Grid.RowProperty,1);
    newBrowser.Margin = new Thickness(5);
    newBrowser.EnableAsyncRendering = true;
    newBrowser.Width = 460;
    newBrowser.Height = 390;
    newBrowser.LayoutTransform = new ScaleTransform(0.5, 0.5, 0.5, 0.5);
    newBrowser.Ready += new EventHandler(newBrowser_Ready);
    thripleBackGrid.Children.Add(newBrowser);
}


private void newBrowser_Ready(object sender, EventArgs e)
{
    String url = lastSelectedShaderVm.ShaderDetails.Attributes.Url;
    try
    {
        WebClient fileReader = new WebClient();
        using (Stream data = fileReader.OpenRead(url))
        {
            String webText = new StreamReader(data).ReadToEnd();
            (sender as Cjc.ChromiumBrowser.WebBrowser).LoadHtml(webText);
        }
    }
    catch (WebException ex)
    {
        Console.WriteLine("Error accessing site " + ex.Message);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error accessing site " + ex.Message);
    }
    
    
}

I did have one issue with this and VS2010/.NET 4.0, it turns out that the ChromiumBrowser was built using a V2 compliant version of .NET, so I had to allow for this in the App.Config which you can see below where I use the useLegacyV2RuntimeActivationPolicy attribute.

XML
<?xml version="1.0"?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
    <supportedRuntime version="v4.0" 
	sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

That's It Hope You Liked It

Anyway there you go, hope you liked it. Even if you dont have a use for think there are still some bits and pieces you could use if and when you do start using Windwos7 / VS2010

Thanks.

As always votes / comments are welcome.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
Questionthanks Pin
gowithsnow8124-Jun-14 2:27
gowithsnow8124-Jun-14 2:27 
QuestionPesky JumpLists Pin
John Whitmire10-Dec-13 3:09
professionalJohn Whitmire10-Dec-13 3:09 
AnswerRe: Pesky JumpLists Pin
Sacha Barber10-Dec-13 4:04
Sacha Barber10-Dec-13 4:04 
GeneralSo great Pin
shijun_china20-Dec-10 20:57
shijun_china20-Dec-10 20:57 
GeneralMy vote of 5 Pin
Paulo Morábito Neto4-Nov-10 3:08
professionalPaulo Morábito Neto4-Nov-10 3:08 
GeneralRe: My vote of 5 Pin
Sacha Barber20-Dec-10 21:32
Sacha Barber20-Dec-10 21:32 
GeneralGreat Work :) Pin
g0got220-Apr-10 10:46
g0got220-Apr-10 10:46 
GeneralRe: Great Work :) Pin
Sacha Barber20-Apr-10 20:12
Sacha Barber20-Apr-10 20:12 
GeneralShould upgrade to Windows 7 Pin
hsn13-Apr-10 4:23
hsn13-Apr-10 4:23 
GeneralRe: Should upgrade to Windows 7 Pin
Sacha Barber3-Apr-10 20:55
Sacha Barber3-Apr-10 20:55 
GeneralMake sure that the file is a valid .NET Framework assembly. Pin
Geoffrey Jones25-Mar-10 11:22
Geoffrey Jones25-Mar-10 11:22 
GeneralRe: Make sure that the file is a valid .NET Framework assembly. Pin
Sacha Barber25-Mar-10 20:58
Sacha Barber25-Mar-10 20:58 
QuestionExpandoObject - I dont understand why you used it Pin
theperm10-Feb-10 5:09
theperm10-Feb-10 5:09 
AnswerRe: ExpandoObject - I dont understand why you used it Pin
Sacha Barber10-Feb-10 6:06
Sacha Barber10-Feb-10 6:06 
GeneralExcellent Article but... Pin
Abhishek Sur29-Dec-09 21:01
professionalAbhishek Sur29-Dec-09 21:01 
GeneralRe: Excellent Article but... Pin
Sacha Barber29-Dec-09 23:25
Sacha Barber29-Dec-09 23:25 
GeneralRe: Excellent Article but... Pin
Abhishek Sur30-Dec-09 0:18
professionalAbhishek Sur30-Dec-09 0:18 
GeneralMy vote of 1 Pin
Ahmed Safan4-Dec-09 14:15
professionalAhmed Safan4-Dec-09 14:15 
GeneralRe: My vote of 1 Pin
Sacha Barber5-Dec-09 0:14
Sacha Barber5-Dec-09 0:14 
GeneralRe: My vote of 1 Pin
Pete O'Hanlon7-Dec-09 10:29
mvePete O'Hanlon7-Dec-09 10:29 
GeneralRe: My vote of 1 Pin
Abhishek Sur29-Dec-09 20:55
professionalAbhishek Sur29-Dec-09 20:55 
GeneralRe: My vote of 1 Pin
hsn13-Apr-10 4:21
hsn13-Apr-10 4:21 
GeneralRe: My vote of 1 Pin
Sacha Barber3-Apr-10 20:55
Sacha Barber3-Apr-10 20:55 
GeneralRe: My vote of 1 Pin
Harry Neethling1-Nov-11 20:32
Harry Neethling1-Nov-11 20:32 
GeneralRe: My vote of 1 Pin
dmihailescu19-Aug-10 5:24
dmihailescu19-Aug-10 5:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.