Welcome to MSDN Blogs Sign in | Join | Help

Windows Media Metadata in Managed Code (and by extension - Silverlight)

I have encountered several inquiries about accessing the metadata embedded in Windows Media file headers from Silverlight. Unfortunately Silverlight does not expose this information out of the box. The only way you can access this is by utilizing the Windows Media Format SDK.

The Format SDK exposes its functionality in COM and requires you to write your code in C++. The nice thing about Visual C++, is that the C++ compiler allows you to author mixed mode assemblies i.e. one assembly containing both CLR types as well as code that is completely unmanaged, both authored in C++. Using this feature you could author a set of CLR targeted wrappers in C++ that interact with the Format SDK to expose select metadata to the managed world. Once you are done with that step, you can host this code in a Windows Service, a WCF service or some other form of server side component accessible over HTTP/TCP, and then have your Silverlight code interact with that service to get the metadata over the wire.

Attached is some code that I authored recently, that allows you to get started in that direction. You will find the code here.

In the rest of the post I will give you some details on the code. To better understand (and modify if necessary) the source, you may need some familiarity with authoring managed code with C++, the basics of COM in C++, and the Windows Media Format SDK.

Windows Media Files contain a lot of information in the header. The requests I have most often encountered are for the following:

  • File Level Metadata Attributes and Per Stream Metadata Attributes: A Windows Media file can contain multiple streams of different types including video streams, audio streams, text streams etc. The header may contain metadata attributes defined for the entire file, as well for each individual stream in the file.
  • Frame Rate for the video streams in the file
  • Embedded SMPTE Timecode ranges for the video streams

In the attached solution, you will find three projects – the WMShim and the WMShimManagedClasses projects produce assemblies that you will need to reference in your code to extract the metadata from the file. The third project named WMShimTestHarness contains a simple WPF application that shows you how to extract metadata using the classes in the previous two assemblies.

