|

Introduction
The About Box dialog isn't an essential part of any application, but it serves an important role, as the "dogtags" for your application.
There are two distinct, and very different, audiences for this dialog.
- users: to identify the application's name, who made it, when it was created, and what version they have. Very basic stuff.
- developers: to provide extremely detailed build, version, and file information. Typically used when troubleshooting problems with compiled, deployed code.
You definitely should have an About Box-- but to continue with the dogtag analogy, if you're whipping out dogtags on a regular basis, that's symptomatic of a deeper problem (Mediiiic!). When you do need it, it can be a lifesaver. It's OK to be used infrequently, but it also needs to provide decent diagnostic info. Decorative About Boxes with their scrolling text and 3D graphics may be fun, but they aren't helpful.
In order to serve the needs of these two vastly different user groups, my About Box provides two views: a simple, basic view for users, and a vastly more detailed view (accessible through a "More >>" button) for developers.
Using the code
This form is intended to be a standalone, resuable component; simply drag and drop the AboutBox.vb file into your project, then instantiate it as you would any other form. It has no special dependencies.
Private Sub MenuItemAbout_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles MenuItemAbout.Click
Dim frmAbout As New AboutBox
frmAbout.ShowDialog(Me)
End Sub
That provides the simplest, default About Box behavior. This should work for most applications right out of the box (as pictured in the screenshot). If you want to customize the form's behavior, it does have a number of optional properties that can be set before showing the dialog:
Public Property AppEntryAssembly() As System.Reflection.Assembly
Public Property AppTitle() As String
Public Property AppDescription() As String
Public Property AppVersion() As String
Public Property AppCopyright() As String
Public Property AppImage() As Image
Public Property AppMoreInfo() As String
Public Property AppDetailsButton() As Boolean
The sample application demonstrates how to set these properties to customize the text in the dialog.
Points of Interest
The primary information presented by the About Box form is automatically derived from the AssemblyInfo.* file. The AssemblyInfo.* file should always be populated for all of your assemblies as a best programming practice, but you'll want to make doubly sure in this case.
<Assembly: AssemblyTitle("About Box Demo")>
<Assembly: AssemblyDescription("Demonstration of AboutBox.vb code")>
<Assembly: AssemblyCompany("Atwood Heavy Industries")>
<Assembly: AssemblyProduct("Demo code")>
<Assembly: AssemblyCopyright("© 2004, Atwood Heavy Industries")>
<Assembly: AssemblyTrademark("All Rights Reserved")>
The rest of the detailed information is gathered through .NET's built in, and very powerful, reflection capabilities.
The build date is automatically calculated for each assembly based on the Build and Revision numbers specified in the AssemblyInfo.*:
<Assembly: AssemblyVersion("4.1.*")>
This algorithm only works if standard auto-increment values were used (as pictured):
dt = CType("01/01/2000", DateTime). _
AddDays(AssemblyVersion.Build). _
AddSeconds(AssemblyVersion.Revision * 2)
If TimeZone.IsDaylightSavingTime(dt, _
TimeZone.CurrentTimeZone.GetDaylightChanges(dt.Year)) Then
dt = dt.AddHours(1)
End If
If dt > DateTime.Now Or AssemblyVersion.Build < 730 Or _
AssemblyVersion.Revision = 0 Then
dt = AssemblyLastWriteTime(a)
End If
If you've overridden the Build or Revision numbers, I can't calculate build date that way. In those scenarios, I use GetLastWriteTime on the assembly DLL file. I'm not aware of any more accurate methods to get build date, but between these two, the build time is usually correct.
History
- Monday, June 14, 2004 - Published.
- Sunday, December 19, 2004 - Version 1.1
- code refactored for readability and simplicity.
- switched to
RichTextBox for more text, so URLs and MAILTO are automatically supported in .AppMoreInfo property.
- More assembly properties are enumerated in the Assembly Detail tab.
- converted to VB.NET 2005 style XML comments.
- now with 36% more cowbell!
- Wednesday, Janary 30, 2007 - Version 1.2
- Added support for using proper system font
- Tested in Windows Vista
- Ported to Visual Studio 2005 and .NET 2.0
- Monday, February 26, 2007 - Version 1.2a
- Added C# version, thanks to Scott Ferguson!
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 27 (Total in Forum: 27) (Refresh) | FirstPrevNext |
|
|
 |
