Instructions : due to the size of the zip, I had to zip each project seperately. So please read instructions below carefully
- Create a new folder on your desktop called MefFX, and unzip the MefFX Solution.zip file to it.
- 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.
- 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
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
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.
So you can now supply an image, by dragging one of your favourite images to the app, which will make it look as follows:
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.
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.
Here is the WebBrowser control showing the details for the users selected pixel shader item.
The other area of interest is the Windows7 TaskBar
area, which looks like this
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 ICommand
s 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.
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.
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)
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);
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
:: 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:
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
{
private static PixelShader _shader = new PixelShader()
{
UriSource =
Utilities.GetResourcePackUri("MyEffect1/MyEffect1.ps")
};
public MyEffect1()
{
PixelShader = _shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(SomeInputProperty);
}
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input",
typeof(MyEffect1), 0);
public double SomeInput
{
get { return (float)GetValue(SomeInputProperty); }
set { SetValue(SomeInputProperty, value); }
}
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
<Image>
<Image.Effect>
<DropShadowEffect/>
</Image.Effect>
</Image>
WPF only really comes with a couple of pre built Effect
s, such as DropShadowEffect
. The Effect
s are not to be mistaken for the older BitmapEffect
s which are not hardware accelerated.
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 Type
s 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.
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
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Windows.Media.Effects;
using System.Dynamic;
using PixelShaders;
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
.
[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
.
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:
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.
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
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);
}
}
This this example shows how you can easily add new methods to dynamic
objects
sampleObject.number = 10;
sampleObject.Increment = (Action)(() => { sampleObject.number++; });
Console.WriteLine(sampleObject.number);
sampleObject.Increment();
Console.WriteLine(sampleObject.number);
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
.
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.
So all we need to do, to bind to an Expando is as follows, easy right.
<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>
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)
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:
<Grid AllowDrop="True" Drop="GridDrop".../>
And using Microsoft.WindowsAPICodePack, I support Drag & Drop onto this Grid by using the following code:
private void GridDrop(object sender, DragEventArgs e)
{
if (!inDragDrop)
{
string[] formats = e.Data.GetFormats();
foreach (string format in formats)
{
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.
The Windows7 TaskBar
is a strange one, as it can be done using the Microsoft.WindowsAPICodePack, there is however native support for Windows7 TaskBar
s 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:
<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>
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.
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.
private Boolean CanExecuteAboutCommand
{
get
{
return true;
}
}
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.
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."
Basically you can think of JumpList
s 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 LayoutTransform
s 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:
- Reference the Cjc.AwesomiumWrapper.dll
- Reference the Cjc.ChromiumBrowser.dll
- Include the Awesomium.dll (the original C++ wrapper (non WPF)), and make sure this is copied to the output folder
- 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 LayoutTransform
s as seen here
Here is the code to do this:
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.
="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.