Skip to main content

Pragmatic TSQL Programmer

Go Search
Home
  

Pragmatic TSQL Programmer > Categories
Directory Size Utility
A few weeks ago, I fooled around for a few hours to make an app that gives a decent visualization of where all the space on a drive has gone.
 
I did it as an example while I was teaching WPF to a couple of new developers, and we played with lots of things like multi-threading and databinding and it was a great lesson.
 
Since then, I've been using it over and over, and have had several people ask for a copy, so I am making available the source code and the executable.  If you just want to run it, all you need is the .exe.  If you want to play with the source code, it is there as well.
 
My animated .gif control for WPF

Since WPF 1.0 dropped the ball on supporting animated .gif files, I spent some time assembling various ideas that I found on the MSDN WPF Forums into a nice custom control with a few unique features. Firstly, my control allows you to use a .gif that you have compiled as an Embedded Resource in your project, and secondly, my control allows you to pause/unpause the animation by clicking on the .gif. You can turn this second feature off by setting AllowClickToPause to false, but it is on by default. Other than that, it inherits from the Image control and should act just like you would expect a WPF control to act like.

You may download the full source code, and a demo app if you would like. (Props to Wikipedia for the Stirling Engine animation I used for the demo.)

Here is the code I used: (As you will see I began with the code from Sergey Ukraine's posts)

using System;

using System.Drawing;

using System.IO;

using System.Reflection;

using System.Runtime.InteropServices;

using System.Windows;

using System.Windows.Input;

using System.Windows.Interop;

using System.Windows.Media.Imaging;

using System.Windows.Threading;

using Image=System.Windows.Controls.Image;

 

namespace UI

{

public class GIFImageControl : Image

{

public static readonly DependencyProperty AllowClickToPauseProperty =

DependencyProperty.Register("AllowClickToPause", typeof (bool), typeof (GIFImageControl),

new UIPropertyMetadata(true));

 

public static readonly DependencyProperty GIFSourceProperty =

DependencyProperty.Register("GIFSource", typeof (string), typeof (GIFImageControl),

new UIPropertyMetadata("", GIFSource_Changed));

 

public static readonly DependencyProperty PlayAnimationProperty =

DependencyProperty.Register("PlayAnimation", typeof (bool), typeof (GIFImageControl),

new UIPropertyMetadata(true, PlayAnimation_Changed));

 

private Bitmap _Bitmap;

 

private bool _mouseClickStarted;

 

public GIFImageControl()

{

MouseLeftButtonDown += GIFImageControl_MouseLeftButtonDown;

MouseLeftButtonUp += GIFImageControl_MouseLeftButtonUp;

MouseLeave += GIFImageControl_MouseLeave;

Click += GIFImageControl_Click;

//TODO:Future feature: Add a Play/Pause graphic on mouse over, and possibly a context menu

}

 

public bool AllowClickToPause

{

get { return (bool) GetValue(AllowClickToPauseProperty); }

set { SetValue(AllowClickToPauseProperty, value); }

}

 

public bool PlayAnimation

{

get { return (bool) GetValue(PlayAnimationProperty); }

set { SetValue(PlayAnimationProperty, value); }

}

 

public string GIFSource

{

get { return (string) GetValue(GIFSourceProperty); }

set { SetValue(GIFSourceProperty, value); }

}

 

private void GIFImageControl_Click(object sender, RoutedEventArgs e)

{

if (AllowClickToPause)

PlayAnimation = !PlayAnimation;

}

 

private void GIFImageControl_MouseLeave(object sender, MouseEventArgs e)

{

_mouseClickStarted = false;

}

 

private void GIFImageControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

{

if (_mouseClickStarted)

FireClickEvent(sender, e);

_mouseClickStarted = false;

}

 

private void GIFImageControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

{

_mouseClickStarted = true;

}

 

private void FireClickEvent(object sender, RoutedEventArgs e)

{

if (null != Click)

Click(sender, e);

}

 

public event RoutedEventHandler Click;

 

private static void PlayAnimation_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

var gic = (GIFImageControl) d;

if ((bool) e.NewValue)

{

//StartAnimation if GIFSource is properly set

if (null != gic._Bitmap)

ImageAnimator.Animate(gic._Bitmap, gic.OnFrameChanged);

}

else

//Pause Animation

ImageAnimator.StopAnimate(gic._Bitmap, gic.OnFrameChanged);

}

 

 

private void SetImageGIFSource()

{

if (null != _Bitmap)

{

ImageAnimator.StopAnimate(_Bitmap, OnFrameChanged);

_Bitmap = null;

}

if (String.IsNullOrEmpty(GIFSource))

{

//Turn off if GIF set to null or empty

Source = null;

InvalidateVisual();

return;

}

if (File.Exists(GIFSource))

_Bitmap = (Bitmap) System.Drawing.Image.FromFile(GIFSource);

else

{

//Support looking for embedded resources

Assembly assemblyToSearch = Assembly.GetAssembly(GetType());

_Bitmap = GetBitmapResourceFromAssembly(assemblyToSearch);

if (null == _Bitmap)

{

assemblyToSearch = Assembly.GetCallingAssembly();

_Bitmap = GetBitmapResourceFromAssembly(assemblyToSearch);

if (null == _Bitmap)

{

assemblyToSearch = Assembly.GetEntryAssembly();

_Bitmap = GetBitmapResourceFromAssembly(assemblyToSearch);

if (null == _Bitmap)

throw new FileNotFoundException("GIF Source was not found.", GIFSource);

}

}

}

if (PlayAnimation)

ImageAnimator.Animate(_Bitmap, OnFrameChanged);

}

 

private Bitmap GetBitmapResourceFromAssembly(Assembly assemblyToSearch)

{

string[] resourselist = assemblyToSearch.GetManifestResourceNames();

if (null != assemblyToSearch.FullName)

{

string searchName = String.Format("{0}.{1}", assemblyToSearch.FullName.Split(',')[0], GIFSource);

if (resourselist.Contains(searchName))

{

Stream bitmapStream = assemblyToSearch.GetManifestResourceStream(searchName);

if (null != bitmapStream)

return (Bitmap) System.Drawing.Image.FromStream(bitmapStream);

}

}

return null;

}

 

private static void GIFSource_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)

{

((GIFImageControl) d).SetImageGIFSource();

}

 

 

private void OnFrameChanged(object sender, EventArgs e)

{

Dispatcher.BeginInvoke(DispatcherPriority.Normal,

new OnFrameChangedDelegate(OnFrameChangedInMainThread));

}

 

private void OnFrameChangedInMainThread()

{

if (PlayAnimation)

{

ImageAnimator.UpdateFrames(_Bitmap);

Source = GetBitmapSource(_Bitmap);

InvalidateVisual();

}

}

 

[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]

public static extern IntPtr DeleteObject(IntPtr hDc);

 

private static BitmapSource GetBitmapSource(Bitmap gdiBitmap)

{

IntPtr hBitmap = gdiBitmap.GetHbitmap();

BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap,

IntPtr.Zero,

Int32Rect.Empty,

BitmapSizeOptions.FromEmptyOptions());

DeleteObject(hBitmap);

return bitmapSource;

}

 

private delegate void OnFrameChangedDelegate();

}

}

WPF - The visualization tool of the future
I fell bad that I haven't posted a lot of what had been on my mind lately, and i wanted to share what is going on.

In my mind, I am realizing that there is a new technology that I believe is going to be making a BIG, BIG impact on developers who work with lots of data: WPF. Over the last 18 months or so, I've been experimenting with WPF, and have created several major applications at work using WPF. And, the more I use it, the more I realize that this is really the future of UI work, especially for the people, like me, who work with lots of data heavy apps.

WPF had a pretty rough start. The initial demos of the technology highlighted ridiculous and worthless things like spinning buttons and the WPF support in Visual Studio is buggy and far behind the tool support for things like WinForms. It didn't even have basic controls like a non-beta rich data grid until October 2008.

But, despite all of the warts that the "1.0" version of WPF has, it is a technology that is very easy to fall in love with. I will begin elaborating on why in future posts.

I am currently writing my first big developer talk on how WPF and SQL can go hand in hand. I'll be presenting it in April to the Cedar Falls user group and then again at the Iowa Code Camp in May.

Most importantly for this blog, I have added a new category for my thoughts on WPF, and you can expect to see good content here soon.