21 Mar 2013 @ 9:08 AM 

Most application frameworks/languages provide access to the Command Line parameters passed to your application. Generally it is passed to your application as either a string or an array of strings. What you do not get automatically is the functionality to parse out switches.

Command-line parameters used to be the only way to communicate with a program. Fundamentally, the command line was your UI into the program. Different platforms took different approaches. Unix-like systems typically take the “invasive” route; they replace wildcards and then pass the resulting command line to the application. This means that you don’t have to do any shell expansion of wildcards (as it is known) but you have to account for the fact that your command line could include a lot of files. It’s a trade-off, really. Either way, I figured for the purposes of this library, we could stick to the platform- if the program is run with a wildcard, you’ll see the wildcard on windows, but it will have been expanded if you run the same program on Linux. It might be worth adding an option to “auto-expand” wildcards- just for consistencies sake, but that seems like a post for another day.

Either way, most applications also include flags and switches. This is more a De Facto standard that has cropped up- there is no hard and fast rulebook about what flags and switches are or how you are supposed to pass arguments, which can cause no end of confusion when it comes to reading application documentation. the .NET language just gives you the string, and leaves it up to you to decide how to interpret it. Some language libraries provide functionality to parse the Command Line appropriately, such as Python. C# doesn’t come with such a class…. So let’s make one!

First we need to determine what exactly can exist in a command line. My method allows for two things: Switches, and arguments. A Switch can include an argument, separated from the switch with a colon. For example:

  1.  
  2. someprogram.exe /switch:argument /sw:"file 1.txt" "filename.txt" /doall
  3.  

In this case, we have three switches- switch, sw, and doall. The first two include an argument. My “syntax” allows for quotes in the arguments of switches as well as the “loose” arguments. We will evidently need classes to represent and parse Arguments, and another one for Switches. The parsing can be done sequentially. Although it’s not a recommended best practice, I chose to use by reference parameters in the class constructors. In order to keep things generic and accessible, both Switches and Arguments will derive from a CommandLineElement abstract class, which will force each base class to implement toString(). the ArgumentItem class will be used for parsing both “loose” arguments, as well as arguments found after a switch.

Arguments

