Thursday, September 07, 2006

When you're creating a GUI control in Visual Studio, your public properties will be displayed in the Property Grid and you can customize this by using attributes such as the Category attribute or the DisplayName attribute. Another nice attribute to use sometimes is the Browsable attribute. If you set the Browsable attribute to false (i.e. <Browsable(False)> in VB.NET), it won't be displayed in the property grid. I typically use this to hide properties that really have no function from the GUI side.

Here's the point of the post though: even if you set a property's Browsable attribute to false, it will still be accessed by the designer. In other words, if you've got code like a Debug.Assert or something in there, it will still fire, regardless of the Browsable attribute's value. So, you might consider putting a DesignMode check around code like that because it will give you some weird behavior when running in the designer.

UPDATE:
No one ever told me about the DesignerSerializationVisibilityAttribute! You can set DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) on a property and it won't get serialized into code!

posted on Thursday, September 07, 2006 7:53:25 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Thursday, August 31, 2006

Back in January, Gaston Milano posted a sort of wishlist for the next version of Visual Studio. One of the features he wanted was the ability to filter properties down in his Properties window. Here's a screenshot of what he had in mind (from his posting):

Well, I've got great news! Corneliu from his parallelthinking blog has created an addin to do just that! It works by using his Hawkeye application, which is a sort of superpowered Spy++ for .NET. Hawkeye allows you to attach to any running .NET application and inspect various .NET controls and their properties as well as methods, events, and all sorts of other fun things. With Hawkeye, you can even CHANGE properties and INVOKE methods! On the fly! And the other .NET application never even has to know about it!

His VS Properties Extender uses the functionality of Hawkeye to add a text box to the properties window at run time which allows you to filter to commands. It works great, too. Now I'm really curious what other sorts of functionality could be added to an application like Visual Studio by using Hawkeye. Check it out!

(found via Larkware)

posted on Thursday, August 31, 2006 10:57:37 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Monday, August 28, 2006

Check out the Syntax Highlighter plugin for Windows Live Writer.

///<summary>
///A test for CreateGallery (string)
///</summary>
[TestMethod(), ExpectedException(typeof(ArgumentNullException))]
public void CreateGallery_TestNull()
{
    GalleryHelper target = new GalleryHelper();

    string imageDirectory = null;

    target.CreateGallery(imageDirectory);
}

Works just like you'd expect! If it had a few more niceties like taking out leading spaces, it'd be even better!

posted on Monday, August 28, 2006 7:31:05 PM (Central Standard Time, UTC-06:00)  #    Comments [2]
 Saturday, August 26, 2006
Jeff Atwood just posted an excellent Programmer's Bill of Rights, which suggests things such as providing at least two monitors for every programmer, providing a fast PC, etc. One thing in particular that he mentioned is that all programmers should have quiet working conditions, and I couldn't agree more, because I sit near some of loudest people at work. Of course, we're all in cubes, too, so it isn't like I can shut the door. I hear more about someone's daughter than I do about work, too. I ended up taking Jeff's advice and dropping some money on some nice headphones that drown out some of the noise, but I think I would end up blowing my ear drums out before I'd drown out all of the noise. I recently moved cubes over to this new, loud area, but it wasn't much better before - I used to sit next to the break room, so you can imagine what it was like there, too.
posted on Saturday, August 26, 2006 8:33:57 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Friday, August 25, 2006
I wanted to put out a quick post to say thanks to Dave Reed and his Infinities Loop blog. He recently moved his blog from blogger.com to weblogs.asp.net, which is great for me, because blogs on blogger.com are blocked by my corporate proxy! He's had some great content out there, particularly for those of us who haven't really had a chance to TRULY understand ASP.NET. He has a couple of great "TRULY understanding" posts: one on ViewState and another on static vs. dynamic controls.

If you're like me and haven't had a chance to really dig into ASP.NET, check out his blog!

