主题的Windows XP风格的浏览器栏
- 下载VS2003 source files - 211 Kb
- 下载VS2002 s
- 下载VS2005 source files - 181 Kb
- 下载demo project - 208 Kb
- 下载documentation - 643 Kb
内容 介绍 特性 主题 ShellStyle.dll 的UIFILE 获取位图 XPExplorerBar 使用XPExplorerBar TaskPane 向TaskPane添加Expandos 重新排序Expandos本质 使用当前主题以外的主题 崩溃/扩展多个Expandos本质 Expando 向Expando添加控件 重新排序控制 隐藏/显示项目 对接和滚动 动画 TaskItem 的兴趣点 序列化 视觉风格和WM_PRINT 已知问题 历史 介绍 Windows XP风格的浏览器栏/任务栏/网页视图——你怎么称呼它们——现在都很流行。在网上有许多伟大的免费浏览器栏控件,如德里克·拉金的可折叠面板栏,达伦·梅的崩溃组控件,和汤姆·金瑟的全功能XP风格的可折叠面板。大多数插件都不支持Windows XP的主题,即使支持,也会通过自己绘制渐变/箭头来“伪造”主题,或者在组件中包含必要的图像。 由于缺乏适当的主题支持,我决定自己制作。 特性 Windows XP主题支持-甚至在前XP版本 动画扩展/折叠与淡出效果 动画显示/隐藏项目的幻灯片效果 所有组及其项的自动布局 组可以开始崩溃或扩展 分组重新排序(包括设计时) 焦点和键盘导航类似于Windows XP的资源管理器栏 用户自定义设置-梯度,颜色,边界和更多 支持。net框架版本1.0和1.1 支持二进制和XML序列化 主题 那么,我们如何利用主题呢? 我的第一个尝试是截取实际操作中的浏览器栏的截图,这样我就可以得到我需要的图像和颜色。只要我只使用XP附带的Luna主题,这就可以很好地工作。我不得不从头再来。 在偶然发现Don Kackman关于在自定义。net控件中添加XP主题的文章后,第2次尝试是使用UxTheme.dll。直到我发现只要你只使用默认的蓝色Luna主题就可以使用UxTheme。然而,Don想出了一个可能的解决方案——从ShellStyle.dll中获取必要的信息。 ShellStyle.dll 现在我知道该往哪里看了,问题是“我在找什么?”TGTSoft (StyleXP的开发者)有一个很棒的免费程序叫ResBuilder,它允许你打开和修改Windows资源文件。有了这个程序,我就可以在ShellStyle.dll内部进行查看了。 图1:ResBuilder中的ShellStyle.dll 我需要的部分是位图(显然)和UIFILE -更多的在后面。 在加载ShellStyle之前。dll,我们需要检查主题是否可用:隐藏 复制Code
// check if we are using themes. if so, load up the // appropriate shellstyle.dll if (UxTheme.AppThemed && LoadShellStyleDll()) { ...
如果它们可用,那么继续加载shell样式。dll:隐藏,收缩,复制Code
////// Loads the ShellStyle.dll into memory as determined by the current /// system theme ///private static bool LoadShellStyleDll() { // work out the path to the shellstyle.dll according // to the current theme string themeName = UxTheme.ThemeName.Substring(0, UxTheme.ThemeName.LastIndexOf('\\')); string styleName = themeName + "\\Shell\\" + UxTheme.ColorName; string stylePath = styleName + "\\shellstyle.dll"; // if for some reason it doesn't exist, use the default // shellstyle.dll in the windows\system32 directory if (!File.Exists(stylePath)) { stylePath = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\shellstyle.dll"; } // attempt to load the shellstyle dll hModule = LoadLibrary(stylePath); // return whether we succeeded return (hModule != IntPtr.Zero); }
的UIFILE 什么是UIFILE?UIFILE基本上是一个样式表,它告诉资源管理器栏它应该如何呈现自己。下面是一个小部分,显示了设置一个特殊组的标题栏的蓝色Luna主题:复制Code
button [id=atom(header)] { background: rcbmp(110,6,#FF00FF,0,0,1,0); borderthickness: rect(2,2,2,0); foreground: white; fontweight: rcint(10); padding: rect(10,0,0,0); animation: rectanglev | s | fast; }
(关于UIFILEs的更多信息,bfarber.com有一个关于如何读取UIFILE的教程)。 你会注意到有两个uifile -第一个是浏览器栏的,第二个是控制面板的。现在我们知道我们想要哪一个,是时候看看它的内容了:Hide 收缩,复制Code
////// Extracts the UIFILE from the currently loaded ShellStyle.dll ///public static string GetResourceUIFile() { // locate the "UIFILE" resource IntPtr hResource = FindResource(hModule, "#1", "UIFILE"); // get its size int resourceSize = SizeofResource(hModule, hResource); // load the resource IntPtr resourceData = LoadResource(hModule, hResource); // copy the resource data into a byte array so we // still have a copy once the resource is freed byte[] uiBytes = new byte[resourceSize]; GCHandle gcHandle = GCHandle.Alloc(uiBytes, GCHandleType.Pinned); IntPtr firstCopyElement = Marshal.UnsafeAddrOfPinnedArrayElement(uiBytes, 0); CopyMemory(firstCopyElement, resourceData, resourceSize); // free the resource gcHandle.Free(); FreeResource(resourceData); // convert the char array to an ansi string string s = Marshal.PtrToStringAnsi(firstCopyElement, resourceSize); return s; }
获取位图 UIFILE中的所有位图都有以下格式:复制Code
rcbmp(id, stretching, transparency, width, height, size, mirror)
要加载位图,我们只需将位图ID传递给GetResourceBMP方法:Hide复制Code
////// Returns a Bitmap from the currently loaded ShellStyle.dll ///public static Bitmap GetResourceBMP(string resourceName) { // find the resource IntPtr hBitmap = LoadBitmap(hModule, Int32.Parse(resourceName)); // load the bitmap Bitmap bitmap = Bitmap.FromHbitmap(hBitmap); return bitmap; }
上面的方法适用于普通的位图,但是如果图像是32bpp的PNG,我们会遇到一个主要的问题——alpha通道丢失了,留下了应该是透明的黑色区域。 图2:Alpha通道的比较 下面是Derek Lakin在博客上发布的解决方案:Hide 收缩,复制Code
////// Returns a Png Bitmap from the currently loaded ShellStyle.dll ///public static Bitmap GetResourcePNG(string resourceName) { // the resource size includes some header information // (for PNG's in shellstyle.dll this appears to be the // standard 40 bytes of BITMAPHEADERINFO). const int FILE_HEADER_BYTES = 40; // load the bitmap resource normally to get dimensions etc. Bitmap tmpNoAlpha = Bitmap.FromResource(hModule, "#" + resourceName); IntPtr hResource = FindResource(hModule, "#" + resourceName, (IntPtr) 2 /*RT_BITMAP*/); int resourceSize = SizeofResource(hModule, hResource); // initialise 32bit alpha bitmap (target) Bitmap bitmap = new Bitmap(tmpNoAlpha.Width, tmpNoAlpha.Height, PixelFormat.Format32bppArgb); // load the resource via kernel32.dll (preserves alpha) IntPtr hLoadedResource = LoadResource(hModule, hResource); // copy bitmap data into byte array directly byte[] bitmapBytes = new byte[resourceSize]; GCHandle gcHandle = GCHandle.Alloc(bitmapBytes, GCHandleType.Pinned); IntPtr firstCopyElement = Marshal.UnsafeAddrOfPinnedArrayElement(bitmapBytes, 0); // nb. we only copy the actual PNG data (no header) CopyMemory(firstCopyElement, hLoadedResource, resourceSize); FreeResource(hLoadedResource); // copy the byte array contents back // to a handle to the alpha bitmap (use lockbits) Rectangle copyArea = new Rectangle(0, 0, bitmap.Width, bitmap.Height); BitmapData alphaBits = bitmap.LockBits(copyArea, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); // copymemory to bitmap data (Scan0) firstCopyElement = Marshal.UnsafeAddrOfPinnedArrayElement(bitmapBytes, FILE_HEADER_BYTES); CopyMemory(alphaBits.Scan0, firstCopyElement, resourceSize - FILE_HEADER_BYTES); gcHandle.Free(); // complete operation bitmap.UnlockBits(alphaBits); GdiFlush(); // flip bits (not sure why this is needed at the moment..) bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY); return bitmap; }
那么,我们如何知道每个图像使用哪个呢?一般来说,普通位图将使用十六进制透明度值,而png将使用整数值。隐藏,复制Code
... // if the transparency value starts with a #, then the image is // a bitmap, otherwise it is a 32bit png if (transparent.StartsWith("#")) { // get the bitmap image = Util.GetResourceBMP(id); ... } else { // get the png image = Util.GetResourcePNG(id); } ...
XPExplorerBar XPExplorerBar由三个主要组件组成: 的TaskPane Expandos本质, TaskItems 我不会详细介绍这些功能是如何实现的,因为这就是源代码的作用,但是我将深入介绍一些更有趣的特性,比如动画。 使用XPExplorerBar 在使用XPExplorerBar之前,您需要在项目的References部分添加对XPExplorerBar.dll的引用。 要将XPExplorerBar.dll添加到工具箱,您可以: 选择工具→从菜单中添加/删除工具箱项,或 右键单击工具箱,选择添加/删除项目。 浏览XPExplorerBar.dll,然后按OK。然后可以将控件拖放到窗体上。 注意:如果重新编译源代码哟你将需要重新签署XPExplorerBar。否则,当您试图将其添加到工具箱时,Visual Studio将抛出异常。 打开VS.NET命令提示符,并将目录更改为指向XPExplorerBar\bin\发布目录。 然后输入“sn -R XPExplorerBar.dll .\..\XPExplorerBar”。snk”(当然没有引号)。 然后就可以将其添加到工具箱中了。 TaskPane TaskPane充当了XPExplorerBar将包含的所有Expandos的容器。 Expandos本质 表示TaskPane中包含的Expandos集合的ExpandoCollection。 CustomSettings 用于绘制任务窗格的自定义设置。 注意:更改其中一个设置将覆盖在shell .dll中定义的相同系统设置。 向TaskPane添加Expandos 有两种方法可以向TaskPane添加Expandos: 通过属性编辑器窗口中的Expandos属性,或者 将Expandos从工具箱拖动到任务窗格上 图3:使用属性编辑器添加Expandos 重新排序Expandos本质 在设计期间,您可以使用Expando集合编辑器中的向上和向下箭头按钮来重新排序Expandos。 图4:在设计时使用箭头按钮对Expandos重新排序 在其他时候,TaskPane的Expandos属性提供了以下方法来重新排序Expandos: 移动(Expando值,int index) 将指定的Expando移动到ExpandoCollection中的指定索引位置。 MoveToTop (Expando价值) 将指定的Expando移动到ExpandoCollection的顶部。 MoveToBottom (Expando价值) 将指定的Expando移动到ExpandoCollection的底部。隐藏,复制Code
// Move an Expando to the top of the TaskPane taskpane.Expandos.MoveToTop(expando);
使用当前主题以外的主题 XPExplorerBar还允许您使用与当前主题不同的主题。 UseClassicTheme () 强制TaskPane和它的所有扩展使用一个等价于Windows XP经典主题的主题(这是Windows 2000或更早版本的默认主题)。 UseCustomTheme(字符串stylePath) 强制TaskPane及其所有扩展项使用指定的主题。 UseDefaultTheme () 强制TaskPane和它的所有扩展使用当前系统主题。 UseClassicTheme()和UseDefaultTheme()似乎在XP之前的Windows版本或禁用主题的XP机器上做同样的事情。注意,UseCustomTheme()将在Windows 2000或更早版本上工作。隐藏,复制Code
TaskPane taskpane = new TaskPane(); // foreverblue.dll lives in the same directory as // the executable. if it were somewhere else, we // would need to use "path/to/foreverblue.dll" taskpane.UseCustomTheme("foreverblue.dll");
图5:XPExplorerBar演示与Windows XP主题永远蓝色在Windows 2000 自定义主题可以在ThemeXP中找到。 崩溃/扩展多个Expandos本质 XPExplorerBar现在允许您同时展开或折叠多个扩展项。 CollapseAll () 折叠TaskPane中包含的所有Expandos。 ExpandAll () 展开TaskPane中包含的所有Expandos。 CollapseAllButOne (Expando Expando) 折叠TaskPane中包含的所有Expandos,除了指定的被展开的Expando。 Expando Expandos是任务项和其他控件的容器,可以根据需要折叠/展开。注意,Expandos只有在添加到TaskPane时才会显示动画。 我确定现在,您正在想我从哪里得到Expando这个名字。如果你看一下这个UIFILE,你会有一个想法(它是微软给可折叠组的名字)。 动画 确定Expando是否会执行折叠/展开或显示/隐藏动画。 自动布局 确定Expando是否会自动布局其项。 倒塌 确定Expando是折叠的还是展开的。 CustomSettings 用于绘制Expando主体的自定义设置。 注意:更改其中一个设置将覆盖在shell .dll中定义的相同系统设置。 CustomHeaderSettings 用于绘制Expando标题栏的自定义设置。 注意:更改其中一个设置将覆盖在shell .dll中定义的相同系统设置。 ExpandedHeight 设置Expando在其展开状态下的高度。如果使用AutoLayout属性,则忽略这一点。 项目 表示Expando中包含的控件集合的ItemCollection。 ShowFocusCues 获取或设置一个值,该值指示Expando在有焦点时是否应显示焦点矩形。 SpecialGroup 确定Expando是否将呈现为一个特殊的组。 TitleImage 指定显示在Expando标题栏左侧的图像。 向Expando添加控件 有两种方法可以添加控件到Expando: 通过属性编辑器窗口中的Items属性,或 将控件从工具箱拖动到Expando上。 图6a(顶部)和6b(底部):使用属性编辑器添加控件。 3.0版本现在允许通过设计器中的Items属性添加任务项之外的其他控件。单击Add按钮将向Expando添加一个TaskItem,而单击Add按钮旁边的箭头将提供要添加到Expando的更有用的控件列表。 重新排序控制 在设计期间,可以使用控件集合编辑器中的向上和向下箭头按钮对控件重新排序。 图7:在设计时使用箭头按钮对控件重新排序 在其他时候,Expando的Items属性提供了以下方法来重新排序控件: 移动(控制值,int索引) 将指定的控件移动到项目集合中指定的索引位置。 MoveToTop(控制价值) 将指定的控件移动到项目集合的顶部。 MoveToBottom(控制价值) 将指定的控件移动到项目集合的底部。隐藏,复制Code
// Move a TaskItem to the top of the Expando expando.Items.MoveToTop(taskitem);
从v3.3开始,Expandos现在可以在TaskPane中拖动。要做到这一点,TaskPane的allowexpandodrag属性应该设置为true。 图8:在TaskPane周围拖动Expandos 隐藏/显示控制 为了隐藏或显示项,提供了HideControl和ShowControl方法。你不应该使用控制。可见= false隐藏物品,Expando需要再控制可见为了执行一个扩大动画没有任何视觉构件(如黑色背景对于某些主题控制),最有可能导致控制保持可见应该隐藏,以及导致一些布局头痛。 HideControl(控制控制) 隐藏指定的控件。 HideControl(控制[]控制) 隐藏指定的控件数组。 ShowControl(控制控制) 显示指定的控件。 ShowControl(控制[]控制) 显示指定的控件数组。 注意:为了让Expando执行动画,Expando的自动布局和动画属性都必须为真。 注意:在3.3版本中,你可以使用BeginUpdate()/ endpdate()方法批量处理HideControl/ ShowControl命令:Hide 复制Code
// stop the following slide animation // commands from being performed expando.BeginUpdate(); expando.HideControl(new Control[] {taskItem1, taskItem2}); expando.ShowControl(taskItem3); // now perform the animations expando.EndUpdate();
注意:目前BeginUpdate()/ endpdate()方法仅用于此目的。 对接和滚动 图9:停靠的可滚动面板 从3.0版本开始,Expandos不再支持滚动。在版本2中实现滚动的方式。x造成了一些渲染问题,我还没有解决。希望通过在这个版本中删除滚动支持,我将有更多的时间来解决这些问题,并在未来的版本中添加滚动支持(而不是延迟发布)。对由此造成的不便表示歉意。 要添加滚动,只需添加一个可滚动面板并将其DockStyle属性设置为Fill。 为了防止子控件在停靠时覆盖标题栏和边框,我覆盖了Expando的DisplayRectangle属性。隐藏,复制Code
////// Overrides DisplayRectangle so that docked controls /// don't cover the titlebar or borders ///public override Rectangle DisplayRectangle { get { return new Rectangle(this.Border.Left, this.HeaderHeight + this.Border.Top, this.Width - this.Border.Left - this.Border.Right, this.ExpandedHeight - this.HeaderHeight - this.Border.Top - this.Border.Bottom); } }
动画 图10:动态折叠 要激活折叠/展开动画,Expando的Animate属性必须设置为true:Hide复制Code
/// Gets or sets whether the Expando is allowed to animate. public bool Animate { get { return this.animate; } set { this.animate = value; } }
当Expando的折叠属性改变时,它检查它是否可以动画。隐藏,复制Code
/// Gets or sets whether the Expando is collapsed. public bool Collapsed { ... set { if (this.collapsed != value) { // if we're supposed to collapse, check if we can if (value && !this.CanCollapse) { // looks like we can't so time to bail return; } this.collapsed = value; // only animate if we're allowed to, we're not in // design mode and we're not initialising if (this.Animate && !this.DesignMode && !this.Initialising) { ...
如果Expando能够动画,它会创建一个新的AnimationHelper,告诉Expando准备好并开始动画计时器。隐藏,收缩,复制Code
... this.animationHelper = new AnimationHelper(this, AnimationHelper.FadeAnimation); this.OnStateChanged(new ExpandoEventArgs(this)); this.animationHelper.StartAnimation(); ... } } } } ////// Starts the animation for the specified expando ///protected void StartAnimation() { // don't bother going any further if we are already animating if (this.Animating) { return; } this.animationStepNum = 0; // tell the expando to get ready to animate if (this.AnimationType == FadeAnimation) { this.expando.StartFadeAnimation(); } else { this.expando.StartSlideAnimation(); } // start the animation timer this.animationTimer.Start(); }
一旦Expando接收到StartAnimation消息,它就会对它的“客户区”进行快照(例如:)。所有Expando的子控件都被设置为不可见,否则它们会从Expando的底部滑出(同样,那些将其FlatStyle属性设置为System的控件不喜欢改变其不透明度)。隐藏,收缩,复制Code
////// Gets the Expando ready to start its collapse/expand animation ///protected void StartAnimation() { this.animating = true; // stop the layout engine this.SuspendLayout(); // get an image of the client area that we can // use for alpha-blending in our animation this.animationImage = this.GetAnimationImage(); // set each control invisible (otherwise they // appear to slide off the bottom of the group) foreach (Control control in this.Controls) { control.Visible = false; } // restart the layout engine this.ResumeLayout(false); } ////// Returns an image of the group's display area to be used /// in the animation ///internal Image GetAnimationImage() { // create a new image to draw into Image image = new Bitmap(this.Width, this.Height); // get a graphics object we can draw into Graphics g = Graphics.FromImage(image); IntPtr hDC = g.GetHdc(); // some flags to tell the control how to draw itself IntPtr flags = (IntPtr) (WmPrintFlags.PRF_CLIENT | WmPrintFlags.PRF_CHILDREN | WmPrintFlags.PRF_ERASEBKGND); // tell the control to draw itself NativeMethods.SendMessage(this.Handle, WindowMessageFlags.WM_PRINT, hDC, flags); // clean up resources g.ReleaseHdc(hDC); g.Dispose(); // return the completed animation image return image; } ////// The SendMessage function sends the specified message to a /// window or windows. It calls the window procedure for the /// specified window and does not return until the window /// procedure has processed the message ///[DllImport("User32.dll")] internal static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
在动画过程中,animationImage会褪色并根据需要向上/向下移动。隐藏,收缩,复制Code
////// Paints the "Display Rectangle". This is the dockable /// area of the control (ie non-titlebar/border area). ///protected void PaintDisplayRect(Graphics g) { // are we animating if (this.animating && this.animationImage != null) { // calculate the transparency value for the animation image float alpha = (((float) (this.Height - this.HeaderHeight)) / ((float) (this.ExpandedHeight - this.HeaderHeight))); float[][] ptsArray = {new float[] {1, 0, 0, 0, 0}, new float[] {0, 1, 0, 0, 0}, new float[] {0, 0, 1, 0, 0}, new float[] {0, 0, 0, alpha, 0}, new float[] {0, 0, 0, 0, 1}}; ColorMatrix colorMatrix = new ColorMatrix(ptsArray); ImageAttributes imageAttributes = new ImageAttributes(); imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); // work out how far up the animation image we need to start int y = this.animationImage.Height - this.PseudoClientHeight - this.Border.Bottom; // draw the image g.DrawImage(this.animationImage, new Rectangle(0, this.HeaderHeight, this.Width, this.Height - this.HeaderHeight), 0, y, this.animationImage.Width, this.animationImage.Height - y, GraphicsUnit.Pixel, imageAttributes); } else { ... } }
TaskItem 图11:TaskItem 任务项类似于标签。每个TaskItem可以包含一个限制为16x16像素的图像。可以使用较大或较小的图像,但必要时将放大/缩小到16x16。 CustomSettings 用于绘制TaskItem的自定义设置。 注意:更改其中一个设置将覆盖在shell .dll中定义的相同系统设置。 图像 在TaskItem上显示的图像。 ShowFocusCues 获取或设置一个值,该值指示TaskItem在有焦点时是否应显示焦点矩形。 的兴趣点 序列化 从v3.2开始,已经重新修改了二进制序列化支持,并添加了XML序列化支持。 注意:v3.2.1在序列化过程中添加了Version属性,以确保与未来版本的向后兼容性。鼓励使用序列化的任何人升级到v3.2.1 为了修复v3.1在序列化方面的问题,我需要找到一种执行序列化的新方法。在谷歌上搜索了很多之后,我发现了代孕的概念。代理是将被序列化到另一个类(通常是因为另一个类不是可序列化的,就是包含不可序列化的类,或者会导致序列化问题)的类。 TaskPane.TaskPaneSurrogate 序列化的类,而不是TaskPane。 Expando.ExpandoSurrogate 序列化的类,而不是序列化的Expando。 TaskItem.TaskItemSurrogate 序列化的类,而不是TaskItem。 上述所有代理人都有以下方法向代理人/从代理人进口/出口数据: 负载(对象值) 使用要从指定对象序列化的数据填充代理项。 Save () 返回包含反序列化代理数据的对象。 下面的示例展示了TaskPane是如何被序列化和反序列化的。TaskPaneSurrogate与二进制和XML序列化:隐藏收缩,复制Code
// BINARY SERIALIZATION // serialize a TaskPane to a file IFormatter formatter = new BinaryFormatter(); stream = new FileStream("TaskPane.bin", FileMode.Create, FileAccess.Write, FileShare.None); TaskPane.TaskPaneSurrogate taskPaneSurrogate = new TaskPane.TaskPaneSurrogate(); taskPaneSurrogate.Load(this.serializeTaskPane); formatter.Serialize(stream, taskPaneSurrogate); stream.Close(); // deserialize a TaskPane from a file IFormatter formatter = new BinaryFormatter(); stream = new FileStream("TaskPane.bin", FileMode.Open, FileAccess.Read, FileShare.Read); TaskPane.TaskPaneSurrogate taskPaneSurrogate = (TaskPane.TaskPaneSurrogate) formatter.Deserialize(stream); TaskPane taskpane = taskPaneSurrogate.Save(); stream.Close(); // XML SERIALIZATION // serialize a TaskPane to a file XmlSerializer xml = new XmlSerializer(typeof(TaskPane.TaskPaneSurrogate)); StreamWriter writer = new StreamWriter("TaskPane.xml"); TaskPane.TaskPaneSurrogate taskPaneSurrogate = new TaskPane.TaskPaneSurrogate(); taskPaneSurrogate.Load(this.serializeTaskPane); xml.Serialize(writer, taskPaneSurrogate); writer.Close(); // deserialize a TaskPane from a file XmlSerializer xml = new XmlSerializer(typeof(TaskPane.TaskPaneSurrogate)); TextReader reader = new StreamReader("TaskPane.xml"); TaskPane.TaskPaneSurrogate taskPaneSurrogate = (TaskPane.TaskPaneSurrogate) xml.Deserialize(reader); TaskPane taskpane = taskPaneSurrogate.Save(); reader.Close();
注意:Expando中的控件。在序列化过程中忽略非任务项的ItemCollection,因为(不幸的是)它们不支持序列化。 视觉风格和WM_PRINT 更新:我向微软发送了一个关于视觉风格和WM_PRINT消息的错误报告(可以在这里找到),他们的回应基本上是,这将很难修复,所以看起来我们被困在这个解决方案,直到Avalon发布。 一些XP主题控件(TextBox, ListView, TreeView, ListBox, CheckedListBox, DateTimePicker, GroupBox)在发送WM_PRINT消息时不绘制主题边框,如果视觉样式是启用的。 图12:WM_PRINT消息后的文本框边框 为了解决这个问题,我立即遇到了另一个问题——如何发现是否应用了视觉样式(即:视觉样式)。enablevisualstyles()被使用)。在google上搜索了几次后,我找到了这个解决方案,它可以检查主题是否启用以及使用的是通用控件的哪个版本:Hide 收缩,复制Code
////// Checks whether Visual Styles are enabled ///protected bool VisualStylesEnabled { get { OperatingSystem os = System.Environment.OSVersion; // check if the OS is XP or higher if (os.Platform == PlatformID.Win32NT && ((os.Version.Major == 5 && os.Version.Minor >= 1) || os.Version.Major > 5)) { // are themes enabled if (UxTheme.IsThemeActive() && UxTheme.IsAppThemed()) { DLLVERSIONINFO version = new DLLVERSIONINFO(); version.cbSize = Marshal.SizeOf(typeof(DLLVERSIONINFO)); // are we using Common Controls v6 if (DllGetVersion(ref version) == 0) { return (version.dwMajorVersion > 5); } } } return false; } } ////// Receives dynamic-link library (DLL)-specific version information. /// It is used with the DllGetVersion function ///[StructLayout(LayoutKind.Sequential)] public struct DLLVERSIONINFO { public int cbSize; public int dwMajorVersion; public int dwMinorVersion; public int dwBuildNumber; public int dwPlatformID; } ////// Implemented by many of the Microsoft Windows Shell dynamic-link libraries /// (DLLs) to allow applications to obtain DLL-specific version information ///[DllImport("Comctl32.dll")] public static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);
然后,我子类化了有问题的控件,并侦听WM_PRINT消息,并在未主题化的边框上画出主题化边框,如果视觉样式是启用的。隐藏,收缩,复制Code
////// Processes Windows messages ///protected override void WndProc(ref Message m) { base.WndProc(ref m); // don't bother if visual styles aren't applied if (!this.visualStylesEnabled) { return; } // WM_PRINT message? if (m.Msg == (int) WindowMessageFlags.WM_PRINT) { // are we supposed to draw the nonclient area? // (ie borders) if ((m.LParam.ToInt32() & (int) WmPrintFlags.PRF_NONCLIENT) == (int) WmPrintFlags.PRF_NONCLIENT) { // open theme data IntPtr hTheme = UxTheme.OpenThemeData(this.Handle, UxTheme.WindowClasses.Edit); if (hTheme != IntPtr.Zero) { // get the part and state needed int partId = (int) UxTheme.Parts.Edit.EditText; int stateId = (int) UxTheme.PartStates.EditText.Normal; // rectangle to draw into RECT rect = new RECT(); rect.right = this.Width; rect.bottom = this.Height; // clipping rectangle RECT clipRect = new RECT(); // draw the left border clipRect.left = rect.left; clipRect.top = rect.top; clipRect.right = rect.left + 2; clipRect.bottom = rect.bottom; UxTheme.DrawThemeBackground(hTheme, m.WParam, partId, stateId, ref rect, ref clipRect); // do the same for other borders ... } UxTheme.CloseThemeData(hTheme); } } }
下载中包含了子类控件(XPTextBox、XPListView、XPTreeView、XPListBox、XPCheckedListBox、XPDateTimePicker)。 已知问题 XPExplorerBar可能不能正确地呈现非官方的XP主题。 历史 2005年9月30日-版本3.3 增加了对Expandos和任务项的右对齐eft支持。 增加了为Expandos批处理HideControl/ShowControl命令的能力(参见隐藏/显示控件了解更多细节)。 增加了在TaskPane周围拖动Expandos的能力。 修正TaskItem的文本有时被剪。 其他的bug修复。 2005年2月24日- 3.2.1.2版本 固定的Expando隐藏/ShowControl方法试图动画而Expando是折叠。 更改了UseDefaultTabHandling属性,从而默认启用了焦点和键盘导航。要使用类似于Windows XP的浏览器条的标签处理,UseDefaultTabHandling必须显式设置为false。 2005年2月17日- 3.2.1.1版本 从不应该序列化的类中删除多余的可序列化属性。 添加了UseDefaultTabHandling属性到Expandos,这样用户可以选择默认的焦点和键盘导航或使用焦点和键盘导航类似于Windows XP的资源管理器栏。 2005年1月22日- 3.2.1版本 为序列化添加了版本属性,以确保与未来版本的向后兼容性。 其他的bug修复。 2005年1月19日-版本3.2 改变了二进制序列化的工作方式,并添加了XML序列化支持。 修正了不能从shellstyle中提取图像。dll在Windows 9x上的bug。 固定“资源转换为文件'Myfile。resx“失败”错误。 改进的主题支持。 其他的bug修复。 2004年11月2日- 3.1版本 添加了对TaskPane, Expandos, TaskItems, TaskPane的二进制序列化支持。ExpandoCollections Expando.ItemCollections。 修正了不能以编程方式向Expandos中添加任务项或其他控件的错误,这个错误是在v3.0中意外重新引入的。 其他的bug修复。 2004年10月25日- 3.0版本 为任务窗格、扩展和任务项添加自定义设置。 由于呈现问题,删除了Expandos的本机滚动支持。 修正了Expandos在以编程方式添加到TaskPane时从点(0,0)开始的错误。 修正了资料片开始时不正确的高度,如果崩溃和有标题图像的错误。 其他错误修复 2004年10月11日-第2.1版 更改了Expandos,所以当CanCollapse属性为false时,折叠/展开箭头和标题栏高亮不显示。 2004年9月30日-版本2.0 为Expandos添加了原生滚动支持。 增加了焦点和键盘导航,类似于Windows XP的资源管理器栏。 修正错误:不能以编程方式添加扩展项、任务项或其他控件。 2004年9月3日-版本1.4 在Expandos上添加了控件重新排序。 2004年8月26日-版本1.3 添加Expando重建创造条件。 2004年8月21日- 1.21版本 Expandos标题栏现在在禁用时以灰度呈现。 当任务项被禁用时,它们呈现的文本与标签相同。 改进的主题支持。 2004年8月5日-版本1.2 名称从XPTaskBar更改为XPExplorerBar。 添加显示/隐藏动画。 改进的主题支持。 二四年六月三日-第一版1。 增加了折叠/扩展多个扩展。 添加了自定义XP主题支持。 2004年5月31日-初次发行。 本文转载于:http://www.diyabc.com/frontweb/news237.html