|
|
Thanks so much for this Jeff, it is a great piece of work.
The first time the form is displayed, I am able to select the details button. If I close the form and display it again, the details button is no longer visible - only the System Info... and OK buttons. Is this intentional?
Cheers
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
First off, thanks a ton for the extremely useful well-thought-out About Box!
I was trying to use the about box in a form, but kept getting a NonSupportedException in the AssemblyLastWriteTime function at the a.Location property of the assembly. The error was something like "Location is not supported for dynamically generated assemblies". Turns out that since I'm using an XslCompiledTransform (that uses reflection to emit a dynamic assembly), it was complaining.
I just moved that line down into the lower try/catch block and it works now 
private DateTime AssemblyLastWriteTime(Assembly a) { try { if (a.Location == null || a.Location == "") return DateTime.MaxValue; return File.GetLastWriteTime(a.Location); }catch(Exception){ return DateTime.MaxValue; } }
Bart: Look at me, I'm a grad student. I'm 30 years old and I made $600 last year. Marge: Bart, don't make fun of grad students. They've just made a terrible life choice.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for you help about box. I added the following extras you may want to put in your copy.
In the ReplaceTokens() function:
s = s.Replace("%fileversion%", EntryAssemblyAttrib("fileversion")) s = s.Replace("%infoversion%", EntryAssemblyAttrib("informationalversion"))
In the AssemblyAttribs() function:
Case "System.Reflection.AssemblyFileVersionAttribute" Value = CType(attrib, AssemblyFileVersionAttribute).Version.ToString
S.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Could someone give me any help as to how to instantiate the form. I'm a newbie and don't quite understand. When I include the AboutBox.vb and build the priject I get lots of errors, "not a member and not declaired. Do I need add all these items or is the design form included?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I used VB.Net to C# Converter 2.08 (For VB 2002, 2003 Projects) to convert this project,the code is converted but the issue is that the whole app throws a exception
Code here for review
_________________________
"When the superior man refrains from acting, his force is felt for a thousand li." Sun Tzu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
quick and dirty.
the collection has never been populated. object obj = nvc[Name]; if ( null == obj ) { nvc.Add(Name, Value); }
Cheers Robert
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
yes i had seen that fixed the code actually to get it working under .net 2.0 so anybody wanting to use it can hook it up.Will upload soon.
_________________________
"When the superior man refrains from acting, his force is felt for a thousand li." Sun Tzu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I submitted an update to the article which includes a full C# conversion of the solution.. it should be posted shortly!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Well hope to see this n and review the C# code.
_________________________
"When the superior man refrains from acting, his force is felt for a thousand li." Sun Tzu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
cOOL
_________________________
"When the superior man refrains from acting, his force is felt for a thousand li." Sun Tzu
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I am using it in my project now. I made a change that I wanted to share, mostly to get your feedback. I try to avoid Select's that use strings, so I changed it to use a type handle, but I am not sure if it is better or not. What do you think?
(The old code is commented out below the changed code)
Private Function AssemblyAttribs(ByVal a As System.Reflection.Assembly) _ As Specialized.NameValueCollection Dim TypeName As String Dim Name As String Dim Value As String Dim nvc As New Specialized.NameValueCollection Dim r As New Regex("(\.Assembly|\.)(?[^.]*)Attribute$", RegexOptions.IgnoreCase)
For Each attrib As Object In a.GetCustomAttributes(False) Dim typ As Type = attrib.GetType() Dim typHandle As Integer = typ.TypeHandle.Value.ToInt32 TypeName = typ.ToString Name = r.Match(TypeName).Groups("Name").ToString Value = ""
Select Case typHandle Case GetType(System.CLSCompliantAttribute).TypeHandle.Value.ToInt32() Value = DirectCast(attrib, CLSCompliantAttribute).IsCompliant.ToString Case GetType(System.Diagnostics.DebuggableAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, Diagnostics.DebuggableAttribute).IsJITTrackingEnabled.ToString Case GetType(System.Reflection.AssemblyCompanyAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyCompanyAttribute).Company.ToString Case GetType(System.Reflection.AssemblyConfigurationAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyConfigurationAttribute).Configuration.ToString Case GetType(System.Reflection.AssemblyCopyrightAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyCopyrightAttribute).Copyright.ToString Case GetType(System.Reflection.AssemblyDefaultAliasAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyDefaultAliasAttribute).DefaultAlias.ToString Case GetType(System.Reflection.AssemblyDelaySignAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyDelaySignAttribute).DelaySign.ToString Case GetType(System.Reflection.AssemblyDescriptionAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyDescriptionAttribute).Description.ToString Case GetType(System.Reflection.AssemblyInformationalVersionAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyInformationalVersionAttribute).InformationalVersion.ToString Case GetType(System.Reflection.AssemblyKeyFileAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyKeyFileAttribute).KeyFile.ToString Case GetType(System.Reflection.AssemblyProductAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyProductAttribute).Product.ToString Case GetType(System.Reflection.AssemblyTrademarkAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyTrademarkAttribute).Trademark.ToString Case GetType(System.Reflection.AssemblyTitleAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, AssemblyTitleAttribute).Title.ToString Case GetType(System.Resources.NeutralResourcesLanguageAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, Resources.NeutralResourcesLanguageAttribute).CultureName.ToString Case GetType(System.Resources.SatelliteContractVersionAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, Resources.SatelliteContractVersionAttribute).Version.ToString Case GetType(System.Runtime.InteropServices.ComCompatibleVersionAttribute).TypeHandle.Value.ToInt32 Dim x As Runtime.InteropServices.ComCompatibleVersionAttribute x = DirectCast(attrib, Runtime.InteropServices.ComCompatibleVersionAttribute) Value = x.MajorVersion & "." & x.MinorVersion & "." & x.RevisionNumber & "." & x.BuildNumber Case GetType(System.Runtime.InteropServices.ComVisibleAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, Runtime.InteropServices.ComVisibleAttribute).Value.ToString Case GetType(System.Runtime.InteropServices.GuidAttribute).TypeHandle.Value.ToInt32 Value = DirectCast(attrib, Runtime.InteropServices.GuidAttribute).Value.ToString Case GetType(System.Runtime.InteropServices.TypeLibVersionAttribute).TypeHandle.Value.ToInt32 Dim x As Runtime.InteropServices.TypeLibVersionAttribute x = DirectCast(attrib, Runtime.InteropServices.TypeLibVersionAttribute) Value = x.MajorVersion & "." & x.MinorVersion Case GetType(System.Security.AllowPartiallyTrustedCallersAttribute).TypeHandle.Value.ToInt32 Value = "(Present)" Case Else Value = TypeName End Select
'Select Case TypeName ' 'Case "System.CLSCompliantAttribute" ' ' Value = CType(attrib, CLSCompliantAttribute).IsCompliant.ToString ' 'Case "System.Diagnostics.DebuggableAttribute" ' ' Value = CType(attrib, Diagnostics.DebuggableAttribute).IsJITTrackingEnabled.ToString ' 'Case "System.Reflection.AssemblyCompanyAttribute" ' ' Value = CType(attrib, AssemblyCompanyAttribute).Company.ToString ' 'Case "System.Reflection.AssemblyConfigurationAttribute" ' ' Value = CType(attrib, AssemblyConfigurationAttribute).Configuration.ToString ' 'Case "System.Reflection.AssemblyCopyrightAttribute" ' ' Value = CType(attrib, AssemblyCopyrightAttribute).Copyright.ToString ' 'Case "System.Reflection.AssemblyDefaultAliasAttribute" ' ' Value = CType(attrib, AssemblyDefaultAliasAttribute).DefaultAlias.ToString ' 'Case "System.Reflection.AssemblyDelaySignAttribute" ' ' Value = CType(attrib, AssemblyDelaySignAttribute).DelaySign.ToString ' 'Case "System.Reflection.AssemblyDescriptionAttribute" ' ' Value = CType(attrib, AssemblyDescriptionAttribute).Description.ToString ' 'Case "System.Reflection.AssemblyInformationalVersionAttribute" ' ' Value = CType(attrib, AssemblyInformationalVersionAttribute).InformationalVersion.ToString ' 'Case "System.Reflection.AssemblyKeyFileAttribute" ' ' Value = CType(attrib, AssemblyKeyFileAttribute).KeyFile.ToString ' 'Case "System.Reflection.AssemblyProductAttribute" ' ' Value = CType(attrib, AssemblyProductAttribute).Product.ToString ' 'Case "System.Reflection.AssemblyTrademarkAttribute" ' ' Value = CType(attrib, AssemblyTrademarkAttribute).Trademark.ToString ' 'Case "System.Reflection.AssemblyTitleAttribute" ' ' Value = CType(attrib, AssemblyTitleAttribute).Title.ToString ' 'Case "System.Resources.NeutralResourcesLanguageAttribute" ' ' Value = CType(attrib, Resources.NeutralResourcesLanguageAttribute).CultureName.ToString ' 'Case "System.Resources.SatelliteContractVersionAttribute" ' ' Value = CType(attrib, Resources.SatelliteContractVersionAttribute).Version.ToString ' 'Case "System.Runtime.InteropServices.ComCompatibleVersionAttribute" ' ' Dim x As Runtime.InteropServices.ComCompatibleVersionAttribute ' ' x = CType(attrib, Runtime.InteropServices.ComCompatibleVersionAttribute) ' ' Value = x.MajorVersion & "." & x.MinorVersion & "." & x.RevisionNumber & "." & x.BuildNumber ' 'Case "System.Runtime.InteropServices.ComVisibleAttribute" ' ' Value = CType(attrib, Runtime.InteropServices.ComVisibleAttribute).Value.ToString ' 'Case "System.Runtime.InteropServices.GuidAttribute" ' ' Value = CType(attrib, Runtime.InteropServices.GuidAttribute).Value.ToString 'Case "System.Runtime.InteropServices.TypeLibVersionAttribute" ' Dim x As Runtime.InteropServices.TypeLibVersionAttribute ' x = CType(attrib, Runtime.InteropServices.TypeLibVersionAttribute) ' Value = x.MajorVersion & "." & x.MinorVersion ' Case "System.Security.AllowPartiallyTrustedCallersAttribute" ' Value = "(Present)" ' Case Else ' '-- debug.writeline("** unknown assembly attribute '" & TypeName & "'") ' Value = TypeName 'End Select
If nvc.Item(Name) = "" Then nvc.Add(Name, Value) End If Next
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I was hoping to just find how to get the AssemblyVersion of a build. This is much better!
Careful. We don't want to learn from this. --Bill Watterson, "Calvin and Hobbes"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Can this be done for web application featuring asp.net with vb.net ? If so which classes etc should be called.
Thanks. A.M
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
|
Very, Very, Very Nice work. I am a new programmer, but I am already guilty for using the cool-looking-type about boxes--with little to no information about my product and or build. I now see the difference and why it is important. Thanks for the info and example and keep up the good work.
Badger
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Very nice. I wish I had found your article earlier; I just spent the last 6 hours rolling a very similar design. A few thoughts that might be of interest though;
+ Make it a separate GAC'd library. Something that you can painlessly reference in a new app, and customize. Using EntryAssembly, this works nicely, and lets all your about boxes upgrade at the same time. (you might have done this; I haven't looked at the source yet)
+ Allow for adding ad-hoc information. I discovered (after some head-banging) that custom attributes are actually quite useful, and that you can extend the AssemblyInfo.cs/.vb file with your own. So things like an AssemblyAuthor tag, AssemblyCompanyWebsite, etc. become easily definable (from your own namespace), and can be absorbed by the about box as well. It also seems useful to support the loading of generic key-value pairs from a resource file. In my case, I tell my about box the name of the resource (App.resources in my case), and it grabs it from the entry assembly and blindly appends everything into a ListView.
+ Allow linking to further detail. It's possible to make subitems in a listview appear blue and underlined, hyperlink-style. I found this really useful for adding links to support websites, bug reporting, documentation, feature suggestion lists, an author mailto:, and things like that. Makes it very easy to connect the app into the business network supporting it.
+ Support a logo. Found this surprisingly easy. You can drop a .png into your main app project, and mark it as an embedded resource. Then tell the about box the name of the resource, and it can load and display it with just a few lines of code. I originally tried GIF, but PNG supports full a full alpha channel, so the logo transparency blends flawlessly with the about box background.
Thanks again for the idea on the complete assembly list, and detailed inspection; very very wise, and I'm definately adding both tonight...
|
| Sign In·View Thread·PermaLink | 4.00/5 (1 vote) |
|
|
|
 |
