List view items with Highlight brush set to Red

I haven’t worked with Windows Forms that much since last December.  This year, I’ve worked on projects that involve ASP.NET, Linq to Sql, and web services.  I’m currently taking a class in WPF from Foothill College—you can visit Cal Schrotenboer’s page to see a description of the classes offered.  The classes are all conducted online and you don’t have to travel to the campus at all.  In addition, you can join the MSDN Academic Alliance program and get access to Microsoft software (include Expression Studio and Windows 7).

Learning WPF has been extremely fun and rewarding, but at times, frustrating!  I’m trying to do as much in XAML as possible.  I am doing an Image Viewer for my last class project, making great strides in some areas—and then getting stuck when I try to add what must be the most simple feature ever: changing the ListViewItem selected color. 

But due to my ignorance a simple task can take hours!  My only desire was to see the color of selected items changed to something other than the default.

My first take—which led me down the wrong path—was to add a Style Trigger.  The Trigger would set the Background color when IsSelected was true on the ListViewItem.  Or so I thought!

<Style TargetType="{x:Type ListViewItem}">
  <Style.Resources>
    <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="DarkCyan" />
  </Style.Resources>
  <Style.Triggers>
    <Trigger Property="IsSelected" Value="true">
      <Setter Property="Background" Value="Red">
      </Setter>
    </Trigger>
  </Style.Triggers>
</Style>

Nope, no matter how I tried to massage that, it didn’t work.  Even more frustrating, I changed IsSelected to IsMouseOver and that worked fine.

As William Han explained to me on .NET Developer Forums, ListViewItem's base type ListBoxItem changes a Border background in control template instead of ListBoxItem.Background on the selected item.  William suggested a workaround could be modifying SystemColors.HighlightBrush since ListBoxItem's control template uses it.

Here’s what did work:

  <Page.Resources>
        <Style TargetType="ListViewItem">
            <Style.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
            </Style.Resources>
        </Style>
  </Page.Resources>  
  
  <Grid>
  <ListView>
    <ListViewItem>Item 1</ListViewItem>    
    <ListViewItem>Item 2</ListViewItem>
    <ListViewItem>Item 3</ListViewItem>    
  </ListView>
  </Grid>

It’s a brute force method—changing the highlight color for all ListViewItem objects, but it works, as you can see by the Kaxaml screenshot on top.

Yesterday, I placed version 1.0.0. of Crystal Image Toolkit on CodeProject with an expanded version of the tutorial.

If you’re visiting here from CodeProject, here is an index of articles I’ve written about misc features in the Crystal Image Toolkit:

  1. Factory For Crystal Collector.
  2. Header Groups for Image Thumbnail Grid View.
  3. Scaling Thumbnail Images with CrystalImageGridView.
  4. A brief explanation of the pan window for CrystalPictureShow.
  5. Screenshots of using CrystalMemoryCollector with CrystalPictureShow to create a wait dialog.

The articles may refer to earlier versions of the toolkit, please ignore and use the latest version.  The samples included in the toolkit will give you a better idea of how to use the various features mentioned in the articles.

Part one of a series of tutorials explaining how to use the Crystal Image Toolkit.  In this example, I will show you how to quickly add an image thumbnail control to your Windows Form application.

First of all, download the latest Crystal Image Toolkit from my downloads page.  Compile the code using the solution that I provided. 

Designer toolbox

Create a new Windows Forms application in Visual Studio. In the Visual Studio toolbox, add a new tab called Crystal Image Toolkit (to separate it from Crystal Reports) and add the controls found in CrystalToolkit.dll.

step 1 - drop CrystalImageGridView on Form

Select the control called “CrystalImageGridView”.  This is the control that displays the thumbnail images.  Drag this control onto your new Form.

step 2 - set properties

Now go to the Properties window, with “crystalImageGridView1” selected.  Change the Dock property to Fill.  Change the Orientation property to Vertical.

Control after set properties

After you set the properties, the image grid control should look like the above picture.  The blue borders simply give you an idea of what the image items will look like in the selected state.  The broken image bitmap is just a placeholder.  At runtime, your images will be placed inside this blue rectangle.  Yes, you can change many other properties, including the border color, background color, and so on.  But for now, let’s accept the defaults.

Let’s write a little bit of code.  Go to the events tab and double click on the Load event of the Form.  Now that you are in Form1.cs, let’s add this field:

   1: /// <summary>

   2: /// CrystalCollector object, assists in retrieving image files in a specified folder.

   3: /// </summary>

   4: private CrystalCollector _theCollector = null;