posted on Friday, August 25, 2006 6:52:32 AM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Tuesday, August 22, 2006
In one of my projects at work, I ran into a situation where Visual Studio 2005 didn't recognize my designer files (i.e. MainForm.Designer.vb) so all of the serialized designer code was getting stuck in a new InitializeComponent in my code file (i.e. MainForm.vb). Of course, because my Designer code was a partial class, I got an error out of it. In my experience, this behavior is sort of obscure, but here is an easy fix.

Open up the project file (which in my case was GUI.vbproj) in a text editor and do a search for your Designer file. You should see some tags that look like this:
<Compile Include="CustomControl.Designer.vb">
</Compile>
<Compile Include="CustomControl.vb">
<SubType>UserControl</SubType>
</Compile>
Now, what it SHOULD look like is this:
<Compile Include="CustomControl.Designer.vb">
<DependentUpon>CustomControl.vb</DependentUpon>
</Compile>
<Compile Include="CustomControl.vb">
<SubType>UserControl</SubType>
</Compile>
I've bolded the important part for you. Basically, my project file, for whatever reason, was missing the <DependentUpon> tag. If you add that back, Visual Studio should start behaving as expected again.
posted on Tuesday, August 22, 2006 3:23:40 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Monday, August 21, 2006

(After yesterday's post on getting metadata from images in .NET, I've decided to continue to post some of the information I find regarding working with images in .NET.)

As I mentioned yesterday, I'm working on an application to export a folder of images over to a webpage or something (I haven't decided on the output format, whether to a database, XML, or what). I've been learning quite a bit about .NET image support (primarily from GDI+) and it really provides a lot right out of the box. Today's post is on resizing an image without losing too much quality in the process.

The first resource I found was System.Drawing.Image.GetThumbmailImage. My first thought was, "Surely it isn't this easy!" I was right - it wasn't that easy. Check out this quote from the remarks section of the documentation:

The GetThumbnailImage method works well when the requested thumbnail image has a size of about 120 x 120 pixels. If you request a large thumbnail image (for example, 300 x 300) from an Image that has an embedded thumbnail, there could be a noticeable loss of quality in the thumbnail image.

My first test was resizing an image that was originally 2580 x 1932 down to 640 x 483. It looked quite nasty after trying to call GetThumbnailImage. Back to Google I went, but thankfully I soon hit paydirt. I found the "Resizing a Photographic image width GDI+ for .NET" article by Joel Neubeck on CodeProject. The meat of the functions he provides are that they give examples on how to carry the resolution of the original image over and also how to use bicubic interpolation. With his code, my 640 x 483 picture still looks great - just as good as Picasa does for its export code!

I will say that I am still using the GetThumbnailImage method for what it was primarily designed for: thumbnails. As the documentation says, it works fine up to about 120 x 120. I'm personally using a max width/height of 90 for my thumbnails right now. Here's an example of a call to GetThumbnailImage just so we'll get some code in this post:

private static void CreateThumbnail(Image img, string path, Size imageSize)
{
    // Only call this to create thumbnails smaller than 120 x 120! Otherwise, check out 
    // http://www.codeproject.com/csharp/imageresize.asp.
    
Image thumbnail = img.GetThumbnailImage(
        imageSize.Width,
        imageSize.Height,
        (
delegate { return false; }),
        
IntPtr.Zero);
    thumbnail.Save(path);
}

As you can see, I'm using an anonymous delegate for the 3rd parameter, which presumably only exists for backwards compability, because it isn't called. It makes more sense to me to do it this way than to add a function that won't even be called.

posted on Monday, August 21, 2006 9:12:41 PM (Central Standard Time, UTC-06:00)  #    Comments [0]
 Sunday, August 20, 2006

So, this is a little off-topic compared to some of the other posts I have done lately, but it was something new to me and I thought I'd share it for information... and as a means of helping me remember it later!

At home, I have been using both Microsoft Digital Image Library 2006 and Picasa to manage my digital pictures because I like features in both. Microsoft's product provides some nice editing features and tagging while I've been using Picasa's export as a webpage in an XML format for mine and my wife's blog. One of the hassles between the two, though, is that they store captions for images differently. I was having to manually copy and paste captions between both programs, which is huge time waster. Because I wanted to continue using Digital Image Library, I decided to write a program to export the gallery. (I know there are programs out there that can do this for me... I've got the programmers' disease!)