Arguments are simple- if the first letter of the position is a quote, we look for the next quote that isn’t doubled up. Otherwise, we look for either the next whitespace or the end of the string. Each argument only needs the actual argument value.

  1.  
  2.   /// <summary>
  3.         /// Represents an Argument. This includes arguments that exist bare on the command line as well as arguments used with a given switch.
  4.         /// </summary>
  5.         public class ArgumentItem : CommandLineElement
  6.         {
  7.             public static ArgumentItem Empty = new ArgumentItem();
  8.             private String _Argument;
  9.  
  10.             protected internal ArgumentItem()
  11.             {
  12.                 _Argument = "";
  13.             }
  14.  
  15.             /// <summary>
  16.             /// Construct an Instance from the given string, assuming the start of an Argument element at the given position.
  17.             /// </summary>
  18.             /// <param name="strParse">Command Line to parse.</param>
  19.             /// <param name="Position">Location to start. This variable will be updated with the ending position of the argument that was discovered upon return.</param>
  20.             public ArgumentItem(String strParse, ref int Position)
  21.             {
  22.                 int startpos = Position;
  23.                 int sloc = startpos;
  24.                 while (char.IsWhiteSpace(strParse.ElementAt(sloc))) sloc++;
  25.                 if (strParse.ElementAt(sloc) == ‘"’)
  26.                 {
  27.                     sloc++;
  28.                     while (true)
  29.                     {
  30.                         if (sloc >= strParse.Length) break;
  31.                         bool doublequote = strParse.Length > sloc + 2 && strParse.Substring(sloc, 2).Equals("\"");
  32.                         //if we found a quote and it’s not a double quote…
  33.                         if (strParse.ElementAt(sloc) == ‘"’ && !doublequote)
  34.                         {
  35.                             sloc++;
  36.                             break;
  37.                         }
  38.                         if (doublequote) sloc++; //add an extra spot for the dual quote.
  39.  
  40.                         sloc++;
  41.                     }
  42.                 }
  43.                 else
  44.                 {
  45.                     sloc = strParse.IndexOfAny(new char []  {‘/’, ‘ ‘}, sloc);
  46.                 }
  47.                 _Argument = strParse.Substring(Position, sloc – startpos);
  48.                 Position = sloc;
  49.             }
  50.  
  51.             /// <summary>
  52.             /// returns the Argument this Object represents. This will include quotation marks if they were used in the originally parsed string.
  53.             /// </summary>
  54.             public String Argument
  55.             {
  56.                 get { return _Argument; }
  57.             }
  58.  
  59.             /// <summary>
  60.             /// implicitly converts an ArgumentItem to a String.
  61.             /// </summary>
  62.             /// <param name="value">ArgumentItem to implicitly convert.</param>
  63.             /// <returns>the result from calling Chomp() on the given instance.</returns>
  64.             public static implicit operator String(ArgumentItem value)
  65.             {
  66.                 return value.Chomp();
  67.             }
  68.  
  69.             /// <summary>
  70.             /// returns the Argument value. If it starts with and endswith quotation marks, they will be removed.
  71.             /// </summary>
  72.             /// <returns></returns>
  73.             public String Chomp()
  74.             {
  75.                 if (_Argument.StartsWith("\"") && Argument.EndsWith("\""))
  76.                     return _Argument.Substring(1, _Argument.Length2);
  77.                 else return _Argument;
  78.             }
  79.  
  80.             public override string ToString()
  81.             {
  82.                 if (Argument.Any(Char.IsWhiteSpace))
  83.                     return "\"" + Argument + "\"";
  84.                 else return Argument;
  85.             }
  86.         }
  87.  

The constructor is where the important stuff happens. the by reference parameter is used to define the starting position, and we update it when the constructor returns to point at the character after the argument. The class also defines some statics for implicit conversions to and from a string.

Now that we have the Argument class, we can define the Switch class. The actual syntax of switches often depends on the application but also seems to depend on the platform. for example, Linux tools favour the hyphen for single letter flags, and double hyphens for multi-character flags. Switches are also called flags. forward slash is not generally used as a switch or flag indicator. Windows platforms prefer the forward slash but generally allow for single hyphens as well. We aim to support all three syntaxes, and make the client application not have to worry about which it is. We also add support for arguments- a switch can be specific as such:

 
someprogram /d:argument.exe
 

The element after the colon will be parsed as an argument and attached to the switch itself. But enough waffling- on to the Switch:

  1. public class Switch : CommandLineElement
  2. {
  3.     internal static String []  SwitchPreceders = new string []  {"–", "-", "/"};
  4.     private ArgumentItem _Argument = ArgumentItem.Empty;
  5.     private String _SwitchValue;
  6.  
  7.     public Switch(String strParse, ref int StartLocation)
  8.     {
  9.         while (String.IsNullOrWhiteSpace(strParse.ElementAt(StartLocation).ToString())) StartLocation++;
  10.         var sLoc = StartLocation;
  11.         var retrieved = SwitchPreceders.
  12.             FirstOrDefault((s) => !(sLoc + s.Length > strParse.Length) &&
  13.                                   strParse.Substring(sLoc, s.Length)
  14.                                           .Equals(s, StringComparison.OrdinalIgnoreCase));
  15.         if (retrieved == null)
  16.         {
  17.             throw new ArgumentException("Passed String " + strParse +
  18.                                         " Does not have a switch preceder at position " + StartLocation);
  19.         }
  20.         var NextSpace = strParse.IndexOfAny(new char []  {‘ ‘, \t, ‘/’, ‘:’}, sLoc + 1);
  21.         //if(((NextSpace-sLoc)-sLoc+1) <= 0) throw new ArgumentException("Error Parsing Switch");
  22.         _SwitchValue = strParse.Substring(sLoc + 1, NextSpace – sLoc – 1);
  23.         sLoc += retrieved.Length; //we don’t want the switch itself.
  24.         //now we need to determine where the Switch ends. colon or space seems reasonable. If a colon, the next entity will be an argument.
  25.         StartLocation = NextSpace;
  26.         //if the char at NextSpace is a Colon…
  27.         if (strParse.ElementAt(NextSpace) == ‘:’)
  28.         {
  29.             //interpret as an argument
  30.             NextSpace++;
  31.             _Argument = new ArgumentItem(strParse, ref NextSpace);
  32.         }
  33.         StartLocation = NextSpace;
  34.     }
  35.     public String SwitchValue
  36.     {
  37.         get { return _SwitchValue; }
  38.     }
  39.     public String Argument
  40.     {
  41.         get { return _Argument; }
  42.     }
  43.     //Constructs an instance of a switch from the given location.
  44.     public static bool SwitchAtPos(String strParse, int Location)
  45.     {
  46.         var retrieved = SwitchPreceders.
  47.             FirstOrDefault((s) => !(Location + s.Length > strParse.Length) &&
  48.                                   strParse.Substring(Location, s.Length)
  49.                                           .Equals(s, StringComparison.OrdinalIgnoreCase));
  50.         return retrieved != null;
  51.     }
  52.     public override string ToString()
  53.     {
  54.         return "//" + _SwitchValue + ":" + _Argument.ToString();
  55.     }
  56.     public bool HasArgument()
  57.     {
  58.         return _Argument.Equals(ArgumentItem.Empty);
  59.     }
  60.         }

With the basic parsing logic completed, we need to consider how we want this to be used. Best way is to think of how we would like to use them:

  1. CmdParser cp = new CmdParser();
  2. if(cp.HasSwitch("f") && cp ["f"] .HasArgument()) _usefilename=cp ["f"] .Argument;

Some basic take-aways from this. First, the Core Parser Object needs to provide an Indexer. In the above example, we see it is accessing Switches by passing in the Switch name. Other possibilities include using direct numeric indexes to refer to any argument- much like you would access elements in the framework provided args [] String array. Another possibility is to have the Argument of a switch auto-populate, rather than be null, when accessed:

  1.  _usefilename=cp.HasSwitch("f")?cp ["f"] .Argument:_usefilename;

1,924 total views, 30 views today

Posted By: BC_Programming
Last Edit: 21 Mar 2013 @ 09:10 AM

EmailPermalinkComments (0)
Tags
 20 Jan 2013 @ 8:14 PM 

One common problem that comes up in the creation of stylized windows such as Splash screens is the desire to have parts of the form be “shadowed” or, in other words, blend with the forms behind them. You can see this effect in action with other application splash screens, such as photoshop:

Photoshop Splash Screen in front of a dead-sexy website

Photoshop Splash Screen in front of a dead-sexy website

What witchcraft is this? How does Photoshop do it? One of the first things you might try with .NET is something like the TransparencyKey of the form. Of course, this doesn’t work, instead you get this unsightly ring of the transparency key around it.

The solution, of course, is a bit more complicated.

Starting with Windows 2000, the Win32 API and Window Manager has supported the idea of “layered” Windows. Before this, Windows had concepts known as “regions”; this basically allowed you to define regions of your window where the background would show through. This didn’t allow for per-pixel blending, but that sort of thing was not widely supported (or even conceived of, really) by most graphics Adapters. The “Layered” Windowing API basically provided a way to allow Per-pixel Alpha. Which is what we want.

Windows Forms, however, does not expose this layered window API through it’s methods or properties, with the exception of the opacity property. I don’t recall if WPF does either, but I would be surprised if it’s capabilities exposed more of the Layered Windowing API. Basically- we’re on our own

So what exactly do we need to do? Well, one thing we need to do is override the CreateParams property of the desired form, so that it adds the WS_EX_LAYERED style to the window, letting the Windowing Manager know it has to do more work with this window. Since we would like a neatly decoupled implementation, we are going to create a class that derives from Form, but use that as the base class for another form. This let’s us override the CreateParams property within that base class, but this has a bit of a caveat as well, since I’ve found that makes the Designer a bit annoyed and it complains and won’t let you use the designer. So you will have to deal with that particular point yourself. (I imagine there is a way to make it work, but I don’t want to bloat the class with such functionality). If necessary, the appropriate functions and the CreateParams override can be put into the classes that need this functionality instead.

Here is the PerPixelAlphaForm class. By the way, I’m going with Visual Basic.NET because there are already a million hits for this functionality with C#:

  1.  
  2.  
  3. Imports System
  4. Imports System.Drawing
  5. Imports System.Drawing.Imaging
  6. Imports System.Windows.Forms
  7. Imports System.Runtime.InteropServices
  8.  
  9.  
  10.  
  11. ‘ class that exposes needed windows GDI Functions.
  12. Public Class Win32
  13.  
  14.  
  15.     Public Enum Bool
  16.         bFalse = 0
  17.         bTrue = 1
  18.     End Enum
  19.  
  20.  
  21.  
  22.     <StructLayout(LayoutKind.Sequential)>
  23.     Public Structure Point
  24.  
  25.         Public x As Int32
  26.         Public y As Int32
  27.  
  28.         Public Sub New(x As Int32, y As Int32)
  29.             Me.x = x
  30.             Me.y = y
  31.         End Sub
  32.     End Structure
  33.  
  34.  
  35.     <StructLayout(LayoutKind.Sequential)>
  36.     Public Structure Size
  37.         Public cx As Int32
  38.         Public cy As Int32
  39.  
  40.         Public Sub New(cx As Int32, cy As Int32)
  41.             Me.cx = cx
  42.             Me.cy = cy
  43.         End Sub
  44.     End Structure
  45.  
  46.  
  47.  
  48.     <StructLayout(LayoutKind.Sequential, Pack:=1)>
  49.     Structure ARGB
  50.  
  51.         Public Blue As Byte
  52.         Public Green As Byte
  53.         Public Red As Byte
  54.         Public Alpha As Byte
  55.     End Structure
  56.  
  57.  
  58.  
  59.     <StructLayout(LayoutKind.Sequential, Pack:=1)>
  60.     Public Structure BLENDFUNCTION
  61.         Public BlendOp As Byte
  62.         Public BlendFlags As Byte
  63.         Public SourceConstantAlpha As Byte
  64.         Public AlphaFormat As Byte
  65.     End Structure
  66.  
  67.  
  68.  
  69.     Public Const ULW_COLORKEY As Int32 = &H1
  70.     Public Const ULW_ALPHA As Int32 = &H2
  71.     Public Const ULW_OPAQUE As Int32 = &H4
  72.  
  73.     Public Const AC_SRC_OVER As Byte = &H0
  74.     Public Const AC_SRC_ALPHA As Byte = &H1
  75.  
  76.  
  77.     <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)>
  78.     Public Shared Function UpdateLayeredWindow(hwnd As IntPtr, hdcDst As IntPtr, ByRef pptDst As Point, ByRef psize As Size, hdcSrc As IntPtr, ByRef pprSrc As Point, crKey As Int32, ByRef pblend As BLENDFUNCTION, dwFlags As Int32) As Bool
  79.  
  80.     End Function
  81.  
  82.     <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)>
  83.     Public Shared Function GetDC(hWnd As IntPtr) As IntPtr
  84.  
  85.     End Function
  86.     <DllImport("user32.dll", ExactSpelling:=True)>
  87.     Public Shared Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As Integer
  88.     End Function
  89.     <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)>
  90.     Public Shared Function CreateCompatibleDC(hDC As IntPtr) As IntPtr
  91.  
  92.     End Function
  93.     <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)>
  94.     Public Shared Function DeleteDC(hdc As IntPtr) As Bool
  95.  
  96.     End Function
  97.     <DllImport("gdi32.dll", ExactSpelling:=True)>
  98.     Public Shared Function SelectObject(hDC As IntPtr, hObject As IntPtr) As IntPtr
  99.  
  100.     End Function
  101.     <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)>
  102.     Public Shared Function DeleteObject(hObject As IntPtr) As Bool
  103.  
  104.     End Function
  105. End Class
  106.  
  107. Public MustInherit Class PerPixelAlphaForm
  108.     Inherits Form
  109.  
  110.     Public Sub New()
  111.         FormBorderStyle = FormBorderStyle.None
  112.     End Sub
  113.     Public Overloads Sub SetBitmap(bmp As Bitmap)
  114.         SetBitmap(bmp, 255)
  115.     End Sub
  116.     Protected Overrides ReadOnly Property CreateParams() As CreateParams
  117.         Get
  118.             Dim cp As CreateParams = MyBase.CreateParams
  119.             cp.ExStyle = cp.ExStyle Or &H80000
  120.             Return cp
  121.  
  122.  
  123.  
  124.         End Get
  125.     End Property
  126.     Public Overloads Sub SetBitmap(bmp As Bitmap, Opacity As Byte)
  127.  
  128.         If bmp.PixelFormat <> PixelFormat.Format32bppArgb Then
  129.             Throw New ApplicationException("The Bitmap must be a 32bpp with a Alpha Channel")
  130.         End If
  131.  
  132.         ‘Create Compatible DC with the screem
  133.         ‘Select bitmap with 32bpp with alpha into the compatible DC
  134.         ‘call UpdateLayeredWindow
  135.  
  136.         Dim ScreenDC As IntPtr = Win32.GetDC(IntPtr.Zero)
  137.         Dim MemDC As IntPtr = Win32.CreateCompatibleDC(ScreenDC)
  138.         Dim hBitmap As IntPtr = IntPtr.Zero
  139.         Dim oldBitmap As IntPtr = IntPtr.Zero
  140.         Try
  141.             hBitmap = bmp.GetHbitmap(Color.FromArgb(0))
  142.             oldBitmap = Win32.SelectObject(MemDC, hBitmap)
  143.             Dim thesize As Win32.Size = New Win32.Size(bmp.Width, bmp.Height)
  144.             Dim pointSource As Win32.Point = New Win32.Point(0, 0)
  145.             Dim topPos As Win32.Point = New Win32.Point(Left, Top)
  146.             Dim blend As Win32.BLENDFUNCTION = New Win32.BLENDFUNCTION()
  147.             blend.BlendOp = Win32.AC_SRC_OVER
  148.             blend.BlendFlags = 0
  149.             blend.SourceConstantAlpha = Opacity
  150.             blend.AlphaFormat = Win32.AC_SRC_ALPHA
  151.             Win32.UpdateLayeredWindow(Handle, ScreenDC, topPos, New Win32.Size(Size.Width, Size.Height), MemDC, pointSource, 0, blend, Win32.ULW_ALPHA)
  152.  
  153.         Catch ex As Exception
  154.  
  155.         Finally
  156.             Win32.ReleaseDC(IntPtr.Zero, ScreenDC)
  157.             If Not hBitmap = IntPtr.Zero Then
  158.                 Win32.SelectObject(MemDC, oldBitmap)
  159.                 Win32.DeleteObject(hBitmap)
  160.             End If
  161.             Win32.DeleteDC(MemDC)
  162.  
  163.         End Try
  164.  
  165.  
  166.     End Sub
  167. End Class
  168.  
  169.  

This provides the core implementation. The “Win32″ class here provides the needed API declarations.

In order to use this, we need to make our form derive from it, and then use the inherited SetBitmap function:

  1.  
  2. Public Class Form1
  3.     Inherits PerPixelAlphaForm
  4.     Private useBackground As Bitmap
  5.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
  6.         useBackground = New Bitmap("D:\pngtest.png")
  7.         SetBitmap(useBackground)
  8.     End Sub
  9. End Class
  10.  
  11.  

(Here I just used an image on my second Hard disk that had a transparency channel, but the Bitmap constructor parameter can easily be changed.). The result:

The code in action

The code in action:

The Project I used for this can be downloaded from here . This was created using Visual Studio Professional 2012, Update 1- other editions or earlier versions may have problems with the project or source files.

1,222 total views, 38 views today

Posted By: BC_Programming
Last Edit: 20 Jan 2013 @ 08:17 PM

EmailPermalinkComments (6)
Tags
 17 Dec 2012 @ 12:33 PM 

Many developers and programmers may describe their experience with a language or technology as them having “mastered” it. But is this really the case?

Take me as an example- I cannot think of a single technology that I feel comfortable saying I “mastered”; even those that I’ve worked with extensively, such as the C# language itself, or the .NET Framework, or the Java Base Class Library, I don’t feel safe saying I’ve “mastered”. The way I see it, “mastered” implies that for any question about that technology, you will have an answer for. I cannot, personally, say that I’ve reached that stage. In fact, it’s debatable whether any person reaches that stage about anything. C#-wise I’ve worked extensively with the framework, serialization, dynamic dispatch (via C# 4.0/5.0′s “dynamic” keyword).