|
|
Thanks, glad you liked it!
MichaelWells wrote: Make it a separate GAC'd library. Something that you can painlessly reference in a new app, and customize. Using EntryAssembly, this works nicely, and lets all your about boxes upgrade at the same time. (you might have done this; I haven't looked at the source yet)
I am very much against GAC-ing *anything*, just because of all the problems I've seen it lead to. That's just me though..
MichaelWells wrote: Allow linking to further detail. It's possible to make subitems in a listview appear blue and underlined, hyperlink-style. I found this really useful for adding links to support websites, bug reporting, documentation, feature suggestion lists, an author mailto:, and things like that. Makes it very easy to connect the app into the business network supporting it.
I thought about doing a lot of the fancier stuff, but after surveying a ton of About Boxes in commercial applications-- just to see what's out there-- I found that they are almost always super-simple. Very few do anything other than the basics (what I have implemented), and possibly an email address and website. Not to say that they can't do more, of course, but I think it's good for the about box to focus on doing one thing, and doing it well: being the dogtags for your application. That's good advice for every form in any application, I think..
That said, I especially like the idea of AssemblyCompanyWebsite and AssemblyAuthorEmail (or equivalents) because those are quite common.
As for the other customizations (logo, hyperlinks) that's what the source is for May I recommend the RichTextBox for simple URL and mail encoding with no code? Not always the most elegant, but it's free!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks for your great article. But the buttons at title bar interest me. I can't find any code in your project. How does it action? How to draw non-client button and capture non-client event? Would you please give some sample in C#?

|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Those are multi-monitor buttons provided through the great UltraMon software! Unfortunately I don't have any examples of how to do this in .NET, but I'm sure it's possible with the right Win32 API interop calls..
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|