The CrystalCollector object is the controller in this framework. 

The collector works with the CrystalImageGridView and the CrystalImageGridModel.  It finds image files set in the ImageLocation property, creates objects to mirror those files as CrystalImageItem objects, and places those within the model.  The collector spawns a background thread to start thumbnailing the image items; this thread sends events to the view to tell it when an image thumbnail is available.  That is what happens behind the scenes, but here in this simple example, you only need to add this method for your Form:

   1: private void InitCollector()

   2: {

   3:     _theCollector =

   4:         CrystalCollectorFactory.DefaultFactory.CreateCollector(CrystalCollectorType.CrystalFileCollector);

   5:  

   6:     // Add the CrystalImageGridView object to the collector.

   7:     // The collector will work with the view to draw the images.

   8:     _theCollector.SetupView(crystalImageGridView1);

   9:  

  10:     // Optional:

  11:     // Set an initial folder to collect images.

  12:     // If no folder is set, collector starts at MyPictures in WinXP or Pictures folder in Vista

  13:     //CrystalCollectorFactory.DefaultFactory.InitCollectorSource("c:\\myImages", _theCollector);

  14:  

  15:     // Tell CrystalFileCollector to collect the images in the ImageLocation folder.

  16:     _theCollector.CollectImages();

  17: }

  18:  

  19: private void Form1_Load(object sender, EventArgs e)

  20: {

  21:     InitCollector();

  22: }

  23:  

  24: protected override void OnFormClosing(FormClosingEventArgs e)

  25: {

  26:     if (_theCollector != null)

  27:     {

  28:         _theCollector.StopCollection();

  29:     }

  30:     base.OnFormClosing(e);

  31: }


InitCollector creates the collector object using a factory.  The type it chooses is a file-based collector.  You could just create a new CrystalFileCollector yourself, but it is better to use the Factory.  This can be used to track down objects getting created/destroyed later.

Once the collector has been created, we call SetupView, passing in the CrystalImageGridView object that we dropped onto the Form.  This must be done before we gather any images.  The collector creates a CrystalImageGridModel object and links up the view to it.  The model must know about certain view properties, which affects how the image items will be displayed.

CollectImages is called at the end, which tells the collector to look at the ImageLocation and start gathering data about the images found there.  In this case, we did not set the ImageLocation—the default is set to your Pictures folder (on Vista) or MyPictures folder (in WinXP).  If you want to initialize another ImageLocation, use the code I commented out above the call to CollectImages.

Form1_Load calls InitCollector.  But there’s also the override to OnFormClosing, which calls the StopCollector method on the collector object.  This call will stop any background threads going on to thumbnail the images.

Simple form with thumbnail image control

When you press F5 and run, you should see the thumbnails for whatever images are in your Pictures folder.  Hopefully, they will be images of friends, family, and pets instead of comic book images!  Play around with this form—you should see that the control responds to resizing events, making the number of image items per row grow or shrink the form.  You can do ctrl-click and shift-click on image items for multiple selection as well.

Thumbnail image location

Where are these thumbnail images stored?  The CrystalThumbnailer object, which does the work of creating them, has a property called ThumbnailLocationRoot.  By default, it is your AppData folder, under the name of your Company and Product name.  The collector creates a sub folder here, based on the hash number of the original location.  It stores the thumbnails here—the largest thumbnail size it needs to display.  In these simple demos, the thumbnails will exist permanently.  I’ve decided the behavior to retain or erase them is application dependent and left the choice up to you.

This is obviously a very simple example, but I have included many sample Form applications in the toolkit.

SimpleImageGridDemo shows a slightly more complex version of this application, by allowing you to open any folder and view the thumbnail images.

PictureShowDemo

PictureShowControllerDemo shows you how to create a more realistic picture viewing application.  A split container is used to hold the CrystalImageGridView in Horizontal orientation on the bottom pane.  The top pane contains the CrystalPictureShow control, which is used to display images, magnify them, and present slideshows.  There’s also a panning window that appears whenever the image is displayed in a non-fit mode.

Crystal Image Toolkit 1.0.0 is ready to download.

Enhancements:

Factory object:  CrystalCollectorFactory was added to allow you to create the correct collector objects based on file or folder input.  Using this factory will help keep track of when objects are created.  See this post for details.