“Mastered ” for me implies that there is nothing left to learn for that person in that field; but when you think about it, that’s sort of impossible, particularly in regards to software development, programming, and even just computers in general. There is always more to learn, there is always ground left uncovered, and as you uncover that ground you reveal just how much you don’t know. One of my favourite quotes, attributable to Dave Ward, is:

“the more you know, the more you realize just how much you don’t know. So paradoxically, the deeper down the rabbit hole you go, the more you might tend to fixate on the growing collection of unlearned peripheral concepts that you become conscious of along the way.”

This is from Scott Hanselmans blog post on a similar subject- titled “I’m a phony. Are you?”   In many ways, I find myself identifying with this- For example, I still often try to rationalize my reception of an MVP Award as “oh, it’s nothing” or “must have been a mistake”; that is, like the post describes and quotes, people that feel like an imposter. The MVP Award is not just given away to anybody, though. Best I can think of would be that my Blog posts with regard to C# Programming (and programming topics in general) were that good . Personally I have a hard time really believing that.

Much like Scott Hanselman describes in the post, I try to learn a new programming language- even if I don’t use it for a large project, the exposure to new concepts and problem domains can really improve my work in those languages that I’ve used for a longer period, and hold off stagnation. But even so, very seldom do I ever get deep enough into those languages to truly grasp their idioms.

And yet, at the same time, When I push myself into a language- I  eventually catch on. For example- and I’ve written about this before- I languished with Visual Basic 6 for a very long time. I thought it was good enough- there is nothing better. This is what I told others, but who was I trying to convince, them, or me? I aim for the latter. I don’t know what prompted me to do so but I moved on to C#. Now in fairness even then I did at least give other languages a shot, but VB6 was the only one that felt “familiar”; that is, that I could easily type code in without looking up documentation. At some point I’d had enough. Visual Basic 6 was an old, dead, language, and so too would my experience if I didn’t do something. I moved to C# instead of VB.NET mostly out of my own self-interest; I still had many projects in VB6 and I (possibly erroneously) thought that learning VB.NET might cloud my ability to work on those projects. That, and C# seemed to be the de riguer standard.

My first applications were rather simple. I made an attempt to duplicate the functionality of HijackThis first- this was a learning experience, particularly since I was now able to use those OO concepts I knew about but was never able to leverage properly in VB6, which only had interface inheritance. I moved forward with a simple game. This was to become the first version of BCDodger, which is a relatively simple game. The major stumbling block here was with regard to the use of multiple threads and handling concurrency and handling repainting. I eventually looked at my ancient ‘Poing’ game, which was an arkanoid/breakout clone I had written in Visual Basic 6. It was terrible- the drawing code was limited, everything was ugly and there was very little “whizbang” to the presentation. I decided to make a variant of the game in C#, using a better design and a class heirarchy. This snowballed pretty quickly, and I now have a game that is able to dynamically load new components from scripts (which it compiles at startup), has a fully-featured editor that is able to find and show any class that implements a specific interface or derives from a given class in portions of the interface; for example the “add block” toolbar dropdown get’s populated with every single block in the game, categorized based on attributes on the appropriate classes.

With Freelancing, my results have been met with an overwhelmingly positive response, in one case, where I was tasked with using Semantic cleanup of a longest common subsequence algorithm, there was some back-and forth over what might be the best output given what we were working with; the client eventually responded that they did some research and are pretty sure it’s impossible. By which time, I had actually finished implementing it. Needless to say, the results were very positive. The same applies for a similar project where I created the Product Key registration and generation logic in a self-contained class.

The nice thing is that these projects exposed me to things I might not have dealt with before. WPF was a technology I didn’t use until it’s appeal in a given situation made it very much preferable. Full screen, resolution independence was a lot easier with WPF than it would be with Windows Forms- so now I have experience with WPF as well. Naturally the overwhelmingly boring nature of Semantic cleanup of the output of a longest common subsequence algorithm (I nearly fell asleep writing that…) meant I wouldn’t otherwise explore it; however it turned out to be very interesting as well as mentally taxing. There was, of course, some confusion. But each time I was able to look at it with freshly rested eyes (apparently staying up for 48 hours working on the same problem clouds your judgement), I was able to find some very obvious fix or addition to resolve that which I was working on. I guess it boils down to- if you just write what you <want> you will always try to work on something interesting, but not always useful. When working on an actual project- for somebody else, or as an otherwise professional undertaking, what you work on might not always be fun, but it will always be useful to somebody. So in some respects this rubs off; with my own projects, I try to make them fun to work on, but might not always pay the closest attention to how useful they are; in retrospect when working in a professional capacity, whether they are useful is the most important goal. A program that isn’t useful is useless by definition.

This isn’t to say the experience isn’t occasionally frustrating; when you provide a product or service to somebody or something, I like to think there is some sort of a promise of the workmanship of that product; for example on more than one occasion, a product has stopped suiting the original purpose; for example, this could be because it was not as scalable as it needed to be, or perhaps a database corruption has caused some other problems. Whatever the case, since the product no longer works for the original intent, I could never charge for the service of getting the product working again- because that almost seems like holding them hostage.

At the same time, however, part of me doesn’t like releasing new products, simply because it feels like a lifetime contract to keep working on and improving them. This is part of why the vast majority of projects I’ve worked on I’ve not released. and by “vast  majority” we’re talking about nearly a hundred projects. Some big, some small; most of them unfinished in some fashion but most quite usable.

But I’m getting off the topic of this post. My point is, that I like to think I’m fairly awesome, but at the same time, not too awesome. There is plenty out there to learn, and the pursuit of awesomeness demands that I seek them out, absorb them, and then continue. Like a sponge that comes alive and wants to suction up all the water in the world, my task will never be completed. But that doesn’t stop me from trying anyway.

This applies in a general sense, as well; It’s not just programming that I’m very interested in, but rather tangential topics such as gaming and whatnot. I do also enjoy science-fiction- statistically, as a person who works in Software Development, this is more likely than not- as well as simply general knowledge. A good example being a tab I have open in my browser, where I was researching Eggplant. Why? I haven’t a clue. Just something I wanted to absorb and learn about, I suppose.

Conclusion: learning about Eggplants is unlikely to make you a better programmer. But it never hurts to stay versed on vegetables.

414 total views, 6 views today

Posted By: BC_Programming
Last Edit: 17 Dec 2012 @ 12:34 PM

EmailPermalinkComments (0)
Tags
 02 Aug 2012 @ 2:49 PM 

Now that I finally have a windows machine with a internet connection, I decided to investigate issues I heard of from reliable sources that the BCUpdateLib immediate update was broken, and hopefully fix it.

I was expecting some catastrophic crash or hang or something. I ran it several times. It worked perfectly each time. Both the “immediate” update (which updates a single application, used for checking for and updating the application/game at startup) as well as the “full” updater, which allowed for the downloading of multiple BASeCamp programs.

Not encountering any issues, I decided I may as well touch up the immediate view; it now shows a better display for average speed, and it attempts to estimate the time remaining, which usually works OK. the “full” updater has some flicker issues while downloading that I don’t remember seeing previously, but I’m not sure how to fix that at the moment (and aside from that issue it works OK).

BASeCamp Full Updating Library

Full Updater from the BASeCamp Updating Library.

As can be seen in the images, the Full Updater is essentially a “full” update program, for BASeCamp products. One might think that it is ironic given my Animousity towards ‘Update managers’ But this won’t be some silly program that sits in the background, and will either be a separate program (if at all) or simply integrated into many other BASeCamp applications, which may or may not expose the full functionality. I imagine I will probably be more likely to use the immediate mode. The ‘immediate’ mode is the name I gave for when the update form’s task is not to show and allow different updates but instead to directly start downloading and running a certain Application ID. Typically, this will be done from the main game itself, and as part of a set of prompts determining if there is a new version, and asking if the user would like to update or not, and then this will appear if yes, download the program, launch the installer, and then exit. (It exits so that the installer for that program can run).

Immediate Update

BASeCamp ‘Immediate’ Updater

