CodeProject, WPF

Alternative Item Backgrounds The .NET 3.5 SP1 Way

The .NET 3.5 SP1 is full of tricks, just today I found a new property called AlternationCount which is available on all the ItemControl controls such as ItemControl, ListBox etc etc.

This simply property can be used to provide some sort of alternative look based on the current value of the AlternationIndex. So you can color items or format them differently based on their index.

Here is a small example where I color the items, a different color based on their AlternationIndex value. The AlternationIndex  works hand in hand with the AlternationCount of the ItemsControl (or derived control), so in this example the ItemsControl AlternationCount is 3, which means we can have triggers on the AlternationIndex  for values 1/2 or 3.

Here is the XAML for a small example

   1:  <Window x:Class="WpfApplication1.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Window1" Height="300" Width="300">
   5:  
   6:      <Window.Resources>
   7:  
   8:          <DataTemplate x:Key="someStringItemsTemplate">
   9:  
  10:              <Border x:Name="bord" BorderBrush="Transparent"
  11:                      CornerRadius="0"
  12:                      HorizontalAlignment="Stretch"
  13:                      Background="#ff656565">
  14:                  <Label Content="{Binding}"
  15:                         HorizontalContentAlignment="Stretch"
  16:                         HorizontalAlignment="Stretch"
  17:                         Margin="0"
  18:                         Background="Transparent"/>
  19:  
  20:              </Border>
  21:              <DataTemplate.Triggers>
  22:                  <Trigger Property="ItemsControl.AlternationIndex"
  23:                           Value="1">
  24:                      <Setter TargetName="bord"
  25:                           Property="Background" Value="Yellow"/>
  26:                  </Trigger>
  27:                  <Trigger Property="ItemsControl.AlternationIndex"
  28:                           Value="2">
  29:                      <Setter TargetName="bord"
  30:                           Property="Background" Value="Orange"/>
  31:                  </Trigger>
  32:              </DataTemplate.Triggers>
  33:          </DataTemplate>
  34:  
  35:      </Window.Resources>
  36:  
  37:      <ItemsControl x:Name="someItems" AlternationCount="3"
  38:                        VerticalAlignment="Top"
  39:                        HorizontalAlignment="Stretch"
  40:                        Margin="0,0,0,0"
  41:                        ItemTemplate="{StaticResource 
  42:                          someStringItemsTemplate}">
  43:          <ItemsControl.ItemsPanel>
  44:              <ItemsPanelTemplate>
  45:                  <StackPanel Orientation="Vertical"/>
  46:              </ItemsPanelTemplate>
  47:          </ItemsControl.ItemsPanel>
  48:  
  49:      </ItemsControl>
  50:  </Window>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

And when run with a small set of items we get

image

Advertisements
WPF

Sonic : Out There In The Wild

A couple of posts back I blogged about a cool app that I have been working on called Sonic. Basically its a queryable media library that uses a semi custom IQueryProvider. It is also a good example of the MVVM pattern and custom templates in WPF.

image

I am totally happy with the results of this one, and I feel its a very cool article.

You can read more over at http://www.codeproject.com/KB/smart/Sonic.aspx

C#, CodeProject

A Little Chat About Code Sensible Code Generation

At work we all try and do our best to be productive. And as software developers we all probably have to author classes, that follow a familiar pattern. So if like me you are looking to work smart, you will undoubtedly look into code that writes code.

This is good in my opinion, code generation can save a LOT of time. But the cardinal rule of clever code generation is never ever having to touch the generated code once it has been generated. If you do have to make changes by hand to a generated file, its all wrong and the entire process can no longer be trusted.

Now this perplexed me for a while as I had a very real demand to perform some calculations when a property changes. Now this would be cool and could possibly be made part of the generated code if you know about it in advance and have the correct knowledge to instruct your code generator about this in advance. We neither knew about these requirements up front or were able to build enough intelligence into our code generator to deal with this sort of this.

So you can imagine that our business analyst came along and said “when property X changes, use property Y and Z to work out Q”. Mmmmm.

This would require me to do something when a property changes, this would mean manually changing my auto generated code. Oh no.

So I had a think about this, and luckily C# being the lovely language that it is has the answers. Partial classes / methods. All we need to do is provide the stubs in the generated code portion and provide hooks that the manually created partial class can use.

Here is an example.