CrystalCollector: added StopCollector as a pure virtual method.  StopCollector stops the collection operation initiated by CollectImages.


CrystalCollector:  SortCrystalList with CrystalSortType.DisplayName will now sort the image items with case insensitivity.  This sort option uses DisplayNameLower property in CrystalImageItem.


CrystalImageItem:  Added ImageCorrupted property.  True means image is corrupt and cannot be displayed, false means it is safe to display.


CrystalImageItem:  Added DisplayNameLower property.  Takes the DisplayName string and does a ToLower call.


CrystalImageItem, CrystalCollector, and CrystalImageGridModel all implement IDisposable.  This objects hold references to Image objects, which can now be disposed of more efficiently.


Event notification: All events are now broadcast using the EventNotifier class.  This utility walks through the invocation list of a multi-cast delegate and checks to see if InvokeRequired is true before calling the delegate.

Code Cleanup:

Exception Handling Refactoring: Previously, the Crystal  Image Toolkit was eating all exceptions.  This was a bad practice and actually hid threading errors that were occurring in the forms and controls that were using the toolkit.  Now those exception handlers have all been removed, except for one in CrystalFileCollector:  LoadImage.  The exception here is caught when an image is corrupt and cannot be loaded from the disk.  In that case, ImageCorrupted in CrystalImageItem is set to true, see below.


CrystalCollector: AddView method was removed.  I had intended to support multiple views, but I need to wrap up this toolkit for the time being and move on to other projects.  Only 1 view is supported.  AddView has been changed to SetupView.  The internal list of views has been removed.

 

Download: Crystal Image Toolkit 1.0.0.  Totally free, open-source, C# .NET Framework 2.0 for Windows Forms, works with both Visual Studio 2005 and 2008.

Factory for CrystalCollector

| | Comments (0)

Factory Diagram for Crystal Collector objects.

I’ve implemented a factory for all the CrystalCollector-based objects in the upcoming release of the Crystal Toolkit.  Using the factory helps keep track of objects getting created and deleted.  I’ve also implemented IDispose in CrystalCollector and CrystalImageGridModel.  These objects keep a reference to Image objects that need to be released when the Crystal objects are no longer needed.  CrystalLogger, which works with log4net, reports when CrystalCollectors are created in the factory and released in the Dispose methods.

The next version of the Crystal Toolkit will be 1.0, at last.

Here's another question that is often asked, reversing a string in C.  I think most people can get through this one easily, creating a temporary char variable and swapping the first and last values.  However, I came across a slight optimization to this method:

char *strrev2(char *s,int n)
{
       int i=0;
       while (i<n/2)
       {
           //uses the null character as the temporary storage,
           // for the front character.
           *(s+n) = *(s+i);
           // swap the front and last character.
           *(s+i) = *(s + (n - i -1));
           // put the front character in n-1 position.
           *(s+n-i-1) = *(s+n);
           i++;
       }
       // tie off the string with the NULL character.
       *(s+n) = '\0';
       return s;
}
char* strrev(char* s)
{
       return strrev2(s, strlen(s));
}

With this example, you don't need a temporary variable.  You use the char at the end of the string, the one that holds the NULL (\0) character that marks the end of the string.  Just remember to put the NULL character back when you are done.

A slight twist: afterward, the interviewer may ask you how to reuse this code to reverse a sentence.  This is why I wrote strrev2.  I won't write the code that does this, but here's the general algorithm:

  1. Call strrev to reverse the entire string at one time.
  2. Take the new string, parse the tokens separated by whitespace and delimeters.
  3. Reverse each token inside the reversed sentence string.
  4. The result should be a perfectly reversed string.

Interview Coding: atoi, itoa in C

| | Comments (0)

Converting a string to an integer is another classic interview question.  I was asked this one during my Microsoft interview in 1991 and I was so nervous I butchered it badly.  I still remember sweating bullets even now, almost twenty years later.  Fortunately, the manager took pity and allowed me to play a debugging exercise where I discovered the mistakes I had made.

The function atoi is shorthand for ascii to integer.  You call it like this:

    int fooval = atoi("123");

It can be written in a few lines of code, but it hinges on your awareness of the Ascii table.  ASCII stand for American Standard Code for Information Interchange; it’s the fundamental building block that allows computers to exchange information.  There are codes for uppercase alphanumeric characters (A-Z) in the range 65-90, and the lowercase alphanumeric characters (a-z) in the range 97-122.  Numerals 0-9 are in the range 48-57.  Your knowledge of the Ascii table can help you not only with these interview coding exercises, but many others as well.

