Table of Contents
I don't know about you, but there are times when I wished that there was a way to add extra functionality to the OpenFileDialog
and SaveFileDialog
, but the existing framework stayed in the way of doing it. If you were using Windows Forms, then probably my previous article Extend OpenFileDialog and SaveFileDialog the easy way[^], could be a solution to this issue. But, if your application is designed to use Windows Presentation Foundation[^] introduced by Microsoft with .NET 3.0, this might not be the path you want to pursue. You can try hosting a Windows Presentation Foundation control in Windows Forms [^], but interoperability might pose some issues[^]. Even with an interop solution available, you might still want a pure WPF solution to avoid using the System.Windows.Forms.dll in order to keep the memory usage low, or just to keep the UI consistency.
Unlike Windows Forms, the WPF OpenFileDialog
and SaveFileDialog
work differently, and catching messages or grabbing handles before they display does not work in this scenario. So, the trick used for their Windows Forms counterparts to extend them cannot be applied for WPF, and I had to go for a very different alternative. I used the free Red Gate Reflector[^] to get the relevant code from PresentationFramework.dll and recreate the existing functionality in my own namespace. While trying to reverse engineer the OpenFileDialog
and SaveFileDialog
, I was forced to go two levels deep to the FileDialog
class because these classes override an abstract
method called RunFileDialog
with a parameter of the OPENFILENAME_I
type that is internal to the PresentationFramework
assembly only.
So, I ended up extracting a few more additional helper types used by my own FileDialog
type, the base class for the new OpenFileDialog
and SaveFileDialog
. Since I grabbed all the code from the version 3.5 of the assembly, be advised that some new features introduced by .NET 4.0 into the Microsoft.Win32.FileDialog
class like CustomPlaces [^] will not be available. Everything is packaged into the WpfCustomFileDialog
assembly under the same namespace.
To make things more interesting and to give you the feel of a real world application, I have used the OpenFileDialog
in conjunction with a media viewer based on Lee Brimelow's code[^], and SaveFileDialog
with a media encoder settings window that is merely a redesign of Armoghan Asif's work[^]. In order for the latter to work properly, you would have to download and install the now obsoleted Windows Media Encoder 9[^] if you want to have full functionality, and obviously the runtime for .NET 3.5[^] or later. However, the utilization of the media component or encoder will not be the focus of this article.
The new OpenFileDialog
and SaveFileDialog
I've created can only manipulate old style window handles, but existing WPF controls don't have one. That made the System.Windows.Window
a good candidate for a WPF child since it seems to be the only one to have an easily accessible handle represented as an IntPtr
type. There is a caveat, as you’ll see in my code snippets below because extra code is needed to deal with different runtime versions.
Fortunately, there is also a way to assign a Windows handle to a UserControl
that does not depend on the runtime version and that makes it the preferred design in my opinion. I'll elaborate more about it later in this article. In order to show the similarities of the two approaches, I've duplicated the same functionality on two tabs, one using a Window
and the other a UserControl
.
My new dialog type has to be aware of the window that will become one of its children and vice versa, so they can update their appearance when needed. To have some strong type checking when developing, I've created two interfaces to make it easier to enforce type safety. The old FileDialog
class has become the new FileDialogExt
, that looks like below:
public abstract partial class FileDialogExt<T>: Microsoft.Win32.CommonDialog,
IFileDlgExt where T : ContentControl, IWindowExt, new()
{
}
As you might have guessed, T
is the child type that will implement the IWindowExt
interface. It makes sense to try to abstract the common behavior that the UserControl
and Window
types are sharing and use the inheritance and generics constraints to enforce it.
Below, the interfaces are defined:
public interface IWindowExt
{
HwndSource Source
{
set;
}
IFileDlgExt ParentDlg
{
set;
}
}
public interface IFileDlgExt : IWin32Window
{
event PathChangedEventHandler EventFileNameChanged;
event PathChangedEventHandler EventFolderNameChanged;
event FilterChangedEventHandler EventFilterChanged;
AddonWindowLocation FileDlgStartLocation
{
set;
get;
}
string FileDlgOkCaption
{
set;
get;
}
bool FileDlgEnableOkBtn
{
set;
get;
}
bool FixedSize
{
set;
}
NativeMethods.FolderViewMode FileDlgDefaultViewMode
{
set;
get;
}
}
Once we start the dialog, it enters in a Windows loop as defined below:
protected override IntPtr HookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
{
IntPtr hres = IntPtr.Zero;
switch ((NativeMethods.Msg)msg)
{
case NativeMethods.Msg.WM_NOTIFY:
hres = ProcOnNotify(hwnd, lParam);
break;
case NativeMethods.Msg.WM_SHOWWINDOW:
InitControls();
ShowChild();
break;
case NativeMethods.Msg.WM_INITDIALOG:
_hwndFileDialog = NativeMethods.GetParent(new HandleRef(this, hwnd));
_hwndFileDialogEmbedded = hwnd;
NativeMethods.GetWindowRect(new HandleRef(this, _hwndFileDialog),
ref _OriginalRect);
break;
default:
if (msg == (int)MSG_POST_CREATION)
{
CustomPostCreation();
}
break;
}
return hres;
}
Here is the order for the relevant events:
- Firstly, the dialog's handle
_hwndFileDialog
will be saved to be used subsequently. - Secondly, when the dialog is about to be displayed,
WM_SHOWWINDOW
will trigger ShowChild()
that will create the child window. - Thirdly, the final adjustments are made to display it properly in
CustomPostCreation()
, which is triggered by a PostMessage
call from the ProcOnNotify()
method.
The ShowChild
method does most of the magic of establishing the layout between the dialog and its child, as displayed below:
private void ShowChild()
{
_childWnd = new T();
try
{
_childWnd.ParentDlg = this;
}
catch
{
return;
}
RECT dialogWindowRect = new RECT();
RECT dialogClientRect = new RECT();
Size size = new Size(dialogWindowRect.Width, dialogWindowRect.Height);
NativeMethods.GetClientRect(new HandleRef(this, _hwndFileDialog),
ref dialogClientRect);
NativeMethods.GetWindowRect(new HandleRef(this, _hwndFileDialog),
ref dialogWindowRect);
int dy = (int)(dialogWindowRect.Height - dialogClientRect.Height);
int dx = (int)(dialogWindowRect.Width - dialogClientRect.Width);
size = new Size(dialogWindowRect.Width, dialogWindowRect.Height);
if (_childWnd is Window)
{
Window wnd = _childWnd as Window;
wnd.WindowStyle = WindowStyle.None;
wnd.ResizeMode = ResizeMode.NoResize;
wnd.ShowInTaskbar = false;
wnd.WindowStartupLocation = WindowStartupLocation.Manual;
wnd.Left = -10000;
wnd.Top = -10000;
wnd.SourceInitialized += delegate(object sender, EventArgs e)
{
try
{
_source = System.Windows.PresentationSource.FromVisual(
_childWnd as Window) as HwndSource;
_source.AddHook(EmbededWndProc);
_childWnd.Source = _source;
}
catch{}
};
wnd.Show();
long styles = (long)NativeMethods.GetWindowLongPtr(new HandleRef(_childWnd,
_source.Handle), GWL.GWL_STYLE);
if (IntPtr.Size == 4)
{
styles |= System.Convert.ToInt64(NativeMethods.WindowStyles.WS_CHILD);
styles ^= System.Convert.ToInt64(NativeMethods.WindowStyles.WS_SYSMENU);
}
else
{
styles |= (long)NativeMethods.WindowStyles.WS_CHILD;
styles ^= (long)NativeMethods.WindowStyles.WS_SYSMENU;
}
NativeMethods.CriticalSetWindowLong(new HandleRef(this, _source.Handle),
(int)GWL.GWL_STYLE, new IntPtr(styles));
NativeMethods.SetParent(new HandleRef(_childWnd, _source.Handle),
new HandleRef(this, _hwndFileDialog));
}
else
{
ContentControl ctrl = _childWnd as ContentControl;
HwndSourceParameters parameters = new HwndSourceParameters("WPFDlgControl",
(int)ctrl.Width, (int)ctrl.Height);
parameters.WindowStyle = (int)NativeMethods.WindowStyles.WS_VISIBLE |
(int)NativeMethods.WindowStyles.WS_CHILD;
parameters.SetPosition((int)_OriginalRect.Width, (int)_OriginalRect.Height);
parameters.ParentWindow = _hwndFileDialog;
parameters.AdjustSizingForNonClientArea = false;
switch (this.FileDlgStartLocation)
{
case AddonWindowLocation.Right:
parameters.PositionX = (int)_OriginalRect.Width - dx/2;
parameters.PositionY = 0;
if (ctrl.Height < _OriginalRect.Height - dy)
ctrl.Height = parameters.Height = (int)_OriginalRect.Height - dy;
break;
case AddonWindowLocation.Bottom:
parameters.PositionX = 0;
parameters.PositionY = (int)(_OriginalRect.Height - dy +dx/2);
if (ctrl.Width < _OriginalRect.Width - dx)
ctrl.Width = parameters.Width = (int)_OriginalRect.Width - dx;
break;
case AddonWindowLocation.BottomRight:
parameters.PositionX = (int)_OriginalRect.Width - dx/2;
parameters.PositionY = (int)(_OriginalRect.Height - dy +dx/2);
break;
}
_source = new HwndSource(parameters);
_source.CompositionTarget.BackgroundColor =
System.Windows.Media.Colors.LightGray;
_source.RootVisual = _childWnd as System.Windows.Media.Visual;
_source.AddHook(new HwndSourceHook(EmbededCtrlProc));
}
switch (this.FileDlgStartLocation)
{
case AddonWindowLocation.Right:
size.Width = _OriginalRect.Width + _childWnd.Width;
size.Height = _OriginalRect.Height;
break;
case AddonWindowLocation.Bottom:
size.Width = _OriginalRect.Width;
size.Height = _OriginalRect.Height + _childWnd.Height;
break;
case AddonWindowLocation.BottomRight:
size.Height = _OriginalRect.Height + _childWnd.Height;
size.Width = _OriginalRect.Width + _childWnd.Width;
break;
}
NativeMethods.SetWindowPos(new HandleRef(this, _hwndFileDialog), new HandleRef(this,
(IntPtr)NativeMethods.ZOrderPos.HWND_BOTTOM),
0, 0, (int)size.Width, (int)size.Height,
NativeMethods.SetWindowPosFlags.SWP_NOZORDER);
}
When using a Window
, the above anonymous delegate captures the HwndSource
that will provide the handle and the message loop for it. If using a UserControl
, the HwndSource
is programmatically created and is associated with it, so we can have the Windows handle to work with. Finally, the CustomPostCreation
will do the final resizing to make it look like a real child.
private void CustomPostCreation()
{
_hListViewPtr = NativeMethods.GetDlgItem
(this._hwndFileDialog, (int)NativeMethods.ControlsId.DefaultView);
UpdateListView(_hListViewPtr);
if (_bFixedSize)
SetFixedSize(_hwndFileDialog);
RECT dialogWndRect = new RECT();
NativeMethods.GetWindowRect(new HandleRef(this, this._hwndFileDialog),
ref dialogWndRect);
RECT dialogClientRect = new RECT();
NativeMethods.GetClientRect(new HandleRef(this, this._hwndFileDialog),
ref dialogClientRect);
uint dx = dialogWndRect.Width - dialogClientRect.Width;
uint dy = dialogWndRect.Height - dialogClientRect.Height;
if (_childWnd is Window)
{
Window wnd = _childWnd as Window;
switch (FileDlgStartLocation)
{
case AddonWindowLocation.Bottom:
int left = (Environment.Version.Major >= 4)
? -(int)dx / 2 : dialogWndRect.left;
if (wnd.Width >= _OriginalRect.Width - dx)
{
NativeMethods.MoveWindow(new HandleRef(this, this._hwndFileDialog),
left, dialogWndRect.top, (int)(wnd.ActualWidth + dx / 2),
(int)(_OriginalRect.Height + wnd.ActualHeight), true);
}
else
{
NativeMethods.MoveWindow(new HandleRef(this, this._hwndFileDialog),
left, dialogWndRect.top, (int)(_OriginalRect.Width),
(int)(_OriginalRect.Height + wnd.ActualHeight), true);
wnd.Width = _OriginalRect.Width - dx / 2;
}
wnd.Left = 0;
wnd.Top = _OriginalRect.Height - dy + dx / 2;
break;
case AddonWindowLocation.Right:
int top = (Environment.Version.Major >= 4) ? (int)(dx / 2 - dy) :
dialogWndRect.top;
if (wnd.Height >= _OriginalRect.Height - dy)
NativeMethods.MoveWindow(new HandleRef(this, _hwndFileDialog),
(int)(dialogWndRect.left), top, (int)(_OriginalRect.Width +
wnd.ActualWidth), (int)(wnd.ActualHeight + dy - dx / 2), true);
else
{
NativeMethods.MoveWindow(new HandleRef(this, _hwndFileDialog),
(int)(dialogWndRect.left), top, (int)(_OriginalRect.Width +
wnd.ActualWidth), (int)(_OriginalRect.Height - dx / 2), true);
wnd.Height = _OriginalRect.Height - dy;
}
wnd.Top = 0;
wnd.Left = _OriginalRect.Width - dx / 2;
break;
case AddonWindowLocation.BottomRight:
NativeMethods.MoveWindow(new HandleRef(this, _hwndFileDialog),
dialogWndRect.left, dialogWndRect.top, (int)(_OriginalRect.Width +
wnd.Width), (int)(int)(_OriginalRect.Height + wnd.Height), true);
wnd.Top = _OriginalRect.Height - dy + dx / 2;
wnd.Left = _OriginalRect.Width - dx / 2;
break;
}
}
else
{
ContentControl ctrl = _childWnd as ContentControl;
const NativeMethods.SetWindowPosFlags flags =
NativeMethods.SetWindowPosFlags.SWP_NOZORDER |
NativeMethods.SetWindowPosFlags.SWP_NOMOVE;
SetWindowPosFlags.SWP_NOREPOSITION |
SetWindowPosFlags.SWP_ASYNCWINDOWPOS |
SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_DRAWFRAME;
switch (FileDlgStartLocation)
{
case AddonWindowLocation.Bottom:
NativeMethods.SetWindowPos(new HandleRef(this, this._hwndFileDialog),
new HandleRef(this,(IntPtr)ZOrderPos.HWND_BOTTOM),
dialogWndRect.left, dialogWndRect.top,
(int)(ctrl.ActualWidth + dx / 2),
(int)(_OriginalRect.Height + ctrl.ActualHeight), flags);
NativeMethods.SetWindowPos(new HandleRef(ctrl, _source.Handle),
new HandleRef(_source, (IntPtr)ZOrderPos.HWND_BOTTOM),
0, (int)(_OriginalRect.Height - dy + dx / 2),
(int)(ctrl.Width), (int)(ctrl.Height), flags);
break;
case AddonWindowLocation.Right:
NativeMethods.SetWindowPos(new HandleRef(this, this._hwndFileDialog),
new HandleRef(this, (IntPtr)ZOrderPos.HWND_BOTTOM),
(int)(dialogWndRect.left), dialogWndRect.top,
(int)(_OriginalRect.Width + ctrl.ActualWidth - dx / 2),
(int)(ctrl.ActualHeight + dy - dx / 2), flags);
NativeMethods.SetWindowPos(new HandleRef(ctrl, _source.Handle),
new HandleRef(_source, (IntPtr)ZOrderPos.HWND_BOTTOM),
(int)(_OriginalRect.Width - dx), (int)(0),
(int)(ctrl.Width), (int)(ctrl.Height), flags);
break;
case AddonWindowLocation.BottomRight:
NativeMethods.SetWindowPos(new HandleRef(this, this._hwndFileDialog),
new HandleRef(this, (IntPtr)ZOrderPos.HWND_BOTTOM),
dialogWndRect.left, dialogWndRect.top,
(int)(_OriginalRect.Width + ctrl.Width),
(int)(_OriginalRect.Height + ctrl.Height), flags);
break;
}
}
CenterDialogToScreen();
NativeMethods.InvalidateRect(new HandleRef(this, _source.Handle), IntPtr.Zero, true);
}
As you might expect, mixing WPF with Win32 comes with issues. This is the main reason why we needed the HwndSourceHook
delegate from the child window or the User Control. It provided not only a Win32 handle, but also a message loop to be used as a way to fix it:
IntPtr EmbededWndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
IntPtr hres = IntPtr.Zero;
const int DLGC_WANTALLKEYS = 0x0004;
switch ((NativeMethods.Msg)msg)
{
case NativeMethods.Msg.WM_SYSKEYDOWN:
SetChildStyle(true);
break;
case NativeMethods.Msg.WM_SYSKEYUP:
SetChildStyle(false);
break;
case NativeMethods.Msg.WM_GETDLGCODE:
if (lParam != IntPtr.Zero)
hres = (IntPtr)DLGC_WANTALLKEYS;
handled = true;
break;
}
return hres;
}
Without the fix shown above, the TextBox
will not accept most key strokes, and the Alt key won't work properly.
You will need one of the latest versions of Windows and .NET 4.0 to use Microsoft.Win32.FileDialog.CustomPlaces[^]. Since this is not included because I’ve based my code on the older .NET 3.5, I included my own implementation that can handle older versions too. The code for this feature is similar with the what I’ve done in my previous article Extend OpenFileDialog and SaveFileDialog the easy way[^]. The main difference is the fact that instead of using extension methods, I use plain data members in the FileDialogExt
class. The overridden FileDialog.RunDialog
calls the methods described below:
private readonly string TempKeyName = "TempPredefKey_" + Guid.NewGuid().ToString();
private const string Key_PlacesBar =
@"Software\Microsoft\Windows\CurrentVersion\Policies\ComDlg32\PlacesBar";
private RegistryKey _fakeKey;
private IntPtr _overriddenKey;
private object[] m_places;
public void SetPlaces(object[] places)
{
if (m_places == null)
m_places = new object[5];
else
m_places.Initialize();
if (places != null)
{
for (int i = 0; i < m_places.GetLength(0); i++)
{
m_places[i] = places[i];
}
}
}
public void ResetPlaces()
{
if (_overriddenKey != IntPtr.Zero)
{
ResetRegistry(_overriddenKey);
_overriddenKey = IntPtr.Zero;
}
if (_fakeKey != null)
{
_fakeKey.Close();
_fakeKey = null;
}
Registry.CurrentUser.DeleteSubKeyTree(TempKeyName);
m_places = null;
}
private void SetupFakeRegistryTree()
{
_fakeKey = Registry.CurrentUser.CreateSubKey(TempKeyName);
_overriddenKey = InitializeRegistry();
RegistryKey reg = Registry.CurrentUser.CreateSubKey(Key_PlacesBar);
for (int i = 0; i < m_places.GetLength(0); i++)
{
if (m_places[i] != null)
{
reg.SetValue("Place" + i.ToString(), m_places[i]);
}
}
}
readonly UIntPtr HKEY_CURRENT_USER = new UIntPtr(0x80000001u);
private IntPtr InitializeRegistry()
{
IntPtr hkMyCU;
NativeMethods.RegCreateKeyW(HKEY_CURRENT_USER, TempKeyName, out hkMyCU);
NativeMethods.RegOverridePredefKey(HKEY_CURRENT_USER, hkMyCU);
return hkMyCU;
}
private void ResetRegistry(IntPtr hkMyCU)
{
NativeMethods.RegOverridePredefKey(HKEY_CURRENT_USER, IntPtr.Zero);
NativeMethods.RegCloseKey(hkMyCU);
return;
}
You should only care about calling SetPlaces
which takes an argument as an array of up to five objects that can be numbers representing Windows special folders or strings as regular folders. For your convenience, I’ve included the Places
helper enumeration for predefined special folders.
In order to use these controls, you need to follow several steps:
- Add the
WpfCustomFileDialog
project to your solution and reference it in your project. If you don't like this way, as other alternatives, you could just reference this DLL or drop the code into your own project. - Create the child window or the user control, and implement the
IWindowExt
interface. - Set up the properties and events exposed by
IFileDlgExt
in your child window or externally. - Create the dialog using its constructor and the child window type as a generic parameter.
You have two choices to do that. The first choice is to implement the IWindowExt
directly in your class. You can implement the IWindowExt
interface by copying the code below:
System.Windows.Interop.HwndSource _source;
IFileDlgExt _parentDlg;
public System.Windows.Interop.HwndSource Source
{
set
{_source = value;}
}
public IFileDlgExt ParentDlg
{
set { _parentDlg = value; }
get { return _parentDlg; }
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (Height > 0 && Width > 0)
{
arrangeBounds.Height = this.Height;
arrangeBounds.Width = this.Width;
}
return base.ArrangeOverride(arrangeBounds);
}
While the virtual ArrangeOverride
is not part of the interface, it has to be overridden as shown above to avoid sizing issues.
You've probably noticed that the above functionality is a good candidate for a base class, hence the second choice is to inherit from existing classes I've created in the WpfCustomFileDialog
assembly.
They are WindowAddOnBase
and ControlAddOnBase
, and inheriting is just a plain class inheritance:
public partial class SelectWindow : WpfCustomFileDialog.WindowAddOnBase
{
.....
}
or:
public partial class SelectControl : WpfCustomFileDialog.ControlAddOnBase
{
.....
}
Unfortunately, WPF window inheritance is not supported by the Visual Studio designer, and you've got some manual typing to do[^] in the XAML file. My inherited class has to set the xmlns:src
attribute, and the SelectWindow.xaml file becomes:
<src:WindowAddOnBase x:Class="VideoManager.SelectWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfCustomFileDialog;assembly=WpfCustomFileDialog"
.......
</src:WindowAddOnBase>
The ControlAddOnBase
follows the same rules:
<src:ControlAddOnBase x:Class="VideoManager.SelectControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfCustomFileDialog;assembly=WpfCustomFileDialog"
.........
</src:ControlAddOnBase>
Fortunately, the Designer won't complain about it after you've built the assembly/project.
You can set up the properties and events from the caller, as shown below:
var ofd = new WpfCustomFileDialog.OpenFileDialog<SelectWindow>();
ofd.Filter = "avi files (*.avi)|*.avi|wmv files (*.wmv)|*.wmv|All files (*.*)|*.*";
ofd.Multiselect = false;
ofd.Title = "Select Media file";
ofd.FileDlgStartLocation = AddonWindowLocation.Right;
ofd.FileDlgDefaultViewMode = NativeMethods.FolderViewMode.Tiles;
ofd.FileDlgOkCaption = "&Select";
ofd.FileDlgEnableOkBtn = false;
ofd.SetPlaces(new object[] { @"c:\", (int)Places.MyComputer,
(int)Places.Favorites, (int)Places.All_Users_MyVideo, (int)Places.MyVideos });
bool? res = ofd.ShowDialog(this);
if (res.Value == true)
{
}
Using a User Control is almost identical to using a window, so I won't repeat the very similar code. You can also achieve the same thing from inside of your child window or control you've created.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_parentDlg.FileDlgDefaultViewMode = NativeMethods.FolderViewMode.List;
var fd = _parentDlg as SaveFileDialog<TargetWindow>;
fd.FileOk += new System.ComponentModel.CancelEventHandler(CreateEncodeInfo);
}
Since the whole reason of this article was to have interaction between the dialog and the WPF child, especially when using OpenFileDialog
, you should make use of the events to update both:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ParentDlg.EventFileNameChanged +=
new PathChangedEventHandler(ParentDlg_EventFileNameChanged);
ParentDlg.EventFolderNameChanged +=
new PathChangedEventHandler(ParentDlg_EventFolderNameChanged);
ParentDlg.EventFilterChanged +=
new FilterChangedEventHandler(ParentDlg_EventFilterChanged);
_comboSpeed.IsEnabled = false;
}
Be advised that ParentDlg
is null
in the constructor and it is initialized for the subsequent events only.
I have included solutions for Visual Studio 2008 (v9) and 2010 (v10). If you choose not to install Windows Media Encoder 9, you will still have some limited runtime functionality but the VS 2010 solution might not build. To fix it, you would have to add the existing Interop.WMEncoderLib.dll assembly as a reference to the VideoManager
project.
If Visual Studio starts complaining about weird errors while building or running, close the solution and delete all the bin and obj folders before trying again.
I have tested this project only on Windows XP SP3 32 bit and .NET 3.5 and 4.0. You might think that everything was pretty straightforward, but I can tell you that the development involved lots of trial and error phases or compromises. If you find ways to improve it like getting the relevant code from the latest version of the PresentationFramework.dll assembly, or make bug fixes, let me know and I'll try to have them in a future update.
If you are interested in Video encoding, you should probably consider replacing the existing deprecated Windows Media Encoder 9 encoder with the latest Expression Encoder[^]. While the code shown in the article is only C#, for the VB.NET folks, I have included the equivalent Visual Basic .NET solutions for Visual Studio 2008 and 2010 in the download source file.
- Version 1.0 The original
- Version 1.1 improves the child layout, suppresses the Window flash, and most importantly, adds support for the
UserControl
to be used as an extension - Version 1.2 adds my own
CustomPlaces
and fixes some incompatibilities with .NET 4.0 - Version 1.3 adds VS 2013 project style, an access warning for
CustomPlaces
and moved the latest source code on codeplex.