The code generated part

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  
   6:  namespace NiceCodeGenStylee
   7:  {
   8:      public partial class Class1
   9:      {
  10:  
  11:          private Int32 number1 = 0;
  12:          private Int32 number2 = 0;
  13:  
  14:          /// <summary>
  15:          /// When this method in called the hand cranked 
  16:          /// code should have the implementation
  17:          /// </summary>
  18:          partial void Number1PropertyChanging(
                            IntReportChanges args);
  19:  
  20:          /// <summary>
  21:          /// When this method in called the hand cranked 
  22:          /// code should have the implementation
  23:          /// </summary>
  24:          partial void Number1CodePropertyChanged(
                            Int32ItemChanges args);
  25:  
  26:  
  27:          /// <summary>
  28:          /// When this method in called the hand cranked 
  29:          /// code should have the implementation
  30:          /// </summary>
  31:          partial void Number2PropertyChanging(
                            IntReportChanges args);
  32:  
  33:          /// <summary>
  34:          /// When this method in called the hand cranked 
  35:          /// code should have the implementation
  36:          /// </summary>
  37:          partial void Number2CodePropertyChanged(
                            Int32ItemChanges args);
  38:  
  39:  
  40:          /// <summary>
  41:          /// Number1 property
  42:          /// </summary>
  43:          public Int32 Number1
  44:          {
  45:              get { return number1; }
  46:              set
  47:              {
  48:                  //is it different
  49:                  if(value != number1)
  50:                  {
  51:                      Int32 oldValue = number1;
  52:  
  53:                      //what are the changes
  54:                      Int32ItemChanges possibleValueChanges
  55:                          = new Int32ItemChanges(
                                      oldValue, value);
  56:  
  57:                      //report the possible change, allows non generated file to 
  58:                      //respond to the change, or cancel it
  59:                      IntReportChanges changes =
  60:                          new IntReportChanges
  61:                          {
  62:                              Values = possibleValueChanges
  63:                          };
  64:                      Number1PropertyChanging(changes);
  65:  
  66:                      //if there was no cancellation
  67:                      if (!changes.Cancel)
  68:                      {
  69:                          number1 = value;
  70:                          //report the change, allows non generated file to 
  71:                          //respond to the change
  72:                          Number1CodePropertyChanged(
                                   possibleValueChanges);
  73:                      }
  74:                  }
  75:  
  76:              }
  77:          }
  78:  
  79:          /// <summary>
  80:          /// Number2 property
  81:          /// </summary>
  82:          public Int32 Number2
  83:          {
  84:              get { return number2; }
  85:              set
  86:              {
  87:                  //is it different
  88:                  if (value != number2)
  89:                  {
  90:                      Int32 oldValue = number2;
  91:  
  92:                      //what are the changes
  93:                      Int32ItemChanges possibleValueChanges
  94:                          = new Int32ItemChanges(oldValue, value);
  95:  
  96:                      //report the possible change, allows non generated file to 
  97:                      //respond to the change, or cancel it
  98:                      IntReportChanges changes =
  99:                          new IntReportChanges
 100:                          {
 101:                              Values = possibleValueChanges
 102:                          };
 103:                      Number1PropertyChanging(changes);
 104:  
 105:                      //if there was no cancellation
 106:                      if (!changes.Cancel)
 107:                      {
 108:                          number2 = value;
 109:                          //report the change, allows non generated file to 
 110:                          //respond to the change
 111:                          Number1CodePropertyChanged(
                                   possibleValueChanges);
 112:                      }
 113:                  }
 114:  
 115:              }
 116:          }
 117:  
 118:  
 119:      }
 120:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }Where the manually created code part, can use these partial method stubs to do calculations when a property changes or even cancel a changing property based on some condition.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  
   6:  namespace NiceCodeGenStylee
   7:  {
   8:      partial class Class1
   9:      {
  10:          /// <summary>
  11:          /// When this method in called the hand cranked 
  12:          /// code should have the implementation
  13:          /// </summary>
  14:          partial void Number2PropertyChanging(
                   IntReportChanges args)
  15:          {
  16:              if (args.Values.NewValue == 0)
  17:              {
  18:                  Console.WriteLine(
  19:                      "Number2 is invalid value, cancelling");
  20:                  args.Cancel = true;
  21:              }
  22:          }
  23:  
  24:          /// <summary>
  25:          /// When this method in called the hand cranked 
  26:          /// code should have the implementation
  27:          /// </summary>
  28:          partial void Number2CodePropertyChanged(
                   Int32ItemChanges args)
  29:          {
  30:              Console.WriteLine(String.Format(
  31:                  "Number2 changed to {0}, doing calcs",
                       args.NewValue));
  32:              DoNumberCalcs();
  33:          }
  34:  
  35:  
  36:          /// <summary>
  37:          /// When this method in called the hand cranked 
  38:          /// code should have the implementation
  39:          /// </summary>
  40:          partial void Number1PropertyChanging(
                   IntReportChanges args)
  41:          {
  42:              if (args.Values.NewValue == 0)
  43:              {
  44:                  Console.WriteLine(
  45:                      "Number1 is invalid value, cancelling");
  46:                  args.Cancel = true;
  47:              }
  48:          }
  49:  
  50:          /// <summary>
  51:          /// When this method in called the hand cranked 
  52:          /// code should have the implementation
  53:          /// </summary>
  54:          partial void Number1CodePropertyChanged(
                   Int32ItemChanges args)
  55:          {
  56:              Console.WriteLine(String.Format(
  57:                  "Number1 changed to {0}, doing calcs",
                       args.NewValue));
  58:              DoNumberCalcs();
  59:          }
  60:  
  61:  
  62:          private void DoNumberCalcs()
  63:          {
  64:              Console.WriteLine(
  65:                  String.Format("Number1 ({0}) + Number2 ({1}) ={2}",
  66:                      number1, number2, number1 + number2));
  67:          }
  68:      }
  69:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }Where these little helper classes are used

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  
   6:  namespace NiceCodeGenStylee
   7:  {
   8:      struct Int32ItemChanges
   9:      {
  10:          public readonly Int32 OldValue;
  11:          public readonly Int32 NewValue;
  12:  
  13:          public Int32ItemChanges(
                   Int32 oldValue, Int32 newValue)
  14:          {
  15:              OldValue = oldValue;
  16:              NewValue = newValue;
  17:          }
  18:      }
  19:  
  20:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  
   6:  namespace NiceCodeGenStylee
   7:  {
   8:      class IntReportChanges
   9:      {
  10:          public Int32ItemChanges Values { get; set; }
  11:          public Boolean Cancel { get; set; }
  12:      }
  13:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is a demo of it running

image

See it all works lovely.

Here is a small demo project for your enjoyment : nicecodegenstylee.zip

WPF

WPF : Sonic is coming soon

I have been working on a rather large and pretty involved article for quite some time now, ok I have pushed out a few smaller articles, but this big one has been my love for the last n-many weeks. I am calling the article "Sonic".

Today I finally finished the code, and I have to say I think this article (which I hope to get published by next week), is my best work to date.

Basically when I started the article I wanted to do something a little advanced with LINQ and I knew I wanted to get a better understanding of IQueryProvider which is what LINQ to SQL uses internally.

I did not actually want to create a whole IQueryProvider implementation (I am not mental), but I did want to delve a little deeper into the inner bowels of LINQ.

So I thought I know, I’ll write myself a little MP3 application that allows the users to specify the users music location(s) (Via App.Config) and then use some funky LINQ/IQueryProvider magic to work out what MP3s are available, and the use the ID3 tag information to store the file metadata in SQL. The metadata could then be used to search against again using more LINQ/IQueryProvider magic. I also thought why not just work a MP3 player in there and also spend a little bit of time ensuring that the application is a good example of WPF best practices, such as MVVM / Commands etc etc.

I have to say now that I am finished, I am absolutely stoked with the results. I will have the article up as soon as humanly possible, but for now here is the sort of things that you can expect from the "Sonic" article :

  • Stylish UI
  • Custom window Style
  • LINQ Love
  • IQueryProvider
  • LINQ to SQL
  • MVVM, including showing you how to use MVVM to trigger animations
  • 3D
  • Fancy user controls, some of which are useful standalone

And here are some screen shots, just to wet your appetite:

 

image

Sonic : scanning/working on your request

image

Sonic : 1/2 way through animating in the matched albums for the query (which is really an Expression tree that is used in conjunction with the IQueryProvider)

image

Sonic : 1/2 way through animating in the matched tracks for the currently clicked Album (albums are the bottom row)

image

Sonic : loading the album art.

 

I will do my best to write the article text as soon as I can and get it up.

CodeProject, WPF

WPF : Binding to global App properties

I was working on something for an upcoming article that I am writing where I am messing around with 3D, and I was having a small issue with a VisualBrush (something to do with Freezable, I never did quite figure it out and came up with a better solution), but along the way of trying to figure that out, one of the ideas I had was to use a globally available Application property and Bind to that. This is something I had not done before. Luckily it is very simple.

The only thing that you have to be aware of is that you can not use the normal DP GetValue() SetValue() syntax in Application inheriting objects, as these are added further up the inheritance tree (DependencyObject) where as Application inherits from DispatcherObject, so does not have these methods.

So we must simply use CLR properties.

Here is a small example.

My App class looks like this

   1:  public partial class App : Application
   2:  {
   3:      private String someText="default";
   4:  
   5:      public String SomeText
   6:      {
   7:          get { return this.someText; }
   8:          set { this.someText = value; }
   9:      }
  10:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And I have a small XAML Window which wants to bind to one of the App properties, we simply use the following Binding, note the x:Static markup extension.

   1:  <Window x:Class="BindingToApp.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Window1" Height="300" Width="300">
   5:  
   6:      <StackPanel Orientation="Vertical">
   7:          <TextBlock Margin="5" TextWrapping="Wrap"
   8:                     Text="Shows how to bind to App level properties, 
   9:                     type below and then click button"/>
  10:          <TextBox x:Name="txt" Margin="5" >
  11:              <TextBox.Text>
  12:                  <Binding Path="SomeText"
  13:                           Source="{x:Static Application.Current}"/>
  14:              </TextBox.Text>
  15:          </TextBox>
  16:          <Button x:Name="BtnShowAppSetting" Margin="5"
  17:                  Content="What is App Value Now"
  18:                  Click="BtnShowAppSetting_Click"/>
  19:      </StackPanel>
  20:  
  21:  </Window>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is the code behind

   1:      public partial class Window1 : Window
   2:      {
   3:          public Window1()
   4:          {
   5:              InitializeComponent();
   6:          }
   7:  
   8:          private void BtnShowAppSetting_Click(object sender, RoutedEventArgs e)
   9:          {
  10:              MessageBox.Show(String.Format("App value for SomeText property is {0}",
  11:                  (Application.Current as App).SomeText));
  12:          }
  13:      }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is the result when run

props

Here is a link to this demo project in case you need to try it : bindingtoapp.zip

Along the way I also found a very interesting post that illustrated how to bind directly to the Settings files that can be added to a VS project. That is pretty useful for persisting user specific data. Here is a link to that article

http://dedjo.blogspot.com/2008/04/quick-wpf-tip-how-to-bind-to-wpf.html

Enjoy

CodeProject, WPF

WPF : Circular Progress Bar

Ever wanted a slightly different progress bar from the standard WPF Progress bar which looks like the following (IsIndeterminate is like the WinForms Marquee enum value) :

   1:  <ProgressBar VerticalAlignment="Top"
   2:      Height="22" IsIndeterminate="True"/>
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }

image

What I would like is one that is more like the ones you see all over the web, where we have a round wheel sort of progress indicator. Would you like one of those?

Well fear not your search is over, here is a very simple idea, just arrange some Ellipses in a circle within a Canvas and do a constant Rotate StoryBoard and bam, a circular progress bar.

Here is the XAML for such a UserControl.

 
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }

   1:  <UserControl x:Class="Sonic.CircularProgressBar"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Height="120" Width="120" Background="Transparent">
   5:      <Grid x:Name="LayoutRoot" Background="Transparent"
   6:            HorizontalAlignment="Center" VerticalAlignment="Center">
   7:          <Grid.RenderTransform>
   8:              <ScaleTransform x:Name="SpinnerScale"
   9:                              ScaleX="1.0" ScaleY="1.0" />
  10:          </Grid.RenderTransform>
  11:          <Canvas RenderTransformOrigin="0.5,0.5"
  12:                  HorizontalAlignment="Center"
  13:                  VerticalAlignment="Center"
  14:                  Width="120" Height="120" >
  15:              <Ellipse Width="21.835" Height="21.862"
  16:                       Canvas.Left="20.1696"
  17:                       Canvas.Top="9.76358"
  18:                       Stretch="Fill" Fill="Orange"
  19:                       Opacity="1.0"/>
  20:              <Ellipse Width="21.835" Height="21.862"
  21:                       Canvas.Left="2.86816"
  22:                       Canvas.Top="29.9581" Stretch="Fill"
  23:                       Fill="Black" Opacity="0.9"/>
  24:              <Ellipse Width="21.835" Height="21.862"
  25:                       Canvas.Left="5.03758e-006"
  26:                       Canvas.Top="57.9341" Stretch="Fill"
  27:                       Fill="Black" Opacity="0.8"/>
  28:              <Ellipse Width="21.835" Height="21.862"
  29:                       Canvas.Left="12.1203"
  30:                       Canvas.Top="83.3163" Stretch="Fill"
  31:                       Fill="Black" Opacity="0.7"/>
  32:              <Ellipse Width="21.835" Height="21.862"
  33:                       Canvas.Left="36.5459"
  34:                       Canvas.Top="98.138" Stretch="Fill"
  35:                       Fill="Black" Opacity="0.6"/>
  36:              <Ellipse Width="21.835" Height="21.862"
  37:                       Canvas.Left="64.6723"
  38:                       Canvas.Top="96.8411" Stretch="Fill"
  39:                       Fill="Black" Opacity="0.5"/>
  40:              <Ellipse Width="21.835" Height="21.862"
  41:                       Canvas.Left="87.6176"
  42:                       Canvas.Top="81.2783" Stretch="Fill"
  43:                       Fill="Black" Opacity="0.4"/>
  44:              <Ellipse Width="21.835" Height="21.862"
  45:                       Canvas.Left="98.165"
  46:                       Canvas.Top="54.414" Stretch="Fill"
  47:                       Fill="Black" Opacity="0.3"/>
  48:              <Ellipse Width="21.835" Height="21.862"
  49:                       Canvas.Left="92.9838"
  50:                       Canvas.Top="26.9938" Stretch="Fill"
  51:                       Fill="Black" Opacity="0.2"/>
  52:              <Ellipse Width="21.835" Height="21.862"
  53:                       Canvas.Left="47.2783"
  54:                       Canvas.Top="0.5" Stretch="Fill"
  55:                       Fill="Black" Opacity="0.1"/>
  56:              <Canvas.RenderTransform>
  57:                  <RotateTransform x:Name="SpinnerRotate"
  58:                                   Angle="0" />
  59:              </Canvas.RenderTransform>
  60:              <Canvas.Triggers>
  61:                  <EventTrigger RoutedEvent="ContentControl.Loaded">
  62:                      <BeginStoryboard>
  63:                          <Storyboard>
  64:                              <DoubleAnimation
  65:                                  Storyboard.TargetName
  66:                                      ="SpinnerRotate"
  67:                                   Storyboard.TargetProperty
  68:                                      ="(RotateTransform.Angle)"
  69:                                   From="0" To="360"
  70:                                   Duration="0:0:01"
  71:                                   RepeatBehavior="Forever" />
  72:                          </Storyboard>
  73:                      </BeginStoryboard>
  74:                  </EventTrigger>
  75:              </Canvas.Triggers>
  76:          </Canvas>
  77:      </Grid>
  78:  </UserControl>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is all the C# codebehind

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows;
   6:  using System.Windows.Controls;
   7:  using System.Windows.Data;
   8:  using System.Windows.Documents;
   9:  using System.Windows.Input;
  10:  using System.Windows.Media;
  11:  using System.Windows.Media.Animation;
  12:  using System.Windows.Media.Imaging;
  13:  using System.Windows.Navigation;
  14:  using System.Windows.Shapes;
  15:  
  16:  namespace Sonic
  17:  {
  18:      /// <summary>
  19:      /// Provides a circular progress bar
  20:      /// </summary>
  21:      public partial class CircularProgressBar : UserControl
  22:      {
  23:          public CircularProgressBar()
  24:          {
  25:              InitializeComponent();
  26:  
  27:              //Use a default Animation Framerate of 20, which uses less CPU time
  28:              //than the standard 50 which you get out of the box
  29:              Timeline.DesiredFrameRateProperty.OverrideMetadata(
  30:                  typeof(Timeline),
  31:                      new FrameworkPropertyMetadata { DefaultValue = 20 } );
  32:          }
  33:      }
  34:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is an example of one of these CircularProgressBar  controls in use:

   1:  <Window
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          xmlns:local="clr-namespace:Sonic"
   5:      x:Class="Sonic.MainWindow">
   6:  
   7:  
   8:      <Grid>
   9:              <local:MediaView/>
  10:          </Grid>
  11:  
  12:  </Window>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

And here it is in action (obviously it looks better in run time)….Enjoy

image

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And for those that are interested here is a little demo project, progress.zip