Another concept that will aid you is the knowledge of how C works with character pointers:

char* index = "123";

The index variable is a pointer, but it points at the first character in the char string (1).  If you increment the index, it will point at the next char value (2).  You can determine when you’ve reached the end of the string by testing the value to see if it is the NULL character, \0.  All C strings are terminated by this value.  You have to remember to look for the NULL character when searching C strings, and if you are creating a new string, you have to remember to place that at the end.

Now that we are armed with this knowledge, we can write atoi:

int atoi( char* pStr ) 
{
  int iRetVal = 0; 
 
  if ( pStr )
  {
    while ( *pStr && *pStr <= '9' && *pStr >= '0' ) 
    {
      iRetVal = (iRetVal * 10) + (*pStr - '0');
      pStr++;
    }
  } 
  return iRetVal; 
}

This implementation starts off by clearing out iRetVal with 0.  In case the string is NULL or empty, the return value will be 0.  We loop through the string, looking for numeric characters.  When we encounter one, we determine the integer value (*pstr - ‘0’) and add that iRetVal.  iRetVal gets multiplied by 10 to make the correct scale value.  Note that the first time around, iRetVal is 0.  Finally, we increment the char index to move to the next character in the string.

This is pretty simple and straightforward, although it isn’t perfect.  What if there is a negative sign in the string (“-123’).  Whenever you write code during an interview, it won’t be perfect.  It’s important to be able to identify weaknesses and bugs in your code to the interviewer.

The reverse function for integer to ascii, itoa, is a bit more complex.  I was asked to write this just a couple of years ago during an interview.  Funny enough, the engineer who asked me to write it was an ex-Microsoft engineer.  I solved it correctly for a default case where the number is base 10.  He showed me a way to write it for base 10, base 16, or any base value.  You will call itoa like this:

    printf("itoa(123) = %s\n",itoa(123));

This is the default way of calling itoa.  The return value is “123” which is output by the printf function.  But, as this ex-Microsoft engineer asked me, what if we wanted to output the hexadecimal value, “7b”?  You would call an overloaded itoa function like this, with the base (16 for hexadecimal) like this:

    printf("itoa(123) = %s\n",itoa(123, 16));

Now let’s write the function.  We will actually write two functions here.  If you do this during interview, you will score extra points.  First the itoa function that takes the base as the second parameter:

char *itoa(int n, int b) 
{
    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    int i=0, sign;
    char *s = new char[100];
    
    if ((sign = n) < 0)
        n = -n;

do {
s[i++] = digits[n % b];
} while ((n /= b) > 0);

if (sign < 0)
s[i++] = '-';
s[i] = '\0';

return strrev(s);
}


And now the default itoa function, which assumes the most common case, where we call it for base 10 numbers:

char *itoa(int n) 
{
    return itoa(n, 10);
}

This itoa function with the base parameter makes use of a character array called digits.  This array contains the values 0-9, followed by the lowercase alphanumeric values a-z.  These values will be used for the individual characters in the return string value:

    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";

The first thing we need to do is check the sign of the number we need to convert.  Is it less than 0?  If so, we’ll need to remember this for later.

Now we do a tight compact loop through number n:

    do {
        s[i++] = digits[n % b];
    } while ((n /= b) > 0);

The char string holding the return value starts getting the individual digits chopped off number n.  We chop them off using the modulus operator: %.  When b is set to 10 and n is 123, n % b becomes 123 mod 10.  The value of that mod is 3.  Index 3 inside the digits array is the character ‘3’.

The loop prunes the digits off the number n.  N gets reduced (by the base number) as the loop goes on with the expression “n /= b”.  When N is 123, the first time through this loop, it becomes 12.

Let’s picture what happens to the return char string s and integer n as the loop progresses:

First pass:  n = 12, s = “3”

Second pass: n = 1, s = “32”

Third pass: n = 0, s = “321”

The loop terminates when n reaches 0.  Now we take a look at the sign value.  If the number was negative, we had the char minus sign (1).  Don’t forget to mark the end of the string with the NULL character as I mentioned previously.

Now we almost have the return string value, except it is in reverse.  You can call strrev to reverse it back to the proper form and return the value.

This may lead into writing a function to reverse a string value…which I will write next time.

