C# 中使用 Microsoft.DirectX.AudioVideoPlayback 实现异步进度





0/5 (0投票)
自定义窗口控件,模仿 Windows 7 的调暗和异步 Aero 进度动画。

引言
最近,我需要一个带复选框
的treeview
,以便最终用户能够选择文件夹和文件,并显示与之关联的“windows”图标。由于枚举目录可能会很麻烦,我还想在treeview
上显示一个 aero 风格的指示器,而不会禁用整个窗体。
背景
我绝不是天才,而且我发现我年纪越大,记忆力就越差。我经常在 CodeProject 上搜索一些零碎的知识来帮助我解决正在处理的问题,或者回忆起我做过但已经忘记的事情。自从我于 2006 年成为会员以来,我想是时候回馈一些东西了!这并不是我正在进行工作的完整实现,但它是作为我的其他控件模板而创建的控件之一。
在搜索 CodeProject 时,我找到了 Paul Ingles 提交的文章 使用 C# 中的 SHGetFileInfo 获取(并管理)文件和文件夹图标,这是一篇关于获取“windows”图标的非常好的文章。我还找到了 Nathan B. Evans 提交的文章 WinForms 的 AJAX 风格异步进度对话框,这是一篇关于在 Windows 窗体上实现 aero 风格指示器的非常好的文章。我强烈建议您查阅这两篇文章。
虽然我喜欢 Nathan 在他的动画方面所做的工作,但我希望在控件中使用预先构建的.gif图像,而不是覆盖整个窗体,并且我希望能够使用我在其他地方使用过的相同动画。当启动后台进程时,在PictureBox
控件中实现动画.gif是徒劳的。这促使我使用.avi,就像 Windows 所做的那样,因此我使用了我之前在其他解决方案中使用过的Microsoft.DirectX.AudioVideoPlayback
。
我需要解决的第一个问题是将动画.gif转换为.avi。为此,我使用了Animated GIF Banner Maker,这是一款用于从图形文件制作动画 GIF 横幅文件的软件。它支持以下图像格式:*.bmp、*.gif、*.jpg、*.tga、*.png、*.ico、*.wmf、*.emf、*.sgi、*.dib、*.icb、*.pcx、*.pcd、*.psp、*.pbm、*.pgm、*.psd、*.ppm、*.psg、*.vst、*.vda、*.tif、*.wbmp、*.rle。