The class diagram below shows  the managed types that I use to define the metadata that the C++ code extracts (these types are defined in C#, in the MetadataDataContracts.cs file, in the WMShimManagedClasses project). Since the goal is to extract the metadata and have it be accessible remotely, these types are defined as data contracts to make them easily serializable.

  ClassDiagram1

The WMMetadataAttribute type defines a single metadata attribute and its value. The Name property contains the actual Windows Media metadata field name, and Label (where possible) contains a more friendly name explaining the field. The PropertyType contains string name for the CLR type that is best suited to represent the Value in managed code, and the Unit (where applicable) contains a string literal defining the unit of measurement(like bps for bitrate).

The WMStream type defines a stream, and the Attributes property returns a collection of WMMetadataAttribute instances for a specific stream or at the file level.

  • StreamType  contains one of the following values : “File”, “Video”, “Audio”, “Text”, “Script”, “Image”, “File Transfer” or “Unknown”. Each of these string literals are meant to represent a valid media type or subtype as described by the Format SDK (see http://msdn.microsoft.com/en-us/library/dd757532(VS.85).aspx ) that can be used to define a stream type. The exceptions are the values “File” used to describe file level metadata (it is not actually a stream, I just reuse the WMStream type – too lazy to build another type to represent a file) and the value “Unknown” in case there is some weird stuff going on.
  • FrameRate & TimeCodeRanges are meaningful only for video streams
  • For file level metadata BitRate, FrameRate, TimeCodeRanges are not meaningful, and StreamIndex and StreamNumber are set to 0.

The primary C++ class that is central to the extraction functionality is named WMMetadataReader, defined in WMMetadataReader.h in the WMShim project.

 

 

public ref class WMMetadataReader
  {
  public:
    WMMetadataReader(void){} 
    /** 
    Get the number of streams in a media file
    FilePath: Absolute file path 
    Return: Stream count
    **/
    unsigned int GetStreamCount(String^ MediaFilePath);
    /** 
    Get the metadata for a specific stream in a media file
    FilePath: Absolute file path
    RFCLangIdentifier: string literal for the language identifier - "en-us" etc.
    StreamIdx: 1 based Stream Index, Pass 0 for file level metadata
    Return: WMStream instance
    **/
    WMStream^ GetStreamMetadata(String^ FilePath,String^ RFCLangIdentifier, unsigned int StreamIdx);
    /** 
    Get the framerate for a video stream
    FilePath: Absolute file path 
    StreamIdx: 1 based Stream Index
    Return: Frame rate, 0 for non-video streams
    **/
    double GetFrameRate(String^ FilePath, unsigned int StreamIndex);
  }

The code above shows the public methods on WMMetadataReader- the code comments will tell you what each method does. The easiest way to extract metadata for a media file is to call the GetStreamCount() method to get the # of streams, and then call GetStreamMetadata() once for each Stream. Remember to pass a 1 based StreamIdx value for the actual streams, and pass 0 to get the file level metadata. The C# code snippet below will give you an idea – as we loop through the streams and get the metadata for each, we just add them to a List named streams, that we will use eventually else where in our code.

WMShim.WMMetadataReader rdr = new WMShim.WMMetadataReader();
int StreamCount = (int)rdr.GetStreamCount(FilePath);
List<WMStream> streams = new List<WMStream>(StreamCount + 1);
for (int idx = 0; idx <= StreamCount; idx++)
{
  streams.Add(rdr.GetStreamMetadata(ofd.FileName, "en-us", (uint)idx));
}

Below are screen shots from the WPF test harness that is also in the attached code. The above C# code snippet is actually from that app. The two screen shot shows the video stream metadata and the file level metadata from a wmv file named Dust_To_Glory.wmv.

snap1 snap2

If you look through the code you will notice that I do not supply any code to extract SMPTE Timecode ranges yet. I am still finalizing that code – I will have another post with an attachment here in a few days.

A few notes about the attached code:

- The code was built on 64 bit Windows 7 using Visual Studio 2008

- The WMShim C++ project was built targeting the x64 platform. So do make sure that you have the x64 compilers and tools for C++ installed as a part of your VS 2008 installation. If you want to retarget WMShim to a 32 bit platform instead, just change the platform to Win32 in your Project|Properties dialog and recompile.

- If you want to run the code on Windows Server, you will need to enable the “Desktop Experience” feature. This puts the necessary windows media components on the server (they are not there by default – unlike the Windows client desktop OS platforms).

 

 

 

Until the next post then !!

Posted by jitghosh | 0 Comments

IIS Media Services 3.0 Released!!

The IIS Media Services team just released the IIS Media Services 3.0 suite of technologies. This is pretty big release for us – with Live Smooth Streaming possibly being the coolest part of it. If you are watching the NBC Sunday Night Football on the web, then you have seen Live Smooth Streaming in action. You you have not – you must check it out here.

To install and try out IIS Media Services 3.0 , download it via the Web Platform Installer here.

So what’s in IIS Media Services 3.0 ?

Smooth Streaming and Live Smooth Streaming for adaptive streaming of media over HTTP

Bit Rate Throttling for controlling bandwidth spent in delivering media

Web Playlists for controlled and sequenced delivery of media

Advanced Logging for logging both on the client and the server

Application Request Routing to provide edge caching.

 

Another notable bit of software that was just put out there (albeit still in beta) is the Smooth Streaming Player Development Kit. If you are a Silverlight developer, and are looking to extend the same programming model of the Silverlight MediaElement to the world of Smooth Streaming, then the SmoothStreamingMediaElement (SSME) component contained in this kit will make your life really easy. The SSME exposes a ton of additional rich functionality to take advantage of the Smooth Streaming platform and file formats – do check it out !!

 

I will be blogging more on some of this stuff in the coming weeks – ta for now !!

Posted by jitghosh | 1 Comments

Silverlight 3 is now officially released!!

WooHoo!! Go read more about it in Scott’s blog here. I will be positing my own experiences and thoughts in the coming days.

 

Learn more and download at http://silverlight.net/GetStarted/

 

Enjoy!!

Posted by jitghosh | 0 Comments

Architect Council Webcasts

The Microsoft Patterns & Practices team, in collaboration with members of the Microsoft Developer Evangelism team (my uber team that is) is putting together a series of webcasts titled “Pragmatic Patterns for Architects” . I would vote “must attend” for these. Below are more details:

 

ARCHITECT COUNCIL | Pragmatic Patterns for Architects

“Cloud computing will supersede traditional IT”, “SOA will enable business agility”, “my way or the highway”, etc. We’ve all heard this type of proclamations before, as many look to the “next big things” in technology to exact sweeping changes and solve many issues; truth is, technologies and tools aren’t as instrumental in influencing progress, as the design and discipline in applying them to specific issues. When used appropriately, technologies and tools can be powerful enablers that bring about change.

One of the things we hear a lot working with the community is a desire for more guidance about how to use the technology instead of just talking about features and functions.  To address this, our team has put together a series of live webcasts on June 9th – 11th which will focus on guidance and patterns for some of today’s hottest topics. 

DAY 1 – June 9, 2009 at Noon PST

Patterns for Moving to the Cloud

Larry Clarkin & Wade Wegner

Everything that you read these days seems to suggest that you should be moving to the cloud. But where do you start? Which applications and services should be moving to the cloud? How do you build the bridge between on-premises and the cloud? And more importantly, what should you be looking out for along the way? In this session, learn architectural patterns and factors for moving to the cloud. Based on real-world projects, the session explores building block services, patterns for exposing applications, and challenges involving identity, data federation, and management. This session provides the tools and knowledge to determine whether cloud computing is right for you, and where to start.

DAY 2 – June 10, 2009 at Noon PST

Building Silverlight & WPF Applications with Prism

David Hill

Prism provides guidance, via design patterns, to help you build robust, flexible and modular Silverlight and WPF applications. These patterns support unit testing, separation of concerns, loose coupling and the ability to share application logic between Silverlight and WPF applications. Prism includes source code for the library itself, extensive documentation, and a sample application that shows how the patterns work together in a real-world application. It also includes a Visual Studio add-in to help you easily share code between WPF and Silverlight. This session provides an overview of Prism, and shows how you can use Prism to design and build composite Silverlight applications.

DAY 3 – June 11, 2009 at Noon PST

Patterns for Parallel Computing

David Chou

With recent advances in cloud computing, service-oriented architectures, distributed computing, server virtualization, multi-core processors; we are now seeing parallel computing techniques being implemented across the spectrum. It’s moving towards mainstream applications such as internet-scale web applications, massive data processing, graphics rendering, but the myriad of choices also present a number of questions on when and how to utilize parallel computing. This session explores the architectural patterns and trade-offs between different forms of parallel computing including: approaches for utilizing them to improve application performance, optimizing the use of existing infrastructure, and applying concurrency towards day-to-day enterprise information processing needs.

WEBCAST AGENDA

11:45 AM (PST)

Open for Dial-in

12:00 PM (PST)

Day’s Content

12:50 PM (PST)

Q&A

01:00 PM (PST)

Raffle and Close

REGISTER

To register, please click on the link below for each day:

 

Title

Event ID

Link to Register

Day 1 6/9/09

Patterns for Moving to the Cloud

1032416875

http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032416875&EventCategory=2&culture=en-US&CountryCode=US

Day 2 6/10/09

Building Silverlight & WPF Applications with Prism

1032416983

http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032416983&EventCategory=2&culture=en-US&CountryCode=US

Day 3 6/11/09

Patterns for Parallel Computing

1032416984

http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032416984&EventCategory=2&culture=en-US&CountryCode=US

We will email you with the LIVEMEETING information and log-in detail a few days before the actual event. We will use the email address you provide in the registration. Thanks!

SPEAKER BIOS

Larry Clarkin - SR ARCHITECT EVANGELIST, Microsoft

Wade Wegner - SR ARCHITECT EVANGELIST, Microsoft

Architect in the Developer & Platform Evangelism division at Microsoft, tasked to collaborate with organizations in the advanced and emergent areas of enterprise architecture, SOA, Web 2.0, and cloud computing, as well as to support decision makers on defining technology adoption strategies. You can reach Wade at his blog http://www.architectingwith.net/ or through twitter at http://twitter.com/wadewegner.

David Hill – PRINCIPAL ARCHITECT, Microsoft Patterns & Practices Team

David Chou – ARCHITECT, Microsoft

Architect in the Developer & Platform Evangelism organization at Microsoft, focused on collaborating with enterprises and organizations in many areas such as cloud computing, SOA, Web, RIA, distributed systems, security, etc., and supporting decision makers on defining evolutionary strategies in architecture. Drawing on experiences from his previous jobs at Sun Microsystems and Accenture, David enjoys helping customers create value from using objective and pragmatic approaches to define IT strategies, roadmaps, and solution architectures.

Posted by jitghosh | 1 Comments

MTV & Silverlight 3 at NAB 2009

NAB 2009 rocked !!

It was exhausting working the booth, standing on my feet all day, talking to so many people over the course of four days, but it was so exciting at the same time. We showed many cool things at the Microsoft booth, including Silverlight 3, IIS 7 Smooth Streaming, our Fast search engine, our Advertising solutions, a ton of solutions from our partners, and last but not the least – some very interesting open source starter kits that my team built, and that I will blog about in coming posts.

But I am especially excited about a specific application I was directly involved in.

Viacom/MTV announced a web based Digital Asset Management Workflow application completely built in Silverlight 3 (of course using the beta bits for now) and .Net 3.5. This is very cool, not just because it is an early adoption of Silverlight 3, but because :

- It is a great proof point that Silverlight is not just for consumer facing scenarios on the web, but is equally suited for more rigorous applications in broadcast media workflow.

- It is one of the first production applications to use the h.264 playback capabilities being enabled in Silverlight 3. MTV creates all their proxies as QuickTime .mov files, with the essence encoded using h.264 compression, either in SD or in HD resolutions. Since QT is essentially a variant of the MP4 container structure, and SL 3 supports parsing MP4 and decoding h.264 natively, we could playback all of MTV’s QT content natively in SL3, without any further transcoding. This was a huge win-win for all involved both from a time and cost perspective.

I provided some of the necessary technology guidance to a joint team from Microsoft, Vertigo and MTV in implementing the solution.

You can read more about the general press release from Viacom CIO Joe Simon here and about the specific case study here.

Posted by jitghosh | 4 Comments

Binding an embedded image using a value converter

Recently I needed to bind some embedded images to data templates. If you look at the Image type in Silverlight 2, you will see that it exposes a Source property of type ImageSource that can be set to the URI of an image either relative to the XAP, or in its absolute form. If the URI is valid, the image stream is opened and read at runtime, an underlying BitmapImage is created around the stream and is then fed into Image.Source (BitmapImage derives from ImageSource).

However this is all great when the images are on a web site. But what about images that are packaged with my XAP ? It is actually not very hard to bind those either. Assuming you have a XAP assembly named Foo (with Foo being the default namespace), and say your image, Bar.png is stored in a project folder named Images, once you compile the assembly, the image is embedded into the assembly as a resource named Foo.Images.Bar.png. To get this done through Visual Studio mark your image  as an Embedded Resource. However to access the embedded image and create a BitmapImage out of it, you will have to write some code like this:

BitmapImage bim = new BitmapImage();
bim.SetSource(this.GetType().Assembly.GetManifestResourceStream("Foo.Images.Bar.png"));

This BitmapImage instance can then be bound directly to the Image.Source property using a traditional binding expression.

However, you may already know all of this, and in any case I wanted to make this a little more general purpose. I did not quite like the fact that the string literal representing the image (in the above case “Foo.Images.Bar.png” ) could not be directly fed to the Image element in my XAML like a URI. I had to create some sort of artificial property in some type that would instead expose a BitmapImage instance, and then bind that property to my Image.Source. In effect what I wanted to do was something like this:

<Image Source="Foo.Images.Bar.png" />

but sadly that would not work. So instead I took the approach of using a value converter. I wrote a converter that looks like this:

public class ImageResourceNameToBitmapImageConverter : IValueConverter
  {
    public object Convert(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      //check to see that the parameter types are conformant
      if (!(value is string) || targetType != typeof(ImageSource) )
        return null;
      BitmapImage MenuItemImage = new BitmapImage();
      try
      {
        MenuItemImage.SetSource(this.GetType().Assembly.
          GetManifestResourceStream(value as string));
      }
      catch (Exception Ex)
      {
        return null;
      }
      return MenuItemImage;
    }
    public object ConvertBack(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();

    }
  }

 

Now in my XAML I can declare the converter:

 

<local:ImageResourceNameToBitmapImageConverter x:Key="REF_ImageResourceNameToBitmapImageConverter" />

and then an Image element like this:

<Image Source="{Binding Converter={StaticResource REF_ImageResourceNameToBitmapImageConverter}}" DataContext="Foo.Images.Bar.png" />

 

This does the trick. Since the Binding does not specify a path, the converter gets the DataContext (which is the qualified image resource string name) passed in as the value parameter, and all I do in the converter is use the same code as before to create and pass out the BitmapImage instance.

 

Came in pretty handy and saved me the grief of creating unnecessary CLR properties for each necessary image binding.

Posted by jitghosh | 1 Comments

Open position in our team..

Our team (Developer & Platform Evangelism for Communications, Media & Entertainment) is looking to hire a person in the role of a Platform Strategy Advisor. A PSA is a very exciting, highly visible, and influential role within the team and the company. In the words of the hiring manager, below is a description of the job role:

The PSA role is a combination of business development and technical advisory. PSA's are tasked with creating deals that will drive large scale, early adoption of Microsoft technology across key accounts. The expectation is, not only will these wins lead to PR and evidence, but will also strengthen the long-term relationship between Microsoft and the customer. As such, PSA candidates need a good mix of business and technical skills as well as knowledge of the particular industries they serve. The role focuses on Media, Entertainment and Network service providers (Telco, Cable and Satellite) with a particular interest in how they are merging into one “Converged Services and Content Distribution Industry”.

This position handles the top 20-30 customers across those industries and  is based on the east coast. The customers are located throughout the US so travel is required (about 30-40%), with most being one-two day trips. Success is measured by the number of large-scale evidence wins produced and overall strategic relationship we have with the accounts in the industry. Typical activities would include -

  • working with account teams to schedule executive level briefings
  • maintaining close, working relationships with the CTO/CIO organizations
  • working with those organizations to develop strategic projects that drive early adoption of .Net developer platforms and products
  • building mutually beneficial business terms around those projects to create evidence and PR
  • working with architects to develop the project and align it with strategic initiatives inside of Microsoft
  • securing internal funding and resources for those projects
  • managing a pipeline of strategic projects and evidence
  • ensuring that evidence and PR is created from the strategic projects
  • building relationships with key partners to assist in these projects
  • participation in Industry events
  • Provide thought leadership to the industry by way of publications, event participation and PR opportunities
  • working with other members of the team to identify strategic projects and help coordinate activates around them

We're looking for someone with at least 5 years of industry experience (Media, Telco, Cable) as well as a background in software development. They must have acumen in the areas of business development and/or management and have some experience leading a team.

If you are interested, or know someone who might and fit the profile, please send an email to Helen Axelson (helenax@microsoft.com) and  reference Job Code:247186.

Who knows ? We could have some fun together shaping the future of digital media !!

- Jit

Posted by jitghosh | 1 Comments

Back to blogging - my book and other news…

I have been inconspicuously absent on blogosphere for a while now. For my handful readers – I hope you missed me :-). I am back to blogging and hope to put out some more interesting stuff for you guys in the coming weeks.

First for exciting news. We are close, very close to finishing up Silverlight 2. The product team just made the first release candidate available, and you can download it here. You can read more about the release in Scott Guthrie’s blog post here.

We also just announced the first service pack to Expression Encoder 2. Video is very important to my work, and there are some tremendous changes to be included in the SP. H.264/AAC output – finally !!!. Read more about it in Ben’s blog post here and James’ post here.

I have also been busy finishing my book on Silverlight 2, that I have been coauthoring with my teammate Rob Cameron. You can pre-order the book here. We are in the final phases of editing, but APress is also planning to put out an e-book version in their Alpha book program that allows you to pre-purchase the book and progressively read chapters as they are being edited. You can find more details at the APress web site. Rob and I are really excited about the book, and for those of you who decide to give it a try, we hope you enjoy reading it as much as we enjoyed writing it. It was hard writing a book targeting the RTM version of a technology that was still being built while we wrote. But we are pretty proud of the end product, and if you are planning to work with Silverlight 2, we are confident you will find the book useful.

Until the next post…

Posted by jitghosh | 1 Comments

Silverlight 2 Beta 2 is now available !!

Woo - Hoo!!

Navigate over to http://silverlight.net/GetStarted/ to download the  freshly minted beta 2 bits including the runtime, the SDK, and the Visual Studio 2008 tools for Silverlight 2 development. There is also a brand new version of Blend 2.5 preview and a new version of the Deep Zoom Composer available for download as well.

I have been quite busy working on several large customer projects that involve Silverlight 2 Beta 2. In the coming weeks do visit here to find more tid-bits about SL 2 development and the changes in Beta 2.

Enjoy !!

Posted by jitghosh | 2 Comments

Broken link to attachment fixed for previous post

Sone folks pointed that the code sample attachment in my previous post on the Async Multiple File Upload Control for Silverlight 2 was broken. I have fixed it in the post as well provided a link below to the code on my Windows Live SkyDrive.

http://cid-60bb739cbf5d117b.skydrive.live.com/self.aspx/Public/MultiFileUpload/MultiFileUpload.zip

Sorry for the incovinience.

Posted by jitghosh | 2 Comments

Async Multi File Upload Control with Metadata support for Silverlight 2 (Beta 1)

By now a lot of you must be having a lot of fun of Silverlight 2 Beta 1. If you have not downloaded, installed and played with SL 2 Beta 1, I urge you do it right away - you are missing out!!! You can download SL2 here.

I have been working on a number of projects that are using the Silverlight 2 beta bits, and several of them needed file upload functionality. I looked around, and found a few controls, but while all of them did certain things admirably well, there were a few things missing.

  • A few of the controls I found allowed you to select multiple files, and upload them, but all of them did so sequentially i.e. one file at a time. Parallel uploads were a necessity for my projects, with the extent of parallelization as a controllable parameter.
  • None of them really allowed you to control how a large file gets chunked i.e. at what size boundaries. This can be an important parameterization to improve efficiency.
  • Since most of my projects are media related, it is common for me to work on applications where a user is allowed to attach metadata to the file being uploaded. And metadata could vary from application to application. I needed a control that would allow some sort of metadata architecture, that is not hard wired to the control and pluggable by design. I wanted to reuse the same control in many apps, with the consuming apps defining the metadata UI and serialization process, and wiring it to the control through a standard interface.
  • And lastly I also needed a completely templatable control.

Needless to say I could not find a combination of all of the above in one control - so I set out to do the next best thing - write one for my own. And the point of this entry is to share it with you. The code is zipped and attached.

First a little about the solution structure. You will find several projects in the solution:

  • MFUControl - This the main control project containing most of the functionality behind the control. It also contains all of the control's UI.For those of you who are just starting out with SL 2, or have not played with WPF custom controls before, the default UI for a custom control is defined as a control template that gets applied to the control through a style defined in a file called generic.xaml - this file has to be named exactly this way for the runtime to recognize it(this is similar to WPF). Also, applying a UI to a custom control by way of a control template allows the consumer of the control to later apply other templates to change the L&F.
  • ProgressBar - Since SL 2 Beta 1 does not have a ProgressBar control yet, and I needed one, I created one by extending the RangeBase control. Again a default control template is applied and can be found in the generic.xaml file in the project.
  • MFUTestApp - This the test harness for the app - standard SL 2 app that has one page with the control on it.
  • TestWeb - An ASP.Net web app that can be used to run MFUTestApp from IIS
  • UploadService - This the HTTP backend to which the files are uploaded. I have chosen to use a WCF service, using a RESTful interface (implemented using the WCF Web Programming layer), with a single operation that gets invoked using an HTTP POST accepting a file stream.

I will attempt to sparingly explain some of the key pieces of the code here. I have commented the code pretty well - so hopefully in reading through the downloaded code most of it will be obvious to you.

But first things first - below is a screen shot of the control in action, uploading some videos. By the way, if some of you are designers and other kind of creative professionals, and are snickering at the UI, you will have to forgive the paucity of my artistic skills. I can code, but let it be known - I cannot design nice UI's :-).

MFU1

So the key pieces of the code as I said :

  • The actual upload functionality - The upload piece is pretty simple. I use the HttpWebRequest type to do the upload. Initialize it properly, plug in the file name in the HTTP headers, get the request stream, write the file content to it, and POST - that's about it. On the service side, it is even simpler - open the stream, get the file name from the HTTP headers, create a new file at a known location and write it out. This goes on for every file that is being uploaded.
  • The parallelization/multi threaded upload - Unfortunately while SL 2 allows you to spin up your own threads, it does not allow making network calls from anywhere other than the UI thread(at least not yet). So I had to look for a way around. The good thing is that the network API's in SL 2 are all async, and use the standard Begin...()/End...() async invocation pattern established in .Net. In this pattern as soon as you make the egin..() call, control returns to your code and the actual unit of work initiated with the call gets automatically shifted to a background thread from the thread pool.

In my code I break up the entire list of files of chosen by the user into sublists based on the number of background threads the user desires to use (this is specified by the MaximumUploadThreads dependency property on the control) - one sublist for each thread. As an example if you had selected 14 files and specified 4 threads, the first 3 sublists each get 4 files, and the last one gets 2.

Once these lists are broken up, I loop through them, and call BeginGetRequestStream() once for the first file in each list. Control returns and only as many threads get employed as there are sublists, and upload starts. To ensure that at any point in time, no more threads get employed, I start uploading the next file in any sublist, once the previous upload for that sublist is completed i.e. initiate the next upload in the callback handler specified in the BeginGetResponseStream() method for the previous upload, which gets invoked once a response comes back from the service after the previous upload completes (or fails).

Note that upping the number of threads will allow you to achieve more parallelization, but at the cost of more background threads on the client.

  • The chunking of large files - The control has another dependency property called FileChunkBoundaryInKiloBytes. What you specify is used as the chunking size boundary. For files that are smaller in size than the size specified here are uploaded in one fell swoop, and the progress bar goes straight from 0 to 100%. This is also because the HttpWebRequest has no mechanism for reporting true progress of the network activity :-(. However for files that are larger than the chunking size, are uploaded in chunk sizes as specified. the code follows a different route for those files, and all chunks of a file get uploaded in the same background thread as being used for the sublist to which the file belonged. An internal data structure named FileChunk is used to maintain stream positions, so that successive chunk uploads can be done accurately.

Progress is reported for each chunk, so you will see the progress bar report things somewhat more realistically here. Also additional HTTP headers are passed in to the backend, for each chunk, including the chunk #, total chunks and the chunk size, to facilitate the proper appending of chunks to the file as it gets built on the server with each POST of a chunk.

Note that upping the boundary may be a lucrative option for very large file uploads to achieve faster uploads, but this is client memory, and you can soon get OutOfMemory exceptions if you are not careful.

  • The metadata piece - The assumption here is that the client (the app using the control) and the server has some well known contract for the metadata passed and expected respectively, and I needed a way for the control to facilitate the collection, packaging and uploading of that metadata, without itself being aware of any specifics of the metadata - that is the only way it would be reusable across all possible metadata requirements.

The metadata UI is supplied to the control by you defining a Data Template in your app code with the appropriate UI, and supplying it to the control through the FileMetadataTemplate dependency property.

You also need to implement a custom type to act as the data source for this data template. This type needs to implement an interface named IFileMetadataBase that I defined, and that has a single method named SerializeFileMetadata() returning a string. This method is where you supply your serialization logic (in the provided sample I use Json - but you could as easily use XML) , and as long as the serialized form is a string - we are good. This type also will typically implement the metadata elements as properties that you will bind to the appropriate portions of your metadata Data Template.

So how does all this get wired up ? At runtime if the control senses that a Data Template has been specified for metadata, it raises an event called ProvideMetadataInstance and you will need to handle this. The event arguments to this event has a single property of type IFileMetadataBase (the interface I mentioned above), and you set it to a new instance of the implementing type that you created - and voila!!. The control wires up the instance to the data template. When files are uploaded, it calls the function to serialize the metadata to a string, and passes it on as another additional, well named HTTP header, so that the backend can do whatever it wants with it.

I provide a sample of this in the code, with two metadata fields called "Comments" and "Description". Clicking the little button on each file entry, displays the data template which you can fill out.

MFU2

  • The backend - I implement this as a WCF service. But since the interface is simple (HTTP POST + Headers) - I am certain that you ASP.Net (or even other web platform) experts out there can reimplement it using other patterns.

Also the service now puts all uploaded files in a subfolder called Assets under the service directory. This is hardwired in the service code. Change it as you see fit, but do remember to change service code accordingly as well.

  • There is also remove functionality in the control - you can check the little checkboxes, a Remove button appears, and you can then remove files from the list

There are a lot of things incomplete in the control:

  • Slick UI
  • There should be a ceiling enforced on both the chunking size and max thread count to prevent inadvertent memory outage/resource outage on the client
  • The button (or whatever else) that shows the metadata, needs to be hidden when no data template is attached
  • In future revs of SL 2, as network calls and background threads become more friendly, you can take better advantage of that
  • Any other bugs/incomplete features you can find (of which there may be a ton)
  • Slick UI (Have I said that already ??)

In any case my goal was to give you all something to play with and certainly improve upon. Let me know if this helped.

Remember to uninstall any older versions of SL on your machine, download and install the runtime , the SDK and the tools, and enjoy!!!

 

Download Link : http://cid-60bb739cbf5d117b.skydrive.live.com/self.aspx/Public/MultiFileUpload/MultiFileUpload.zip

Posted by jitghosh | 15 Comments

Slides and Demos from Developer Days, March 13th in New York

I recently gave two presentations at one of our events in New York - one on WPF and one on Silverlight 2. A lot of folks asked me to make the slide decks and the code demos available. So here they are (zipped) - you can download them from my Windows Live SkyDrive below. Feel free to ping me if you have questions. Sorry for being tardy in getting this done.

Click the icon above to download

Enjoy!!

Posted by jitghosh | 3 Comments

WPF based Music CD Ripper

One of the WPF based projects I am working on right now needs some functionality to enable ripping music CD's a la Windows Media Player. I was digging into the various Windows Media SDK's as well as the Media Foundation stuff in Windows Vista to find the best way to achieve this - and it turns out the easiest is to rely on Windows Media Player itself. WMP exposes all of this via COM - so all you need to do is add a reference to the appropriate typelib, and off you go. Probably not the best solution, because of the WMP dependency, but it works nicely. I built a quick WPF prototype, and thought some of you might benefit from the code. I am attaching the Visual Studio project (zipped)- feel free to play with it.

 

Note that you will need Visual Studio 2008 and WMP 11 on your machine. There is also a way to use WMP 10 for this, but the user experience is not as nice. Also note that there are ways to rip music CD's with no dependency on WMP, but the solution will be significantly more complicated. Also be aware that depending on your settings, as soon as you pop a music CD, WMP might come up itself and start ripping. Make sure to close WMP before you try this app out.

 

Below is a screenshot. Zipped source code attached.

Enjoy!!!

CDRipper

Posted by jitghosh | 7 Comments
Attachment(s): CDRipper.zip

WPF Control Templates - An Overview

In several of my discussions with customers around WPF in the recent past, I got asked a bunch of questions around Control Templating. Most of them can be summarized to one - "how do control templates work". Control Templating is fairly fundamental to WPF development, and unlike some niche features like WPF 3D, a good understanding of Control Templating goes a long way to sleek UI development.

There are many excellent writings on this topic - so before I write something on this, I should at least point a few out. Chances are that, you might get a much better idea from these works than my post here.

The best coverage I have read is in Chris Anderson's excellent book Essential Windows Presentation Foundation. If you are working with WPF, and need a deep understanding of not just the workings of the framework, but the design motivations behind why certain things are the way they are - I would urge you to read this book cover-to-cover.  You should also check out Adam Nathan's work WPF Unleashed, and Programming WPF by Chris Sells and Ian Griffiths - both excellent books as well. Now that you have that information, let me see if I can put things in a simple perspective.

To understand control templates, it is helpful to first understand two related concepts:

  • The WPF element tree - the "logical tree" of a control template vs. it's "visual tree"
  • The content model for WPF controls.

Logical Tree

Let's start with the "logical" tree. The simple XAML snippet below produces a ListBox with three items:

 

<ListBox Height="100" Width="100">
 
<ListBoxItem>Item 1</ListBoxItem
>
 
<ListBoxItem>Item 2</ListBoxItem
>
 
<ListBoxItem>Item 3</ListBoxItem
>
</ListBox>
image

 
The element tree of the ListBox as depicted in the XAML hierarchy is shaped like 
 

Picture1

 

This is the logical tree for the ListBox. If we were to take an even simpler snippet of XAML like

 

<Button Height="40" Width="100" Content="Hello" /> image
 

the logical tree would have just one node - the Button element itself. The logical tree is the externally visible element hierarchy that a consumer of the control gets to program against.

Visual Tree

However, the logical tree provides no information as to the UI that the control displays. The fact that the ListBox displays itself with a bounding rectangle with a black border, or that the Button has a gradient background with an exterior chrome are all encapsulated somewhere else. 

In older UI Frameworks like Win32, Windows Forms or MFC, the control author implemented the UI in the control code, thus providing no facility to the control consumer to completely replace the control's UI - the traditional ways of offering customization capabilities have been to expose properties and methods that allow some customization of the constituent elements and consequently the control's look & feel. 

In WPF however, this is done somewhat differently. To understand how, let's first take a look at the element tree of the ListBox again. Type the XAML in XAMLPad (available with the Windows SDK), and XAMLPad renders the ListBox. Click the "Show/Hide Visual Tree" button (pointed to by the blue arrow) as shown below:

 

Picture2

 

Expanding the Visual Tree Explorer completely, if you look at the ListBox node and below,you can see a tree formed of a multitude of other controls and drawing primitives . This is the "visual tree" for the ListBox control - the complete element tree made of the actual drawing primitives and other controls used to implement the UI of the control. If you look down you will find a similarly detailed tree for each of the ListBoxItems situated within the ListBox.

image

 

The "visual tree" for a control is what contributes to the complete look & feel of the control - it is helpful to be able to see it like this. But what is even more helpful is an understanding of how this element tree is supplied to the control in order to generate the UI, and most importantly, how you as a consumer of the control might be able to change it.

Control Templates & the "Visual Tree"

As a control author you have the option of hard coding this "visual tree" in your control implementation i.e. somewhere in the control's creation and rendering lifecycle, you can instantiate the complete element tree in code, and use that to render the UI. Not that it's a bad way, and in fact there are several controls, that come with WPF, that take this approach, but it makes your control's UI pretty hard to customize.

Control Template's provide a more extensible way. You may have heard it said many times, that WPF controls are "lookless" in that they define the functionality behind the control, with a "default" UI which can be completely replaced by their consumer. This is what control templates enable.

Control Templates are XAML declarations of a "visual tree" for a control. As a control author you can declare and use them in your control implementation to provide the "default" UI for your control. Doing so also leaves the control open for templating by consumers - a consumer can then declare new templates that can be supplied to a control, either in code or in XAML itself, to replace the default look & feel.

That said let's take a look at how that might work.

Creating and Applying a Control Template 

A Control Template is declared inside a <ControlTemplate> element and is usually defined in a resource dictionary. As a requirement of being a part of a resource dictionary, the template is given a unique identifier using the x:Key attribute, and it also specifies the control type to which it can be applied using the TargetType property.

Let's start with a simple one that can be applied to the Button Control.

<ControlTemplate x:Key="CTGelButton" TargetType="{x:Type Button}">
    <Grid Width="100" Height="100">
        <Ellipse Stroke="#FF000000" Fill="#FF1C46E7" x:Name="OuterEllipse">
            <Ellipse.BitmapEffect>
                <BevelBitmapEffect BevelWidth="8" EdgeProfile="BulgedUp" Smoothness="0.745"/>
            </Ellipse.BitmapEffect>
        </Ellipse>
        <Ellipse Margin="8,8,8,8" x:Name="InnerEllipse">
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" SpreadMethod="Pad">
                    <GradientStop Color="#FF1C46E7" Offset="0"/>
                    <GradientStop Color="#FFFFFFFF" Offset="1"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/>
    </Grid>
</ControlTemplate>

 

Applying the template to a Button control is done so :

<Button Content="Hello World" Template="{DynamicResource CTGelButton}"/>
image

 

As you can see applying the template has completely replaced the default Button UI. However, the essential functionality, as implemented by the Button type, has not changed at all. All the events are still raised the way they would by default, all the methods and properties are still available as prescribed by the Button's API - you would program against the Button instance just the way you would if it was displaying its default UI.

Control Content Model & Control Templates

Sometimes the primary purpose of a control is to display content that is supplied to the control. Take for example a ListBox or a ComboBox or a TreeView - these are all controls that have a default look and feel that can be further customized using control templates, but their primary purpose is to display a set of items somewhere within that look and feel. The part of the WPF control architecture that specifies how controls display content is called the Content Model.

To better understand the content model in WPF, let's consider the ContentControl type. The ContentControl itself has a very limited UI, but has a dependency property called Content, that can either be set or databound to any content, which then gets displayed. More than being useful in and of itself, the ContentControl serves as the base class for many other WPF content controls like Label or Button(in case of Button it is not the immediate ancestor - ButtonBase is), and consequently drives the content model for these controls. For example in the XAML below, we show two examples of setting the Content of the Button we have been working with before - once to a text string, and once again to another XAML element tree consisting of a Grid containing a Radiobutton and a Checkbox(a rather contrived example - but serves the purpose of illustration) that can be visually rendered:

 

Button Content Example 1: Text Content  
   
<Button Content="Hello World" Template="{DynamicResource CTGelButton}"/>
image
   
Button Content Example 2: Visual Tree Content   
   
<Button RenderTransformOrigin="0.625,2.55" Grid.Row="1" Margin="50,21,128,92"
        Style="{DynamicResource StyleGelButton}">
    <Button.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition/>
            </Grid.RowDefinitions>
            <RadioButton Content="RB 1" Grid.Row="0" IsChecked="True"/>
            <CheckBox Content="CB 2" Grid.Row="1" IsChecked="True"/>                    
        </Grid>
    </Button.Content>
</Button>
image

The ContentControl defines a content model where it or its derivatives can display a single piece of content (it can be a whole tree of elements - but still rooted at a single element).

But why is the understanding of the Content Model important to you when considering control templating ?

When you design a template for a control you need to be aware of the intended content model for that control, and to be fair to the control's intended behavior, should try to retain the same content model in your custom template. Let's take a look at the first template we designed for our Button.

<ControlTemplate x:Key="CTGelButton" TargetType="{x:Type Button}">
    <Grid Width="100" Height="100">
        <Ellipse Stroke="#FF000000" Fill="#FF1C46E7" x:Name="OuterEllipse">
            <Ellipse.BitmapEffect>
                <BevelBitmapEffect BevelWidth="8" EdgeProfile="BulgedUp" Smoothness="0.745"/>
            </Ellipse.BitmapEffect>
        </Ellipse>
        <Ellipse Margin="8,8,8,8" x:Name="InnerEllipse">
            <Ellipse.Fill>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" SpreadMethod="Pad">
                    <GradientStop Color="#FF1C46E7" Offset="0"/>
                    <GradientStop Color="#FFFFFFFF" Offset="1"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/>
    </Grid>
 </ControlTemplate>

Take a look at the line of XAML in bold above. The ContentPresenter control is essentially a placeholder that binds to the content specified at the declaration of the Button control via its Content property. The ContentPresenter contains all the rules that determine how the content is displayed. When you are defining a new template for the Button control, if you do not specify a ContentPresenter somewhere in your control template, the Button will not behave as intended i.e. no matter what is specified for the Content property , it will not be displayed within your newly templated Button.

There are several other content models supported by WPF - like having a header portion to the content or displaying a list of items. There are other similar base level control classes, like HeaderedContentControl, ItemsControl and HeaderedItemsControl that can help implement these alternate content models. Let's take a look at a ListBox for example, which has ItemsControl as a parent in its inheritance chain. If we were to specify a template for a ListBox like so:

 

<ControlTemplate x:Key="CTListBox" TargetType="{x:Type ListBox}">
    <Border BorderBrush="#FFE62121" BorderThickness="4,4,4,4" CornerRadius="3,3,3,3" >
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFBCDEDC" Offset="0"/>
                <GradientStop Color="#FFFFFFFF" Offset="1"/>
            </LinearGradientBrush>
        </Border.Background>
        <ItemsPresenter/>
    </Border>
</ControlTemplate>

 

Again notice the ItemsPresenter in the template. This is what preserves the intended "multiple items" content model as prescribed by the ItemsControl, and automatically binds to and displays the content in the Items collection for the ListBox. The template when applied to a ListBox with a set of items, like below , produces the UI on the right.

<ListBox IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Margin="158,142,0,194" 
         Width="96" Template="{DynamicResource CTListBox}">
    <ListBoxItem Content="Item 1"/>
    <ListBoxItem Content="Item 2"/>
    <ListBoxItem Content="Item 3"/>
    <ListBoxItem Content="Item 4"/>
</ListBox>
image

 

So as you see, you should consider the intended content model for the control you are trying to template, and preserve that in your template. A complete discussion of all the various control content models is beyond the scope of this writing, but you can look in the .Net Framework SDK documentation and samples to find more details about the content model , its mechanics, and the set of rules that define  how the WPF runtime resolves how to render arbitrary content (for example a straight piece of text vs. a XAML element tree vs. an instance of some CLR type etc.). 

 

Binding to Parent Properties 

More often than not there may be a desire, or even a need, to not hard code property values in a control template, but let the control consumer specify those values. The control consumer's primary access is however to the control itself - so there needs to be a way of allowing the consumer's desired property settings flow to the template applied to the control. For example in the ListBox control template in the previous section, we see a Border control being used in the template, with its BorderBrush and BorderThickness properties set to specific hard coded values. The ListBox control itself however exposes the same properties. However the way the template is specified above, applying values to those properties on the ListBox control will have no effect on the template, and the ListBox will always show up with a red border of thickness 4 - no matter what.

This is where the TemplateBinding markup extension comes to the rescue. TemplateBinding is a kind of specialized data binding, that binds to the property values in the TemplatedParent i.e. the control instance to which the template is being applied. Take a look at the slightly modifed ListBox template below:

 

<ControlTemplate x:Key="CTListBox" TargetType="{x:Type ListBox}">
    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" 
                              CornerRadius="3,3,3,3" >
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFBCDEDC" Offset="0"/>
                <GradientStop Color="#FFFFFFFF" Offset="1"/>
            </LinearGradientBrush>
        </Border.Background>
        <ItemsPresenter/>
    </Border>
</ControlTemplate>

We are now setting the BorderBrush and BorderThickness as TemplateBindings to matching properties in the TemplatedParent.

 

As a result, the following two ListBox declarations will produce differing outputs :

 

<ListBox IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Margin="158,142,0,194"
BorderBrush="LightBlue" BorderThickness="7" Background="Black"
         Width="96" Template="{DynamicResource CTListBox}">
    <ListBoxItem Content="Item 1"/>
    <ListBoxItem Content="Item 2"/>
    <ListBoxItem Content="Item 3"/>
    <ListBoxItem Content="Item 4"/>
</ListBox>
 
image
<ListBox IsSynchronizedWithCurrentItem="True" HorizontalAlignment="Left" Margin="158,142,0,194"
BorderBrush="Gold" BorderThickness="2" Background="Blue"
         Width="96" Template="{DynamicResource CTListBox}">
    <ListBoxItem Content="Item 1"/>
    <ListBoxItem Content="Item 2"/>
    <ListBoxItem Content="Item 3"/>
    <ListBoxItem Content="Item 4"/>
</ListBox>
   
image

 

You may have noticed that in addition to setting the BorderBrush and BorderThickness properties on the ListBoxes, I also set the Background property value. But it does not have any impact on the ListBox, since in the template, the Background property for the Border element is hardcoded to a gradient.

 

If you look at the Button control template that we have been using in the sections above, you will find another example of TemplateBinding - the ContentPresenter uses TemplateBinding to bind to the Content property of the TemplatedParent (the Button instance in this case).

While in the examples above we have bound similar properties within the templates to the TemplatedParent i.e. BorderBrush to BorderBrush etc. there is no such rule mandating so. As long as the types match or there is a suitable TypeConverter, you can pretty much achieve any kind of binding, only limited by your sense of what is functionally sensible to do. 

 

Control Templates & Triggers

The Button control template that we have been working with before is pretty inert - when you apply it you do not see any of the usual animations that are associated with a button's default UI when a mouse moves over it, or it receives focus, or it is depressed. So let's add a trigger to the template above :

<ControlTemplate x:Key="CTGelButton" TargetType="{x:Type Button}">
    <Grid Width="100" Height="100">        
<!-- OMITTED FOR BREVITY -->
    </Grid>
    <ControlTemplate.Triggers> 
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BitmapEffect" TargetName="OuterEllipse">
                <Setter.Value>
                    <BitmapEffectGroup>
                        <OuterGlowBitmapEffect GlowColor="#FFFF2300" GlowSize="17" Noise="0"/>
                        <BevelBitmapEffect BevelWidth="8" EdgeProfile="BulgedUp" Smoothness="0.7"/>
                    </BitmapEffectGroup>
                </Setter.Value>
            </Setter>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

 

A Trigger is a WPF construct that allows you to declaratively specify a single or a set of changes to be applied to a specific element within scope, when a certain action happens. These driving actions could either be changes in the values of one or a set of properties or be the firing of an event. In other words using Triggers you can achieve the same effect that you would achieve in non WPF applications like Windows Forms by possibly coding sizeable event handlers.

In this case we are using a Property Trigger that fires when the value of the IsMouseOver property changes to True. When that happens, the <Setter> element in the trigger instructs that the BitmapEffect property of the target element OuterEllipse be set as defined in the <Setter.Value>. Unlike what you would possibly do in an event handler, you do not have to do anything to explicitly set the UI back to its original state when the IsMouseOver property changes back to False - the Trigger takes care of that. The resulting UI would look like below when the mouse moves over the Button instance:

image

 

Control Templates & Styles

Styles are convenient ways of grouping property settings together and applying them to more than one element. Since we apply control templates to controls using the Template property, we can use a Style to actually apply the control template, instead of directly setting the Template property on the control instance element. So we could do something like below:

<Style x:Key="StyleGelButton" TargetType="{x:Type Button}">    
    <!-- Other Property Setters ommitted for brevity -->
    <Setter Property="Template" Value="{DynamicResource CTGelButton}"/>
</Style>
<Button Content="Hello World" RenderTransformOrigin="0.625,2.55" Grid.Row="1" Margin="50,21,128,92" Style="{DynamicResource StyleGelButton}"/>

A style can be universally applied to all instances of a type. If you skip setting the x:Key property on the style above, and consequently skip setting the Style property on the Button, the style will be applied to all Buttons within scope. For control templating purposes, this is handy when you want to apply a certain template to all controls of a specific type.

Styles also come in handy when you need to apply templates to a control conditionally - say depending on the outcome of a property trigger. The snippet below defines a style that applies two different templates to the Button control depending on whether the Button is depressed or not.

<ControlTemplate x:Key="CTGelButton" TargetType="{x:Type Button}">
    <Grid Width="100" Height="100">
        <!-- OMITTED FOR BREVITY -->
</ControlTemplate>

<ControlTemplate x:Key="CTGelButtonPressed" TargetType="{x:Type Button}">
    <Grid Width="100" Height="100">
        <Ellipse Stroke="#FF000000" Fill="#FF1C46E7" x:Name="ellipse" StrokeThickness="5"/>
        <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}"/>
    </Grid>
</ControlTemplate>

<Style x:Key="StyleGelButton" TargetType="{x:Type Button}">    
    <Setter Property="Template" Value="{DynamicResource CTGelButton}"/>
    <Style.Triggers>
        <Trigger Property="IsPressed" Value="True">
            <Setter Property="Template" Value="{DynamicResource CTGelButtonPressed}"/>
        </Trigger>
    </Style.Triggers>
</Style>

Applying the style produces a different look & feel for when the button is depressed

<Button Content="Hello World" RenderTransformOrigin="0.625,2.55" Grid.Row="1" 
Margin="50,21,128,92" Style="{DynamicResource StyleGelButton}"/>
image

 

In Conclusion

While the examples above are fairly simplistic, I hope for those of you who have been trying to gain an initial understanding of control templates, the information above will provide a good starting point. In future posts I will  talk about some interesting ways to template the controls that come with WPF, as well as authoring your own controls to be templatable.

Posted by jitghosh | 24 Comments
Filed under: ,

New Drops for ASP.Net 3.5 Extensions CTP and Entity Framework Beta

Scott Guthrie announced a CTP release for ASP.Net Extensions available for public download. Lots of exciting features that you can play with, including the new ASP.Net MVC Framework, as well as the Entity Framework.

ASP.Net 3.5 Extensions CTP

MVC Toolkit Extras

Quick Starts

 

Also download the beta 3 build of Entity Framework Tools for Visual Studio 2008 RTM from here.

ADO.Net Entity Framework Tools Dec 07 Community Technology Preview

 

Enjoy!!

Posted by jitghosh | 1 Comments
More Posts Next page »
 
Page view tracker