I once heard Chris Sells (in a podcast with Scott Hanselman) mention that the .NET Framework is so vast, that he was constantly uncovering objects/methods that he had already written himself.  Here's a few things that I discovered during the past year, thanks to my co-workers (Andy, Jon, Geoff).  Some of these items are so simple that I was embarrassed not to know about them.  Here is the list:

1.  The simple string.IsNullOrEmpty method. 

 if (string.IsNullOrEmpty(imageFileName))
   return false;

Before I encountered this, I was always writing C++ style tests like "if (string != null) && (if string.Length > 0)".  Doing both in one shot is a great piece of shorthand.

2.  The powerful Path object. 

Path.GetDirectoryName(filePathName);

One of the first things I did in C# was to port over a number of C++ methods that dissected a path string, chopping off the extension, returning the folder path, etc.  A nice exercise, but it was totally unnecessary.  The Path object has the following methods: GetDirectoryName, GetExtension, GetFileName, GetFileNameWithoutExtension.

3.  The beautiful BackgroundWorker object.

public class PSPUnpackWorker : BackgroundWorker 
{
    public PSPUnpackWorker()
    {
        WorkerReportsProgress = true;
        WorkerSupportsCancellation = true;
        DoWork += PSPUnpackWorker_DoWork;
    }

Have you ever wanted to perform a task in a background thread?  You can easily spawn off a worker thread on your own by creating a Thread object and calling Start.  But what if you wanted your worker thread to raise an event, to tell you it's completed step X out of 100?  And perhaps hook up this data to a progress bar control?

Don't do it the hard way, the .NET Framework is all setup to accommodate this pattern already with the BackgroundWorker object.

The ComicShowControllerDemo in the Crystal Toolkit has an example of a BackgroundWorker communicating to a ProgressBar control.  First, as shown above, I derive my own specialized class from the BackgroundWorker.  In the constructor, I set the properties in base class to true, WorkerReportsProgress and WorkerSupportsCancellation.  The former property allows me to call the ReportProgress method.  The control containing the ProgressBar can subscribe to the ProgressChanged event of the background worker and determine the percentage of completion.

PSPUnpackWorker _unpackWorker = new PSPUnpackWorker();
_unpackWorker.ProgressChanged += unpackWorker_ProgressChanged;
_unpackWorker.RunWorkerCompleted += unpackWorker_RunWorkerCompleted;
_unpackWorker.RunWorkerAsync();

When RunWorkerAsync is called, the DoWork method inside PSPUnpackWorker is executed:

void PSPUnpackWorker_DoWork(object sender, DoWorkEventArgs e)
{
    foreach (CrystalImageItem imageItem in Collector.GridModel)
    {
        if (CancellationPending)
        {
            e.Cancel = true;
            return;
        }        
        ....
        if (CanRaiseEvents)
        {
            float percent = ((float)++index/(float)Collector.GridModel.Count)*100f;
            int percentDone = Convert.ToInt32(percent);
            ReportProgress(percentDone);
        }
    }
    e.Result = true;
}

Inside the DoWork method, you can perform whatever tasks are required in the background worker.  I've sandwiched these between checking to see if a cancel action was requested at the beginning, and reporting the progress at the end.

4.  The mighty MethodInvoker object.

_imageForm.Invoke(new MethodInvoker(delegate
 {
     _viewerMain.SetImageInfo(imageInfo);
     _viewerMain.ToolTipText = imageItem.ToolTipText;

SetScale();
}));


In Windows Forms programming, you often encounter the problem of needing to update the controls on the main form, running in the main UI thread.  If you try to update these controls without using Invoke or BeginInvoke, you will no doubt encounter an error such as "Cross-thread operation not valid: Control X accessed from a thread other than the thread it was created on."

You could take the main Windows Form, test InvokeRequired, and then call Invoke or BeginInvoke.  You would have to create a delegate and pass this as a parameter.  The delegate would take care of calling the controls on the main form.

There's an easier way: use an Anonymous method, coupled with the MethodInvoker (as shown above).  Using this technique, it's like creating an inline delegate.  The code inside the delegate has access to the outerscope variables, such as imageInfo.  It's another great piece of shorthand.

When the code inside the delegate portion becomes too large, I will place them inside a new method, and call that method within the MethodInvoker:

ImageGridView.BeginInvoke(
    new MethodInvoker(delegate
    {
        InitInitialImageImp(index, ignoreGroupHeader);
    }));

You can find some other interesting tips about MethodInvoker and using invoke with UI controls on TimL's .NET Blog.

1 2 3 4 5 6 Next