这个工具的优点是它有一个将动画 GIF 转换为 AVI 的实用程序,我用它创建了包含在源代码中的 AVI。这是他们产品网站的链接,Animated GIF Banner Maker,您可以在那里下载试用版。您不需要购买它就可以运行 GIF 到 AVI 的转换实用程序,但是,如果您需要创建动画 GIF...可以尝试试用版!
实现这一切需要三个控件
- AviPlayerEx.cs。处理播放、停止和重置
Microsoft.DirectX.AudioVideoPlayback
的所有逻辑。 - ImageListsEx.cs。处理从 API 调用返回的 Windows 图标的检索和缓存的所有逻辑。
我按如下方式使用了 Nathan 的代码(Nathan 的版权包含在源代码中)- 我使用了他AsyncBaseDialog.cs类中
BeginAsyncIndication
方法的快照逻辑来创建Gdi32.GetSnapshot()
方法。我在Gdi32.cs文件中创建了这个方法,以便我可以将此逻辑用于我创建的其他控件。由于我不希望对整个窗体产生效果,因此我创建了TreeViewEx
控件,其中包含TreeView
和PictureBox
。该方法接受两个参数,“源”控件(在此例中为TreeViewEx.TreeView
控件,“ctlTreeView
”)和“目标”控件(在此例中为TreeViewEx.PictureBox
控件“ctlSnapShot
”)。 - 我将
FillBrush
的Opacity
从225
更改为128
,使其更具透明度。 - 我已从使用
Panel
切换到在TreeViewEx
控件中使用PictureBox
。TreeViewEx.PictureBox
比TreeViewEx.TreeView
小 2x2 像素,并且相对于其位置偏移 1x1 像素。当我将快照图形显示在TreeViewEx.PictureBox
中时,TreeViewEx.TreeView
会显得被禁用而没有移动,保留了TreeViewEx.TreeView
的边框。TreeViewEx.PictureBox
还能阻止用户在处理完成之前对TreeViewEx.TreeView
执行任何其他操作,并能干净地隐藏处理TreeViewEx.TreeView
时发生的任何闪烁。 - 我也没有使用他的
Blur
或Grayscale
方法,因此我从我的方法中删除了相关逻辑。
- 我使用了他AsyncBaseDialog.cs类中
- TreeViewEx.cs。使用其他控件来存储图标和显示进度,处理枚举驱动器、文件夹和文件的所有逻辑。
我按如下方式使用了 Paul 的代码- 我将 Paul 的代码从两个类合并到了我的ImageListsEx.cs控件类中。虽然在这个演示中我只使用了
SmallImageList
,但通过简单地用IconSize.Multiple
初始化控件,并将ListView
设置为使用SmallImageList
和LargeImageList
属性,我就可以为ListView
使用相同的ImageListsEx
控件。 - 我修改了
GetFolderIcon()
以使用文件夹路径而不是“null
”,这样我就可以获取 Windows 可能正在为文件夹使用的任何“特殊”图标,而不是通用的打开和关闭图标。同样,这些图标是缓存的。 - 我添加了
GetDriveIcon()
方法来检索每种驱动器类型的已注册 Windows 图标。同样,这些图标也是缓存的。 - 我删除了“
_extensionList
”HashTable
,并使用了ImageList.Images
ImageListCollection
提供的相同功能。
- 我将 Paul 的代码从两个类合并到了我的ImageListsEx.cs控件类中。虽然在这个演示中我只使用了
Using the Code
实现该控件非常简单,只需将AviPlayerEx.cs、ImageListsEx.cs和TreeViewEx.cs文件添加到您的项目中,或者将它们构建到自己的 DLL 中。将TreeViewEx.cs控件拖到您的窗体上,并连接以下事件
OnBusyStateChanged
。这是一个自定义委托BooleanHandler (Boolean value)
事件处理程序,当TreeViewEx
开始处理请求时引发True
,当请求完成时引发False
。这允许您添加自定义处理以在需要时更改控件状态。
private void BusyStateChanged(Boolean busy) { this.btnShow.Enabled = this.btnClose.Enabled = !busy; }
OnPublishException
。这是一个自定义委托ExceptionHandler (Exception exception)
事件处理程序,它将来自TreeViewEx
的异常传递给父级进行处理、记录或显示。private void PublishException(Exception exception) { MessageBox.Show(exception.Message); }
OnPublishStatus
。这是一个自定义委托TextHandler (String value)
事件处理程序,它将来自TreeViewEx
的string
信息传递给父级,用于显示TreeViewEx
当前正在执行的操作。
private void PublishStatus(String text) { this.staStatus.Text = text; }
您需要实现 Nathan 使用的WndProc(ref Message m)
来处理用户调整窗体大小、最大化、最小化、还原或双击窗体标题栏的操作...没必要重复造轮子...他的代码是有效的!
protected override void WndProc(ref Message m) {
if (IsAsyncBusy) {
if (m.Msg == 0x112 /* WM_SYSCOMMAND */) {
int w = m.WParam.ToInt32();
if (w == 0xf120 /* SC_RESTORE */ || w == 0xf030
/* SC_MAXIMIZE */ || w == 0xf020
/* SC_MINIMIZE */)
return; // short circuit
} else if (m.Msg == 0xa3 /* WM_NCLBUTTONDBLCLK */)
return; // short circuit
}
base.WndProc(ref m);
}
您还需要在FormClosing(object sender, FormClosingEventArgs e)
方法中处理Cancel
事件,以防止用户在TreeViewEx
处理请求时关闭窗体。
private void DemoForm_FormClosing(object sender, FormClosingEventArgs e) {
e.Cancel = this.tvwExplorer.IsAsyncBusy;
}
最后,您需要在窗体的Form_Load
方法中调用TreeViewEx.Initialize()
方法来启动一切。
private void DemoForm_Load(object sender, EventArgs e) {
//----------------------------------------------------------------------------------------
//..If you're persisting their selections, then you need to add them
// before initializing the TreeViewEx control. You can use the
//.. List<string> "Selections" property defined within the TreeViewEx control...
//----------------------------------------------------------------------------------------
this.tvwExplorer.Selections.Add(@"C:\Windows\addins");
//----------------------------------------------------------------------------------------
//.. ...or you can manage you own List<string> & set/get the property as follows...
//----------------------------------------------------------------------------------------
// List<string> mySelections = new List<String>();
// mySelections.Add(@"C:\Windows\addins");
// this.tvwExplorer.Selections = mySelections;
//----------------------------------------------------------------------------------------
//.. ...and when you need to get it back...
//----------------------------------------------------------------------------------------
// mySelections = this.tvwExplorer.Selections;
//----------------------------------------------------------------------------------------
this.tvwExplorer.Initialize();
Application.DoEvents();
}
要真正“看到”它的实际运行效果,您需要深入到Windows\System32文件夹。
关注点
在 Microsoft Visual Studio IDE 中运行AviPlayerEx.cs控件时,可能会抛出LoadLock
被检测到错误。我已注释掉出现此问题的代码以及如何“修复”它。运行可执行文件时不会出现此错误。
public void Open(String fileName, Size size, Boolean continous) {
if (mdxAviPlayer != null) {mdxAviPlayer.Dispose();}
//------------------------------------------------------------------------------------
//..Within the Visual Studio IDE the following error is thrown when
//executing "mdxAviPlayer = new Video(fileName)"...
//------------------------------------------------------------------------------------
//.. LoadLock was detected. DLL 'C:\Windows\assembly\GAC\
// Microsoft.DirectX\1.0.2902.0__31bf3856ad364e35\Microsoft.DirectX.dll'
//.. is attempting managed execution inside OS Loader lock.
// Do not attempt to run managed code inside a DllMain or image
//.. initialization function since doing so can cause the application to hang.
//------------------------------------------------------------------------------------
//..The only work around I've found is the following, since this is an error
// being thrown from the OS to the MDA...
//------------------------------------------------------------------------------------
//.. Within the Visual Studio IDE select "Debug | Exceptions".
//.. Expand "Managed Debug Assistants".
//.. Deselect "Thrown" for "Loader Lock".
//.. Select the "OK" button.
//------------------------------------------------------------------------------------
mdxAviPlayer = new Video(fileName);
if (continous) {mdxAviPlayer.Ending += new EventHandler(Ending);}
mdxAviPlayer.Owner = this; this.Size = size;
mfContinous = continous;
}
谢谢
感谢阅读!如果您对控件有任何修改、错误修复或增强功能想要分享,请在评论区发布您的源代码片段和/或想法。
历史
- 2010 年 12 月 1 日 - 首次发布