Of course, as it is now it is still a bit unpolished; for example there is no way to cancel an immediate update and closing that form throws an exception, but these sorts of things should be relatively easy to streamline to make it as simple as possible. I had an additional- almost crazy, even- idea, as well- a “patch” concept, dealt with on the server side as well as the client side. The client code (C#) will know how to integrate a special patch file, and on the server side (PHP) it will know how to inspect two files and see the differences and pass along that file to the client. The idea being that the server can, instead of just giving a full installer, simply give the differences from the current version. I don’t think I will pursue this because in the ideal case it would work with the installer/zip file referenced in the downloadURL, and most people don’t exactly keep those around for easy access.

864 total views, no views today

Posted By: BC_Programming
Last Edit: 02 Aug 2012 @ 02:49 PM

EmailPermalinkComments (0)
Tags
Categories: .NET, API, C#, Windows
 03 Jul 2012 @ 3:23 AM 

I don’t know how but somehow I’ve been awarded the Microsoft MVP award for my contributions to C# technical communities (C# MVP). Of course I am very surprised at this, but I guess I have a short memory. I do have a number of posts and blog entries regarding C#, as well as a lot of forum posts across my various profiles that assist with it. My initial response was actually self-deprecating- “I guess they give them to anybody these days” Which is of course not true.

I cannot help but feel like I got it “by accident”. Most MVPs really are industry professionals with professional expertise, a college education, and a myriad of other qualifications. I feel like an imposter, since I don’t have any post-secondary education and certainly no formal education in any of the domains that I am essentially being awarded for, nor have I actually worked in the industry (well, arguably, that’s not true, if my failing attempt to start a company counts).

That isn’t necessarily to say I don’t deserve the award- I imagine the people responsible for the MVP program are a lot more qualified to make that decision than me.

At this point I’m forced to wonder how it helps me. It does make a very nice thing to put on a resume, but the thing is, I have no place to submit that resume where that award is going to matter. At my last job I think the most my skills were actually used was when I told the manager that, “yes, the monitor needs to be plugged in to work”, or something to that effect. I quit my last job nearly a year ago (Last October) Because I wanted to find something working with computers. The closest things to this are still retail (places like Staples, Best Buy (*Shudder*) and so forth. I applied at every single one I could find, and even got a few interviews, but nothing came of it. Arguably it’s equally likely the fact that shortly after the day I had all those interviews my phone got cut off made follow-ups impossible, so I have absolutely no clue if they ever tried to call me after that (in fairness they did have my E-Mail addresses and I’ve not received anything about it, though it’s more likely they tried to phone, and then just went to the next applicant).

Regardless, let’s be honest. Even that is below my pay grade. I wrote about “getting one’s foot in the door” previously, and this just goes to show how damned impossible it seems to be. The idea of a person who received a MVP Award for sharing C# technical expertise working a minimum wage crap job- or even those above- is almost laughable, but there is absolutely nothing else around here, with one exception.

There is, however, one place I haven’t tried. Pelican Software (which is actually owned by Northwest Forest Products, if memory serves). Well, that’s not quite true, I did in fact try them back when I was a spunky kid whose expertise was pretty much just VB6 and feeling smugly superior… More recently, I did have some dealings with them regarding a Freelance program I had written, “BCJobClock” since it is very similar in many ways to their product, “Tallys”. Things were looking up in that regard but the eventual decision they reached was that BCJobClock was too similar to it. (With the exception that it’s UI is not confusing and it doesn’t cost several thousand dollars). I never actually applied there since to my understanding they really aren’t doing to well and I doubt they’d take the business risk of hiring more staff in their situation. But I may try that anyway. It’s known statistic that companies that employ at least one MVP Award winner are more successful.

At this point I sort of have two options: I can either pursue this BASeCamp thing and try to market BCJobClock (which currently has not appeared on my site at all) for a nominal price, by integrating the existing ProductKey code that I already wrote and used for BASeBlock. But the thing is that the BASeBlock situation really tells me everything I need to know- it’s pointless. Nobody has actually bought a registered copy. And there are very few downloads. It’s online, but in many ways it may as well not be online at all. It just represents 3 years of my spare time that I’ve essentially wasted on a bloody game. It’s still “my product” and I’m proud of it and all that, but pride doesn’t pay bills. And I don’t want to lock away the editor behind the requirement for registration because the Editor is perhaps the part I like the most about the entire thing. Honestly when I was dealing with NWFP regarding the program I just wanted to sell the entire thing and get rid of it. I was sick of it and in some ways I still am. Come to think of it, I’d be more than happy to sign something that gives the complete IP to BCJobClock to NWFP as a condition of working there. Of course it probably wouldn’t get used, but this really would be the only guarantee that I won’t at some point be in direct competition with them, which could very well happen- and this guarantee might be worth it. (I would say so- my program is a heck of a lot easier to use and if I do release it in some manner it’s going to be a lot cheaper, too; though despite their notations it won’t be cutting into any of their market anyway- but in that case it will still be my market share, and not theirs.

Of course, BCJobClock is aimed at a different market. In some ways it’s a Time Management application. I suppose I haven’t discussed the program much since I hadn’t decided what I was going to do with it (well actually there was a page on the main landing site that was a little exuberant on the entire thing at some point, but I removed it when reality punched me in the face with BASeBlock). To Summarize, it basically manages workers and orders for a Repair shop or similar shop. This can be automotive, like the client I originally wrote it for (Somewhere in Iowa, to my understanding) Or it could easily be used for Repair shops or other locations that need a Worker < -> Task management system. The Client program allows employees to clock into and out of orders using a touch-screen interface (naturally I don’t provide the hardware, just the software here), which is done through a WPF C# Application. This program interfaces with a remote MySQL Server using the SQL/Connector which allows the use of ADO.NET Connection and similar objects to work with the MySQL Remote database, which manages all the… data… involved. The Administrator program allows the addition/removal of users, inspection of all orders and users and the time taken on each order as well as each user in total, and all sorts of other information. There is also another little “Watcher” program that is designed for use by people tasked to surpervise work orders and assign tasks to other employees, but aren’t able to have full access to the administrator panel for adding and removing users, getting reports, and all that. Because it is designed for watching users, it also shows Notifications when Users become available for work or when Users or tasks are being “ignored”, and little coloured indicators to show when users/orders are working/being worked on.

It still needs a bit of work to streamline some speed problems that have been encountered by the sole user of the program (which we hacked away with a few INI file changes for their immediate use case), which is related to the fact that the admin program tries to keep it’s view “up to date” by refreshing from the database on a given delay. Unfortunately it picks up a lot of data in the process. Ideally, it would only proceed to actually carry out the “refresh” from the database when it actually knew there was a change, but I’m not really sure how to implement that. Working with databases is frustrating, in that these seemingly basic capabilities seem impossible. (Q.How do I detect when the results of a query changed? A. you perform the query and look through the entire resultset). Of course at that point if you find no changes you just wasted that entire time, so it’s just begging the question.

Actually, with some thought, there is another solution. Relocation. There is simply nothing around here for the type of person who has skills and abilities relevant to a C# MVP Award, so in many ways having it as a bullet point echoes as hollow as the sepia-toned aged mention of my High-School awards from almost ten years ago. So, Maybe it’s time to leave Nanaimo. There simply aren’t any tech jobs here (or I’ve become blind). Not even some sort of more general IT job dealing with servers or the network of a office building or what-have-you.

As I noted however, I never actually inquired NWFP for a career or job, since that wasn’t really my intention at the time. In fact it never even occurred to me. The MVP Award I think helps me here; those aren’t exactly given away freely, there are only two recipients in Nanaimo, Me, and a fellow whose expertise lies in SQL Server; I think there are a dozen on Vancouver Island (though I cannot check).

And if that doesn’t work- well, I guess I’ll have to relocate. On the bright side, My website will still be in the same place :P

610 total views, 2 views today

Posted By: BC_Programming
Last Edit: 03 Jul 2012 @ 03:25 AM

EmailPermalinkComments (3)
Tags
 18 Jun 2012 @ 5:05 AM 

It is a frequent point of debate in many web communities that contain programmers- or back-seat programmers, as it were, to argue that such and such language is better than another, or “if it was written in this language it would be faster”. A lot of the time this is coming from people calling themselves “real programmers” or at least putting on the air that “real” programmers don’t use managed languages like Java or C#; instead they seem to think that “real” programmers use C. And arguably, any programmer should at least be able to read C. However, a good programmer is defined not by what language they use, but their product. a Program that works is a program that works, regardless of the language it was written in.

Anyway, the idea that C- or C++ is superior to Java- or, more generally, that any language is better than another for speed- comes with the implicit assumption that there is some limit. Because you know what can probably create faster code than C++? C. And faster than that? Hand-tuned assembly. Saying C++ is superior is basically saying that it’s worth it to dump the language-based advantages that Java has over C++ for the speed improvement, but somehow it isn’t worth it to make nearly the same amount of gains by switching to C, or by switching from C to Assembly.

Thing is, unless you choose Assembly language, there will always be a language that can make your program faster. But the thing is that we use Programming languages to try to abstract away the details. Instead of having a series of direct instructions to the Floating Point Unit, placing values on the FPU and CPU stacks to perform operations in Assembly, we simply use C or a higher-level language and give them a in-fix expression as we are used to it. Can you sometimes make such code run faster in Assembly? probably; if you take advantage of U and V pipe-lining, make sure to reduce wait states and memory accesses, and so forth.

The bigger question is, “Is it worth it”. And largely, no, it isn’t worth it. In fact, it very seldom is worth it.

another point is that the primary thing you are doing with a programming language now is interfacing with libraries. C++, C, and Assembly don’t make the Libraries run any faster. On windows if you allocate memory- whether by way of creating objects in Java or C#, using new in C++, using malloc, alloc, or whatever in C, it all boils down to OS-specific functions; in Windows case, all of those eventually become calls to LocalAlloc. (Or GlobalAlloc). But whether you make that call from Java or C doesn’t make that function execute faster.

Sure, you can argue that Java or C# probably has more overhead; from new Object() to the actual Allocation there is probably some garbage collection housekeeping and allocating the various fields. But the fact is that in C you will usually be doing that housekeeping yourself anyway; depending on the nature of the memory allocation and what it is for, you will probably be making a lot of calls to malloc() and free(); and every single time is a tango with the evil poltergeist known as memory allocation problems. accidentally forget to free a block of memory and then reassign the value being used to store the pointer- leak. Accidentally call free twice on the same block of memory? double-free. All that extra code adds up, and while I don’t think it quite equals the time penalties associated with Java, which might accure to about a tenth of a second over a constant year of execution, it certainly takes a toll on anybody trying to work on the code. Having to remember “I have to allocate that structure on 16-byte boundaries, and I need to make sure all the char arrays are packed before I call this function,” etc.

And even then, you could easily eke out a similar performance gain over a C or C++ implementation by completely retooling the entire program in Assembly.

For any number of projects written in Java or C#; particularly Games- you can usually find a number of posts on forusm calling for or at least implying that the game should be re-written in C or C++ “for speed”. But why stop at C or C++? Why not call for the entire thing to be re-written in Assembly? Because it’s not worth the effort. But the thing is, it’s not worth the effort to rewrite it in C or C++, either; by the time any such rewrite is completed, computers will have gained enough speed to make the speed improvement moot. The reason that Assembly language isn’t used is because it is no longer necessary.

Programs used to pretty much have to be written in Assembly to be reasonably fast. QDOS/MS-DOS was originally coded in Assembly language. Same with every early Operating System. But those Operating Systems were dead simple by comparison. Now, C is the main language used for Operating Systems. Did C code get faster? Not really, at least not in comparison to hand-tuned Assembly. but the fact is that if writing twice as much code could make your code 10% faster, it was only worth it if that 10% speed difference actually mattered. With the 386, it did, and could often mean your program showing a chart a full second before your competition. Now, your program running 10% faster can often mean that it shows a chart imperceptibly faster, which is hardly justification for tripling the amount of code, making it difficult to maintain with low-level code, and discarding numerous useful abstractions.

That last word sort of touches on what a Programming Language truly is- just a set of abstractions. Let’s take a simple language construct that exists in nearly every language- the simple if. In assembly, the equivalent is to use a compare instruction, and then use a Jump instruction (for example, JNE (Jump if not equal) to Jump to a specific address. Most Assemblers also add features that don’t directly translate to Machine code, such as Macros, making some of this a little easier. Your typical C if statement might take quite a few lines to perform all the needed compares. But it certainly takes more work. Can you make that if run faster? Well, probably, if you are a skilled Assembler programmer. But most people aren’t, and even in the case that one is they would only do this in a time critical inner loop.

Nowadays, inner loop speed is not as critical as it used to be. While a Assembly rewrite to a critical section of code might make that code 200% faster on a 386, that doesn’t mean it would have the same effect on a modern machine, because modern machines have multiple processors, some of which need to access the cache concurrently; there are also numerous pipelining issues to consider. For the most part optimizing code with Assembly on a modern processor is not a feasible solution.

It used to be that Assembly had to be used for everything. If you used C, you were a loser and your code was too slow for general use. Then it was that C was used for everything. If you used C++, you were a loser and your code was too slow for general use. Now, it feels like C++ is used “for everything”. If you use anything else, your code is too slow for general use.

But the fact is that people sticking to these older- if arguably faster- languages are sticking also to the same languages that have made possible almost every single security vulnerability in every modern Operating System. C code just begs to be buffer overflowed, and the simplest oversight can topple a companies entire network, pass along trade secrets, or otherwise be a gigantic pain in the ass. In today’s networked world, Speed is not something that is required of programming languages, both because if it’s myriad trade-offs, such as costing more programmer time and anguish, as well as the fact that you can always buy a faster computer later. Obviously, as long as the algorithms used are sound, you aren’t going to be getting gigantic gains on your implementations just by switching to another language. Sometimes it can even make things slower. Managed languages are a good idea for Application code- and games- simply because they are for Applications. Games don’t need to be close to the hardware because, like a wife after a long marriage, the hardware hasn’t even been touched in years. All “hardware” interaction is done today either through OpenGL or DirectX, which themselves delegate through software-based drivers anyway.

Computer time is cheap. Programmer time is not.

454 total views, no views today

Posted By: BC_Programming
Last Edit: 19 Jun 2012 @ 02:51 AM

EmailPermalinkComments (0)
Tags
 04 Jun 2012 @ 11:09 AM 

I haven’t dealt with XNA heavily as a game platform. My language of choice is C#, but I just never really liked XNA. At any rate, I decided to give it a serious go. My initial dislike is mostly because for 2-D you can’t really draw lines or shapes yourself, instead you basically just draw and rotate some sprites. It’s not awful by any measure. My project in this case is called “BCDodgerX” and is just a re-implementation of BCDodger, the C#/GDI+ implementation of which can be found on my downloads page.

After using it for a bit, (enough to get a playable basic version of the game down, with score display, etc) I have to say I still don’t really like XNA; at least for 2D games. It works, no doubt, but there is something… unnerving.. about it. I can’t put my finger on it.

This version will try to be more “complete” than the C#/GDI+ implementation, which was basically just me messing around with a dual-threaded model (the gameproc() routine looping continuously on one thread, and using Invoke to get the paint routine to draw). BASeBlock further refines the same model, too.

And this is a very short post. it’s a NEW SHORTNESS RECORD!

292 total views, no views today

Posted By: BC_Programming
Last Edit: 04 Jun 2012 @ 11:09 AM

EmailPermalinkComments (0)
Tags
Categories: .NET, API, C#, Programming
 12 Jan 2012 @ 3:11 PM 

In some of my recent posts, I’ve covered the topic of accessing and parsing an INI file for configuration data in a C# Application.

Some may wonder why. After all; the “norm” for C# and .NET applications is to use XML files for configuration information, isn’t it? Well, yes. But to be honest, XML files are a fucking pain in the ass. They aren’t human readable to your average person the same way an INI file is, and getting/setting values is tedious. Primarily, the reason I use INI files is that they are:

  1. Human Readable: Anybody can understand the basic structure of the sections and Name=Value syntax.
  2. Accessible: You don’t need a special editor
  3. Portable: since the entire thing is interpreted using Managed code, it will act the same on any platform (Mono or the MS CLR).

Mostly, I feel that XML, and in many ways other configuration options, are more or less driven by fad. Another option for configuration settings on Windows is the Registry, which is in fact often the recommended method; but this is anything but accessible to the user. Would you rather guide a user to edit a INI file or to fiddle with registry settings?

With that said, INI Files do have their own issues. For example, their data is typically typeless; or, more precisely, the Values are all strings. Whereas using a .NET XML Serializer, for example, you could easily(relatively speaking) serialize and deserialize a special configuration class to and from an XML file and preserve it’s format, with my INI file class there will typically be some work to parse the values.

It was with the idea of turning my string-only INIFile configuration settings into something that can be used for nearly any type that I created the INItemValueExtensions class, which is nothing more than a static class that provides some extension methods for the INIDataItem class. I covered this in my previous post.

The prototypes for the two static functions are:

  1.  
  2. public static T GetValue<T>(this INIDataItem dataitem, T DefaultValue);
  3.  
  4. //and
  5.  
  6. public static void SetValue<T>(this INIDataItem dataitem, T newvalue);
  7.  
  8.  

How would one use these extension methods? Well, here’s an Example:

  1.  
  2. public static void main(String []  args)
  3. {
  4.     INIFile loadini = new INIFile("D:\\testini.ini");
  5.     loadini ["Dates"]  ["TestDate"] .SetValue(DateTime.Now);
  6.     DateTime readvalue = loadini ["Dates"]  ["TestDate"] .GetValue <datetime> ();
  7.    
  8. }
  9.  </datetime>  

Woah, hold the phone! What’s going on here? We’re loading DateTime values directly from the INI File? How does that work?

All the “magic” happens in the getValue generic extension method. The first thing the routine does is check to see if the Type Parameter has a static TryParse() method; if it implements ISerializable and have a TryParse method, than the routine will read the string from the INI file, decode it via Base64, and throw it in a MemoryStream, and then try to deserialize the Object Graph for a Type T using that stream.

If it does implement a TryParse() routine, (like, for example, DateTime) it doesn’t try quite as hard. It takes the string from the INI file and hands it to the Type’s TryParse() routine, and then returns what that gives back. Naturally, the inverse function (setValue) does something somewhat opposite; it checks the Base64 logic, and if so it sets the value of the item to the Base64 encoded value of the serialized object. Otherwise, it just uses toString().

This typically works, particularly with DateTime, because usually ToString() is the inverse of TryParse(). In the case of DateTime, this has a few edge cases with regards to locale, but usually it works quite well. And more importantly, the introduction of allowing any object that implements ISerializable to simply be thrown as an INI value via a Base64 encoded string is useful too, although with large objects it’s probably not a good idea for obvious reasons.

But… I still want to access other settings!

Of Course, an INIFile is only one of any number of ways to store/retrieve configuration settings. And while they don’t typically lend themselves to the same syntax provided by the INIFile class, it would be useful to have some sort of common denominator that can handle it all. That was the original intent of the relatively unassuming ISettingsStorage interface:

  1.  
  2.    public interface ISettingsStorage
  3.     {
  4.         void Save();
  5.         void Load();
  6.         void AddValue(String Category, String ValueName, String Value);
  7.         String GetValue(String Category, String ValueName);
  8.     }
  9.  

This uses a concept known as a “category” which is pretty much the same idea as an INI File section. What makes it different is that, for implementors that use other storage mechanisms, it could have additional meaning; for example, a fictitious XML implementation of ISettingsStorage could use the “Category” string as an XPath to an element; and the Value could be stored/retrieved as a Attribute. a Registry implementation might use it as a Registry path, and so on.

The problem is, even though the INIFile class implements this interface, it’s too basic, and doesn’t provide nearly the syntactic cleanliness that just using the INIFile does. Stemming from that, and because I wanted to try to get a way to store settings directly in a DB, I introduced two events to the INIFile class; one that fires when a Value is retrieved, and one when a value is saved. This way, the event could be hooked and the value saved elsewhere, If desired. Now, to be fair, this is mostly a shortcoming of my interface definition; as you can see above, there is no way to, for example, inspect category or Value names. I toyed with the idea of adding a “psuedo” category/value combination that would return a delimited string of category names, but that felt extremely silly. The creation of a generic interface- or abstract class- that provides all the conveniences I currently enjoy using my INIFile class but allowing me to also use XML, Registry, or nearly any other persistent storage for settings will be a long term goal. For now, I’m content with accessing INI files and having a unclean event to hack in my own behaviour.

My first test of the above feature- whereby it allows values to be TryParse’d and ToString’d back and forth from a given type on the fly- was the creation of a FormPositionSaver class.

The proper way to save and restore a window’s position on Windows is using the GetWindowPlacement() and SetWindowPlacement() API Functions. These use a structure, named, quite aptly, “WINDOWPLACEMENT” to retrieve and set the window position and various attributes. Therefore, our first task is to create the proper P/Invoke’s for these functions:

  1.  
  2.   [DllImport("user32.dll")]
  3.         private static extern int OffsetRect(ref RECT lpRect, int x, int y);
  4.  
  5.  [DllImport("user32.dll")]
  6.         private static extern int GetWindowPlacement(IntPtr hwnd, ref WINDOWPLACEMENT lpwndpl);
  7.  
  8.  [DllImport("user32.dll")]
  9.         private static extern int SetWindowPlacement(IntPtr hwnd, ref WINDOWPLACEMENT lpwndpl);
  10.  

I also include OffsetRect(), but I’ll get to that in a bit. Now the “big one” is the definition of the WINDOWPLACEMENT structure and it’s various aggregate structures. Why? well, in the interest of leveraging the INIFile’s static extensions, Why not define a static TryParse() and a toString() method on the structure that can set and retrieve the member values:

  1.  
  2.  
  3.          [StructLayout(LayoutKind.Sequential)]
  4.         private struct POINTAPI
  5.         {
  6.             internal int x;
  7.             internal int y;
  8.  
  9.  
  10.             public POINTAPI(int px, int py)
  11.             {
  12.                 x = px;
  13.                 y = py;
  14.             }
  15.  
  16.             public static void TryParse(String parseit, out POINTAPI result)
  17.             {
  18.                 //format: (X,Y)
  19.                 //strip out parens.
  20.                 String []  parsed = parseit.Replace("(", "").Replace(")", "").Split(new char []  {‘,’});
  21.                 int kx = int.Parse(parsed [0] );
  22.                 int ky = int.Parse(parsed [1] );
  23.  
  24.  
  25.                 result = new POINTAPI(kx, ky);
  26.             }
  27.  
  28.             public override string ToString()
  29.             {
  30.                 return "(" + x + "," + y + ")";
  31.             }
  32.         }
  33.  
  34.          [StructLayout(LayoutKind.Sequential)]
  35.         private struct RECT
  36.         {
  37.             internal int Left;
  38.             internal int Top;
  39.             internal int Right;
  40.             internal int Bottom;
  41.  
  42.             public override string ToString()
  43.             {
  44.                 return "{" + new POINTAPI(Left, Top).ToString() + "-" + new POINTAPI(Right, Bottom).ToString() + "}";
  45.             }
  46.  
  47.             public RECT(int pLeft, int pTop, int pRight, int pBottom)
  48.             {
  49.                 Left = pLeft;
  50.                 Top = pTop;
  51.                 Right = pRight;
  52.                 Bottom = pBottom;
  53.             }
  54.  
  55.             public static void TryParse(String parsestr, out RECT result)
  56.             {
  57.                 //strip out braces…
  58.                 parsestr = parsestr.Replace("{", "").Replace("}", "");
  59.                 //split at ")-("…
  60.                 String []  Pointstrings = parsestr.Split(new string []  {")-("}, StringSplitOptions.RemoveEmptyEntries);
  61.                 POINTAPI firstpoint, secondpoint;
  62.                 //parse the resulting values. re-add the parens that were removed by the split.
  63.                 POINTAPI.TryParse(Pointstrings [0]  + ")", out firstpoint);
  64.                 POINTAPI.TryParse("(" + Pointstrings [1] , out secondpoint);
  65.  
  66.  
  67.                 result = new RECT(firstpoint.y, firstpoint.y, secondpoint.x, secondpoint.y);
  68.             }
  69.         }
  70.  
  71.  
  72.   [StructLayout(LayoutKind.Sequential)]
  73.         private struct WINDOWPLACEMENT
  74.         {
  75.             internal int Length;
  76.             internal int flags;
  77.             internal int showCmd;
  78.             internal POINTAPI ptMinPosition;
  79.             internal POINTAPI ptMaxPosition;
  80.             internal RECT rcNormalPosition;
  81.  
  82.             public override string ToString()
  83.             {
  84.                 return String.Join(",", new string []
  85.                                             {
  86.                                                 flags.ToString(), showCmd.ToString(),
  87.                                                 ptMinPosition.x.ToString(), ptMinPosition.y.ToString(),
  88.                                                 ptMaxPosition.x.ToString(), ptMaxPosition.y.ToString(),
  89.                                                 rcNormalPosition.Left.ToString(), rcNormalPosition.Top.ToString(),
  90.                                                 rcNormalPosition.Right.ToString(), rcNormalPosition.Bottom.ToString()
  91.                                             });
  92.             }
  93.  
  94.             //parsed a string into a WINDOWPLACEMENT structure.
  95.             public static bool TryParse(String parseme, out WINDOWPLACEMENT result)
  96.             {
  97.                 try
  98.                 {
  99.                     String []  splitvalues = parseme.Split(‘,’);
  100.                     int []  parsedvalues = new int [splitvalues.Length] ;
  101.  
  102.  
  103.                     for (int i = 0; i < parsedvalues.Length; i++)
  104.                     {
  105.                         int.TryParse(splitvalues [i] , out parsedvalues [i] );
  106.                     }
  107.  
  108.                     result = new WINDOWPLACEMENT
  109.                                  {
  110.                                      flags = parsedvalues [0] ,
  111.                                      showCmd = parsedvalues [1] ,
  112.                                      ptMinPosition = new POINTAPI(parsedvalues [2] , parsedvalues [3] ),
  113.                                      ptMaxPosition = new POINTAPI(parsedvalues [4] , parsedvalues [5] ),
  114.                                      rcNormalPosition =
  115.                                          new RECT(parsedvalues [6] , parsedvalues [7] , parsedvalues [8] , parsedvalues [9] )
  116.                                  };
  117.  
  118.                     return true;
  119.                 }
  120.                 catch
  121.                 {
  122.                     result = new WINDOWPLACEMENT();
  123.                     return false;
  124.                 }
  125.             }
  126.         }
  127.  
  128.  

WHEW! that’s quite a bit of code for a structure definition, but we’ll make up for it with the brevity of the actual FormPositionSaver class itself. First, my design goal with this class was to make it basically do all the heavy lifting; it hooks both the Load and Unload event, and saves to and from a given INIFile Object in those events. Since the application I was working on at the time didn’t actually get a Valid INI object until during it’s main form’s Load event, and since there is no way to say “Invoke this event first no matter what” I also added a way for it to be told that hooking the load event would be pointless since it already occured, at which point it will not hook the event and instead set the form position immediately. Values are stored

  1.  
  2. public class FormPositionSaver
  3. {
  4.  
  5.   private Form FormObject = null;
  6.         private INIFile Configuration = null;
  7.         private static readonly String usesectionName = "WindowPositions";
  8.  
  9.  
  10.         ///  <summary>
  11.         /// Create the FormPositionSaver
  12.         ///  </summary>
  13.         ///  <param name="FormObj"> Form to deal with </param>
  14.         ///  <param name="configfile"> INIFile to load and save </param>
  15.         ///  <param name="alreadyloaded"> whether the Load event has fired. If true, will try to set the form position immediately. otherwise, it hooks the Load event and waits. </param>
  16.         public FormPositionSaver(Form FormObj, INIFile configfile, bool alreadyloaded)
  17.         {
  18.             Configuration = configfile;
  19.             FormObject = FormObj;
  20.  
  21.             FormObject.FormClosed += new FormClosedEventHandler(FormObject_FormClosed);
  22.  
  23.  
  24.             if (!alreadyloaded)
  25.                 FormObject.Load += new EventHandler(FormObject_Load);
  26.             else
  27.             {
  28.                 FormObject_Load(FormObject, new EventArgs());
  29.             }
  30.         }
  31.  
  32.         //save the placement...
  33.         private void FormObject_FormClosed(object sender, FormClosedEventArgs e)
  34.         {
  35.             //save placement.
  36.             //all the "tough work" is handled above, and by the INIDataItem Extension methods. Here we
  37.             //can simply use SetValue <>  and set the value. Nice and clean.
  38.             WINDOWPLACEMENT grabplacement = new WINDOWPLACEMENT();
  39.             GetWindowPlacement(FormObject.Handle, ref grabplacement);
  40.             Configuration [usesectionName]  [FormObject.Name] .SetValue(grabplacement);
  41.         }
  42.  
  43.         //Load event: load the form placement, if present, from the INI file we were given in our constructor.
  44.         private void FormObject_Load(object sender, EventArgs e)
  45.         {
  46.             WINDOWPLACEMENT currplacement = new WINDOWPLACEMENT();
  47.             GetWindowPlacement(FormObject.Handle, ref currplacement);
  48.                 //default is wherever it is now if there is a parse problem.
  49.  
  50.             WINDOWPLACEMENT getplacement =
  51.                 Configuration [usesectionName]  [FormObject.Name] .GetValue(currplacement);
  52.  
  53.             //check for previous instances, and offset if there are.
  54.             String thisproc = Process.GetCurrentProcess().ProcessName;
  55.             Process []  existing = Process.GetProcessesByName(thisproc);
  56.             if (existing.Length > 1)
  57.             {
  58.                 //more than one, so offset...
  59.                 OffsetRect(ref getplacement.rcNormalPosition, 16*existing.Length, 16*existing.Length);
  60.             }
  61.  
  62.  
  63.             SetWindowPlacement(FormObject.Handle, ref getplacement);
  64.  
  65.             //load placement...
  66.         }
  67.     }
  68.  

Alright, so maybe I lied a bit. It's not super short. Although a lot of it is comments. Some might note that I only sporadically add doc comments, even though I ought to be adding them everywhere. Well, sue me. I just add them when I feel like it. When I'm concentrating on function, I'm not one to give creedence to form.

This is where I explain OffsetRect(). Basically, if your application is run twice, and you load the form position twice, the second form will open over the first one, and the screen will look pretty much the same. So we detect previous instances and offset by an amount to make it's position different from any previous instances as necessary. That's pretty much the only purpose of OffsetRect.

I have packaged the current versions of cINIFile.cs and the new FormPositionSaver.cs in a zip file, it can be downloaded from here .

312 total views, no views today

Posted By: BC_Programming
Last Edit: 12 Jan 2012 @ 03:11 PM

EmailPermalinkComments (0)
Tags
 01 Jan 2012 @ 5:20 PM 

HAHA! How’s that for a clever title?

Oh… well… ahem… nevermind.

As a avid user of my own INIFile class, which I first write about- at least it’s C# implementation- in my parsing INI files posting , I am always looking for ways to improve it’s usage make it more “accessible”.

Recently, I have been tasked (by way of my new title of “freelance consultant”) with creating several LOB (Line of Business) Type applications. Applications, naturally, have a tendency to lend their implementations to the creation and reading of settings. Being something of a fan of the simplicity of INI Files, I chose to use my INIFile class in the application. It works well, however, I have noticed that I have a lot of duplicate code. More specifically, I typically have to implement a “wrapper” class, which manages configuration information and reads/writes values to and from the INIFile as its own properties are accessed. For example:

  1. public bool PopulateUserOrderDropdown
  2. {
  3.    get {
  4.     bool tparse;
  5.     if(bool.TryParse(OurINI ["Admin.Settings"]  ["PopulateUserOrderDropDown","false"] .Value,out tparse)
  6.         return tparse;
  7.     return false;
  8.    }
  9.    set { OurINI ["Admin.Settings"]  ["PopulateUserOrderDropDown"] .Value;}
  10. }

Nothing too dreadful, but imagine having nearly the exact same thing repeated a number of times! The code is repeated and as Larry Wall says, one of the traits of a good programmer is sloth. I don’t like having to write this same code over and over again! The INIFile is supposed to make it easy!

The trouble here stems from the fact that the INIFile values are only strings; and typically, many settings are represented in the application itself as integers and booleans, dates, and so forth. My first attempt to mitigate the clutter was a static method, which I called xParse:

  1. public static class boolEx
  2. {
  3.     public static bool xParse(String Value, bool Default)
  4.     {
  5.         bool parseresult;
  6.         if(bool.TryParse(Value,out parseresult))
  7.             return parseresult;
  8.         else
  9.             return Default;
  10.  
  11.     }
  12. }

relatively straightforward- basically it’s a shell of what I had repeated over and over again. This mitigated the issue somewhat, so my properties in the wrapper looked like this:

  1. public bool PopulateUserOrderDropdown
  2. {
  3.    get { return boolEx.xParse(OurINI ["Admin.Settings"]  ["PopulateUserOrderDropDown", "false"] .Value); }
  4.    set { OurINI ["Admin.Settings"]  ["PopulateUserOrderDropDown"] .Value;}
  5. }

much more managable, but still, could we not make this more concise? My first thought, was that perhaps I could eliminate the necessity of having the wrapper at all; I recalled two interfaces from my old COM programming days, specifically, IDispatch and IDispatchEx. Surely, I could do something similar?

Unfortunately, the interfaces are for COM, and C# doesn’t have dynamics until Version 4.

So, I fired up Visual Studio 2010 express to see if I couldn’t add the dynamic language constructs to the INIFile class; additionally, since I still need to work with .NET 3.5, I’ll add the new code as a conditional compilation.

The first step was deciding exactly what I wanted to happen. Imagine code like this:

  1. INIFile useINI = new INIFile("settings.ini");
  2. String ConnectionString = (String)useINI.General.ConnectionString;

The holy grail of the INIFile simplicity! Naturally, the .NET framework does provide the facility with which to add this functionality, as part of the System.Dynamic namespace.

The first step was deciding on the method by which to conditional compile. Since projects copy the source of a file to your project folder when you add them, it seemed reasonable to simply add it as a #define right inside the INIFile class itself.

 #define CS4 

And now, I just need to enclose all my new happy stuff in a conditional directive, and I’ll get the best of both worlds- C# 4.0 consumers who keep the #define will be able to use the suave new feature, and older consumers will still be able to work without ripping apart the classes. The code to add this was surprisingly simple; as it stands now the longest method (An implementation of TryDeleteMember, which is never called from C#/VB.NET consumers, so is excessive for my usage). First, obviously we enclose the import statement in the conditional compile; the class headers are conditionally compiled as well, only deriving from DynamicObject with CS4 set.

The core of the new functionality is in the overrides to the Dynamic Object’s TryGetMember.

For the INISection:

  1. public override bool TryGetMember(GetMemberBinder binder,out object result)
  2. {
  3.     result = this [binder.Name] ;
  4.     return true;
  5. }

And for the INIFile…

  1. public override bool TryGetMember(GetMemberBinder binder,out object result)
  2. {
  3.     result = this [binder.Name] ;
  4.     return true;
  5. }

Exactly the same, in fact. This works because of the indexer I added; the indexer will add the item if it doesn’t exist and return the new value, so even if the member name doesn’t exist, the INIFile will simply have that section added.

That’s for the retrieval of erements; to allow the assignment to them in the same fashion, we need to override TrySetMember(). In my case, this was a bit more involved, for flexibility purposes.

For example, code like INIFile.MainSection=”hello” should work, and change the name of the section. And why allow things like assignments from a Dictionary<String, String>, or maybe even a list (assigning a numbered id to set values)? And of course allow setting the Value directly, which will likely use the indexer much as I did for the TryGet… Implementations.

  1.         public override bool TrySetMember(SetMemberBinder binder, object value)
  2.         {
  3.  
  4.             //if it is a dataitem, set it directly.
  5.             if (value is INIDataItem)
  6.             {
  7.                 this [binder.Name]  = (INIDataItem)value;
  8.                 return true;
  9.             }
  10.             else if (value is Tuple<, Object>)
  11.             {
  12.                 Tuple<, Object> theTuple = (Tuple<, Object>)value;
  13.                 INIDataItem getitem = this [binder.Name] ;
  14.                 getitem.Name = theTuple.Item1;
  15.                 getitem.Value = theTuple.Item2.ToString();
  16.                 return true;
  17.  
  18.             }
  19.             else if (value is Tuple<, String>)
  20.             {
  21.                 Tuple<, Object> theTuple = (Tuple<, Object>)value;
  22.                 INIDataItem getitem = this [binder.Name] ;
  23.                 getitem.Name = theTuple.Item1;
  24.                 getitem.Value = theTuple.Item2.ToString();
  25.                 return true;
  26.             }
  27.  
  28.             else if (value is KeyValuePair<, Object>)
  29.             {
  30.  
  31.                 //Allow a KeyValuePair<,Object> to be passed to set Name and Value.
  32.                 KeyValuePair<, Object> castedval = (KeyValuePair<, Object>)value;
  33.                 INIDataItem getitem = this [binder.Name] ;
  34.                 getitem.Name = castedval.Key;
  35.                 getitem.Value = castedval.Value.ToString();
  36.  
  37.                 return true;
  38.             }
  39.             else if (value is KeyValuePair<, String>)
  40.             {
  41.                 //Allow a KeyValuePair<,String> to be passed to set Name and Value.
  42.                 KeyValuePair<, String> castedval = (KeyValuePair<, String>)value;
  43.                 INIDataItem getitem = this [binder.Name] ;
  44.                 getitem.Name = castedval.Key;
  45.                 getitem.Value = castedval.Value;
  46.  
  47.                 return true;
  48.  
  49.             }
  50.  
  51.             else
  52.             {
  53.                 this [binder.Name] .Value = value.ToString();
  54.                 return true;
  55.             }
  56.         }

setting the Value should be equally flexible; since we can, why not?
for example, why not make the following “legal”?

  1. INIFile.Section.Value="newvalue";
  2. INIFile.Section.Value=DateTime.Now;
  3. INIFile.Section.Value=Tuple.Create("NewName","Chicken");
  4. INIFile.Section.Value=Tuple.Create("NewName",DateTime.Now);

The first example sets the Value to a string, the second sets it to a DateTime that is silently casted to a String (using toString(), and the last two use the new C# 4.0 tuples, to set both the name of the value and the value simultaneously.

A more elegant solution would be to add this code to the Indexer, and merely call the indexer with the name and the value and return true if no exception occurs and false otherwise. However, I’m reluctant to go that route since some of the types are C# 4 types (Tuples).

  1.         public override bool TrySetMember(SetMemberBinder binder, object value)
  2.         {
  3.  
  4.             if (value is String)
  5.             {
  6.                 this [binder.Name] .Name = (String)value;
  7.                 return true;
  8.  
  9.             }
  10.             else if (value is List<INIItem>)
  11.             {
  12.                 INISection getsection = this [binder.Name] ;
  13.                 getsection.INIItems = (List<INIItem>)value;
  14.                 return true;
  15.  
  16.             }
  17.             else
  18.             {
  19.                 return false;
  20.  
  21.             }
  22.  
  23.         }

So Now, I’ve got an INI File implementation that supports Dynamic invocation. Well, that’s great… except that the application I first found it clumsy in is using .NET 3.5, so I can’t use the dynamic features. Back at square one.

In C# 2008/3, we might not be able to leverage the power of dynamics, but we do have generics and Extension methods at our disposal. a feasible alternative could be to add a extension method to the INIDataItem class that has a generic type parameter that it will attempt to convert it’s string Value into. First, using ChangeType, second, it can try to invoke a static TryParse on the given Type to parse the “value” string. And if none of that works, it can return a passed in default. This is still more verbose than the dynamic solution, but it has two distinct advantages- first, it’s type-safe, so you get all the intellisense goodness, and second, it’s still shorter than the alternative.

Here is the code, which can be found in the cINIFile.cs file attached to this posting as well.

  1.     public static class INItemValueExtensions
  2.     {
  3.         //extensions for INIDataItem
  4.  
  5.         //normally, INIDataItem is a Name/Value Pair; More Specifically, because of the way INI files are, they are
  6.         //naturally typeless. However, most configuration options are mapped to a different type by the application.
  7.         //and I’ve found it to be a gigantic pain to have to write the same TryParse() handling code over and over.
  8.         //so I added these handy extensions to the INIDataItem class, which provide some functions for setting.
  9.         //I keep them out of the main code simply because that way it doesn’t clutter it up. It’s already cluttered enough as-is.
  10.  
  11.         /// <summary>
  12.         /// Attempts to use Convert.ChangeType() to change the Value of this INIDataItem to the specified type parameter.
  13.         /// If this fails, it will attempt to call a static "TryParse(String, out T)" method on the generic type parameter.
  14.         /// If THAT fails, it will return the passed in DefaultValue parameter.
  15.         /// </summary>
  16.         /// <typeparam name="T">Parameter Type to retrieve and act on in Static context.</typeparam>
  17.         /// <param name="dataitem">INIDataItem instance whose value is to be parsed to the given type.</param>
  18.         /// <param name="DefaultValue">Default value to return</param>
  19.         /// <returns>Result of the parse/Conversion, or the passed in DefaultValue</returns>
  20.         public static T GetValue<T>(this INIDataItem dataitem, T DefaultValue)
  21.         {
  22.  
  23.             //Generic method, attempts to call a static "TryParse" argument on the given class type, passing in the dataitem’s value.
  24.             try
  25.             {
  26.                 return (T)Convert.ChangeType(dataitem.Value, typeof(T));
  27.             }
  28.             catch (InvalidCastException ece)
  29.             {
  30.                 //attempt to call TryParse. on the static class type.
  31.                 //TryParse(String, out T)
  32.  
  33.                 Type usetype = typeof(T);
  34.                 T result = default(T);
  35.                 Object []  passparams = new object []  { dataitem.Value, result };
  36.                 try
  37.                 {
  38.                     bool tpresult = (bool)usetype.InvokeMember("TryParse", BindingFlags.Static, null, null, passparams);
  39.                     if (tpresult)
  40.                     {
  41.                         //tryparse succeeded!
  42.                         return (T)passparams [1] ; //second index was out parameter…
  43.                     }
  44.                 }
  45.                 catch (Exception xx)
  46.                 {
  47.                     //curses…
  48.                     return DefaultValue;
  49.  
  50.                 }
  51.  
  52.             }
  53.             return DefaultValue;
  54.  
  55.         }
  56.         /// <summary>
  57.         /// Logical inverse of the getValue routine… a bit faster to implement…
  58.         /// </summary>
  59.         /// <typeparam name="T"></typeparam>
  60.         /// <param name="dataitem"></param>
  61.         /// <param name="newvalue"></param>
  62.  
  63.         public static void setValue<T>(this INIDataItem dataitem, T newvalue)
  64.         {
  65.             dataitem.Value = newvalue.ToString();
  66.  
  67.         }
  68.  
  69.         private static void GetTypeDefault<T>(out T result)
  70.         {
  71.             Type tt = typeof(T);
  72.  
  73.             //basic idea: call default, empty constructor using reflection.
  74.             ConstructorInfo defaultconstructor = tt.GetConstructor(new Type []  { });
  75.  
  76.             result = (T)defaultconstructor.Invoke(null);
  77.  
  78.         }
  79.  
  80.     }
  81.  
  82.  
  83.  

And there you have it, a bunch of awesome additions. INI files are often thought of as deprecated, but that’s only the INIFile functions. This class was designed because working with the registry makes it difficult to test properly, and because JSON,YAML, and many other formats are excessively complicated. when you just need a few basic settings, all you need is the clean, simple format of a INI file. And now, with these additions, code for reading from those INI files is clean and simple as well!

The Source- cINIFile.cs

540 total views, no views today

Posted By: BC_Programming
Last Edit: 01 Jan 2012 @ 05:27 PM

EmailPermalinkComments (0)
Tags
 25 Dec 2011 @ 2:05 PM 

As I posted previously here , Sorting a Listview can be something of a pain in the butt.

In that article, I covered some basics on providing a class that would essentially give you sorting capabilities for free, without all the messy code that would normally be required. A lot of the code required for sorting is mostly boilerplate with a few modifications for sorting various types. As a result, the generic implementation works rather well.

However, as with any class, adding features never hurts. In this case, I got to thinking- why not have right-clicking the ColumnHeaders show a menu for sorting against that Column? Seems simple enough. I quickly learned that apparent simplicity often is misattributed.

I faced several issues. The first thought was that I could hook a Mouse event for Right-Clicking a column header. Unfortunately, I soon discovered two facts about the .NET ListView control. First, was that there was no event for right-clicking a header control. Second, no even was fired at all by the ListView control when you right-clicked a header.

This left me stymied. How the heck do I implement this feature? I discovered something of a “hack” however, in that when the ListView’s ContextMenuStrip property is set, that ContextMenu Strip will be shown regardless of the location the ListView is clicked. This at least gave me something to work with. Since a ContextMenuStrip’s “Opening” event can be easily hooked, we can use that as an entry point and perform needed calculations to determine if we are indeed on a columnheader.

Which brings me to the next problem, which is determining when a columnheader was in fact the item that was clicked. This requires determining the rectangle the Header control occupies, first. The Header Control is a child control of the ListView; as such, a platform Invoke using the EnumChildWindows() API was required, something like this:

 
private Rectangle _HeaderRect;
private delegate bool EnumWindowsCallBack(IntPtr hwnd,IntPtr lparam);
 [DllImport("user32.dll")] 
private static extern int EnumChildWindows(IntPtr hwndParent,EnumWindowCallBack callbackFunction,IntPtr lParam);
 [DllImport("user32.dll"] 
private static extern bool GetWindowRect(IntPtr hWnd,out RECT lpRect);

 [StructLayout(LayoutKind.Sequential)] 
private struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;

}

private bool EnumWindowCallback(IntPtr hwnd, IntPtr lParam)
{
    RECT rct;
    if(!GetWindowRect(hwnd,out rct))
    {
    //first child of the listview should be the header control
    _HeaderRect=Rectangle.Empty; //likely the listview is not in Details mode, so there is no header control.
    }
    else
    {
        _HeaderRect = new Rectangle(rct.Left,rct.Top,rct.Right-rct.Left,rct.Bottom-rct.Top);
    }
    return false; //cancel enumeration.
}
private static ColumnHeader []  GetOrderedHeaders(ListView lvw)
{
    ColumnHeader []  returnarray = new ColumnHeader [lvw.Columns.Count] ;
    foreach(ColumnHeader loopheader in lvw.Columns)
    { 
        returnarray [loopheader.DisplayIndex]  = loopheader;

    }
    return returnarray;
}
 

Quite a bit of boilerplate to add in. Basically, the idea is that we will hook the contextMenu Opening event of the Listview, (and we add a context menu to hook if the listview in fact doesn’t have one) in our constructor; and then when we receive the event we need to determine if the click occured within the area of the header control of the listview, if so, we cancel the event (which stops the default context menu from appearing) and show our own menu for the columnheader, which we can acquire using a bit of math and the static “GetOrderedHeaders” function, which retrieves the array of columnheaders of a ListView in order of appearance Left to Right (since the user could rearrange the Columns).

So First, we need to add code to the GenericListViewSorter’s Constructor. We also have a few private variables that are added; in this case, we need a ContextMenuStrip variable called “_ghostStrip” which we will use if we need to create a context menu for the control, since we don’t want that to appear in the default case. Of course we create our own ContextMenuStrip which we will show in the event instead of the default when appropriate. so we add this beneath the existing code in the constructor:

 
    if(handleListView.ContextMenuStrip==null)
    {
        handleListView.ContextMenuStrip = new ContextMenuStrip();
        handleListView.ContextMenuStrip.Items.Add("GHOST"); //add a ghost item so we get the Opening Event
        _ghoststrip = handleListView.ContextMenuStrip;
}

//create OUR context menu
_headerContextMenuStrip = new ContextMenuStrip();
//add a ghost item to make sure Opening will fire.
_HeaderContextMenuStrip.Items.Add("ghost");
handleListView.ContextMenuStrip.Opening += ContextMenuStrip_Opening;
handleListView.ContextMenuStripChanged += handleListView_ContextMenuStripChanged;


 

Of course we need to add the two referenced event handlers, too. The ContextMenuStripChanged being a rather simple implementation designed to keep changes in the contextmenu of the listview from causing us to balls up and stop showing ours (since we are now hooking a orphaned context menu not being shown by the listview).

 
void handleListView_ContextMenuStripChanged(object sender,EventArgs e)
{
    OurListView.ContextMenuStrip.Opening+=ContextMenuStrip_Opening;
}
 

Now the meat of the code is in the ContextMenuStrip_Opening() routine. This will need to determine wether its applicable to show the Column menu, or the already present menu (which it doesn’t show either if it happens to be the _ghoststrip). This is accomplished by use of the GetCursorPos() API routine paired with the already present GetWindowRect() implementation, which we update by calling EnumWindows.

 
void ContextMenuStrip_Opening(object sender,System.ComponentModel.CancelEventArgs e)
{
    //first, get screen coordinates of Cursor.
    POINTAPI gapi;
    GetCursorPos(out gapi);
    
    Point gotposition = new Point(gapi.X,gapi.Y);
    
    //acquire the HeaderRect of the control...
    EnumChildWindows(OurListView.Handle,new EnumWindowCallBack(EnumWindowCallback),IntPtr.Zero);
    //if the mouse position is within the retrieved rectangle, cancel the display of the normal menu and create and show ours.
    if(_HeaderRect.Contains(gotposition))
    {
        e.Cancel=true;
        int xoffset = gotposition.X - _HeaderRect.Left;
        ColumnHeader clickedheader = HeaderAtOffset(OurListView,xoffset);
        
        if(clickedheader != null)
        {
        //create the context menu as needed.
        _HeaderContextMenuStrip = new ContextMenuStrip();
        _HeaderContextMenuStrip.Tag = clickedheader;
        //two items, one for ascending order, one for descending order.
        ToolStripMenuItem AscendingHeaderItem = new ToolStripMenuItem(String.Format("Sort Column \"{0}\" Ascending",clickedheader.Text));
        ToolStripMenuItem DescendingHeaderItem = new ToolStripMenuItem(String.Format("Sort Column \"{1}\" Descending",clickedheader.Text));
        
        //if the current sort column is the header, check it off and disable it.
        
        if(CurrentSortColumn == clickedheader)
        {  
            if(OurListView.Sorting ==SortOrder.Ascending)
            {
                AscendingHeaderItem.Checked=true;
                AscendingHeaderItem.Enabled=false;
            }
            else if (OurListView.Sorting==SortOrder.Descending)
            {
              DescendingHeaderItem.Checked=true;
              DescendingHeaderItem.Enabled=false;
            }   
        
        }
        AscendingHeaderItem.Tag = ClickedHeader;
        DescendingHeaderItem.Tag = ClickedHeader;
        //set event handlers for the two items.
        AscendingHeaderItem.Click+= AscendingHeaderItem_Click;
        DescendingHeaderItem.Click+= DescendingHeaderItem_Click;
        //add them to the context menu strip.
        _HeaderContextMenuStrip.Items.Add(AscendingHeaderItem);
        _HeaderContextMenuStrip.Items.Add(DescendingHeaderItem);
        //display the menu.
        _HeaderContextMenuStrip.Show(gotposition);
        }
   } 
   else
   {
       //show the default menu, but only if it isn't the ghoststrip.
       if(OurListView.ContextMenuStrip == _ghoststrip)
           e.Cancel=true;
   
   }   

}


 

The events for the two buttons basically sort based on the columnheader in their tag, nothing particularly special there. the actual details can be seen in the source file itself, really.

It actually works quite well, I’m using it in a production application, and it’s working quite well.

Some obvious enhancements, of course, include making it possible to customize the shown menu, to present other options; perhaps a delegate or event that can be hooked that is given the Strip and the clicked column, and any number of other parameters? This would essentially give the equivalent of a ColumnHeaderRightClicked type event, too.

444 total views, no views today

Posted By: BC_Programming
Last Edit: 05 May 2012 @ 10:21 PM

EmailPermalinkComments (0)
Tags

 Last 50 Posts
 Back
Change Theme...
  • Users » 872
  • Posts/Pages » 193
  • Comments » 75
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.