To write this program correctly, I wanted to still be able to pull out information like the captions I had set for each picture. I ended up digging around a little bit researching the different implementations for storing metadata in the files and it would appear that Microsoft's implementation seems more standard than that of Picasa. In fact, some digging in one of my pictures using a hex editor shows the proprietary <picasastamp> enclosing a <caption> tag. At least that means it will be easier to pull the caption out.

Probably one of the best places to start for researching this is the MSDN how to: Read Image Metadata. It details the PropertyItem that you can get off of the System.Drawing.Image class. There is actually a collection of PropertyItem objects from the PropertyItems collection. Basically, you can loop through the collection and inspect each item's Id property until you find the one you are looking for. That's the easy part. The hard part is figuring out which Id you're interested in because they don't have user-friendly names like "Caption" or "Title" - they're in hex. Thanks to this sort of random post, I was able to find out that the captions I was looking for were 0x9c9b.

So, without further ado, here's a very simple GetCaption method that will return the caption/title based on an Image passed to you:

public static string GetCaption(Image img)
{
    if (img == null) 
        throw new ArgumentNullException("img");

    Encoding enc = Encoding.UTF8;

    foreach (PropertyItem prop in img.PropertyItems)
    {
        if (prop.Id == 0x9c9b)
        {
            return enc.GetString(prop.Value).Replace("\0", "").Trim();
        }
    }

    return string.Empty;
}

Let me know what you think!

If you're interested in more about the PropertyItem class, check out these pages - they've got some nice information on them as well.

- http://www.codeproject.com/cs/media/photoproperties.asp
- http://www.pixvillage.com/blogs/devblog/archive/2005/03/27/176.aspx

posted on Sunday, August 20, 2006 8:21:59 PM (Central Standard Time, UTC-06:00)  #    Comments [2]
 Friday, August 18, 2006
This post is just as much for me as it is to share...

I was attempting to create a automatically resizing GroupBox control that would let users drop controls on it and it would grow as the controls dropped. I also wanted to offer a FlowLayoutPanel-like ability so that it would slide controls that were lower up if higher controls were hidden. Doesn't sound too bad and we would use it enough that it made sense to encapsulate it in a custom control. I started looking into inheriting from GroupBox and adding a FlowLayoutPanel to it's control collection. Then I hooked into the ControlAdded event for the GroupBox so that I could move the control from the GroupBox to the FlowLayoutPanel. I'm just walking you through the steps in my thinking process here... this apparently isn't how it should be done, because though it worked if controls were added at run-time, it gave me a weird designer error that said "'child' is not a child control of this parent" (like these guys did).

What research I came up with looked like I would have to create my own custom designer to support code like this, that would also include code serialization into InitializeComponent. Yay. A little more research brought me this link, though. It details how to drop controls onto a UserControl at design time. It is a slightly different approach, but it provided what I needed. Basically, the solution also involves using a ControlDesigner, but the code seems much less involved than if I were handling code serialization.

Here's a code snippet to give you an idea of what I did:
<Designer(GetType(AutoResizeGroupBoxDesigner))> _
Public Class AutoResizeGroupBox
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
Public ReadOnly Property FlowPanel() As FlowLayoutPanel
Get
' Where _flowPanel has already been added to the designer
Return _flowPanel
End Get
End Property

' code snipped...

End Class

Public Class AutoResizeGroupBoxDesigner
Inherits ControlDesigner
Public Overrides Sub Initialize(ByVal component As System.ComponentModel.IComponent)
MyBase.Initialize(component)
Dim autoGroupBox As AutoResizeGroupBox = DirectCast(component, AutoResizeGroupBox)
EnableDesignMode(autoGroupBox.FlowPanel, "FlowPanel")
End Sub
End Class
posted on Friday, August 18, 2006 6:58:52 AM (Central Standard Time, UTC-06:00)  #    Comments [0]