Tuesday, February 26, 2008

Use PerformClick to call a Menu Item's Click event from your code

Another easy one, and I've done this before too, just forgot about it.

But I had an Menu Click event that I wanted to call from code.  When you call Menuitem1.Click you get an error, because it expects a sender and some args.  I started down the path of supplying the arguments, when I stumbled across the PerformClick event, that you can just call.  Yeah.

Another option would be to move your code from the Menu Click event to another function, and call that function from both the MenuClick event and from any where else you would like to.

How to have different colors for different items in a combobox in VB.NET

I have a drop down style combobox in VB.NET 2003, and I would like to assign different colors to different items in the combobox.  I came across the following article on vbcity:
http://vbcity.com/forums/faq.asp?fid=15&cat=ListBox%2FComboBox

The article shows you how to add images to combobox items and different colors.  You  can also download code and try things out yourself.  However, for me all I wanted was to color different items a different color.  Using some of the concepts from the article,  this is how one can do such a thing:

1.  The first thing one needs to do, in the drawmode property of the Combo Box, to select either OwnerDrawFixed, OwnerDrawVariable.
2.  Next, in the DrawItem Event the user can do something the the following:

        Dim g As Graphics = e.Graphics

        Dim bBlue As SolidBrush = New SolidBrush(Color.Blue)
        Dim bRed As SolidBrush = New SolidBrush(Color.Red)
        Dim bOrange As SolidBrush = New SolidBrush(Color.Orange)

            If e.Index = 0 Then
                g.DrawString("Value1", Me.ComboBox1.Font, bBlue, e.Bounds.Left, e.Bounds.Top)
            ElseIf e.Index = 1 Then
                g.DrawString("Valu2", Me.ComboBox1.Font, bRed, e.Bounds.Left, e.Bounds.Top)
            Else
                g.DrawString("Value3", Me.ComboBox1.Font, bOrange, e.Bounds.Left, e.Bounds.Top)
            End If
        bBlue.Dispose()
        bRed.Dispose()
        bOrange.Dispose()


This basically colors the first value, Blue, the second value Red and everything else Orange.


Sunday, February 24, 2008

My Autos Window has Disappeared in VB.NET

This might not be a big deal, but the other day I had to help a team member work out some problems he was having.  Well, this person has just installed VB.NET on his computer.  In order to figure out the problem, I had to debug the application, well while in debug mode, I noticed that the Autos window, and Locals window didn't exist.  Well, I figured I could just show it by going to the View Menu.  However, when I went there I couldn't find the Autos Window or the Locals Window.  I couldn't figure what was going on on his machine, but was able to use the Watch window to figure the values.  I guess sometimes we don't realize how specific parts of our work environment got there, and we just take them for granted.  I've always had the Autos Window and Locals Window, so I've never had to actually add it back.

Anyways, after checking MSDN library, I found that the Auto's Window can be shown and hidden by going to the Debug Menu/Window/ And selecting it from that submenu.  You can also show it by pressing  Ctrl+Alt+V, and then the letter A.


Friday, February 22, 2008

Beware when removing items from an ArrayList

I have an arraylist, and each item contains a structure.  The structure contains a point and an index value.  There are times when I need to go through the arraylist and delete all the items which have an index equal to a specific value.  So for example, the arraylist contains the followint
 
0 -- pt = 1, index = 0
0 -- pt = 2, index = 0
0 -- pt = 3, index = 0
0 -- pt = 6, index = 1
0 -- pt = 9, index = 1
 
When I tried to loop through through the array using a For Loop, I had the problem where after deleting the first point, the second point was moved to the first position, and the for loop missed it.  This is the code I used for the For Loop:
 
  For i = 0 To aList.Count - 1
   If aList(i).Index = Index Then 'The Index was supplied to the function
    aList.RemoveAt(i)
   End If
  Next
 Furthermore, the for loop ends up giving you an out of bounds error, because the size of the array changes.  So my solution was to use a while loop, like so:
  While i < aList.Count - 1
   If aList(i).Index = Index Then 'The Index was supplied to the function
    aList.RemoveAt(i)
   Else
    i = i + 1
   End If
  End While
Now the first thing I asked when I did this is what happens if you have an arraylist like so:
0 -- pt = 1, index = 0
0 -- pt = 2, index = 1
0 -- pt = 3, index = 1
0 -- pt = 6, index = 0
0 -- pt = 9, index = 0
 
In this situation the arraylist will delete the first item, then i will still be equal to 0, and it will look at the the item with value pt = 2, it will keep it.  It will then look at pt = 3, it will keep it.  It will look at pt = 6, and delete it.  Then look at pt = 9 and delete it as well.  So it looks like this should work without any problems.  Though I'm sure there is something that I'm missing here.
 
 
 

Tuesday, February 12, 2008

How to make a form a child of a control in VB.NET

I had a control.  The control would open a Form.  I wanted to make the Form a child of the control.  For some reason I couldn't get this to work.  What I wanted was this Form to be on top of the control, and I didn't want the form to be moved out of the control.  I also wanted the form to close if the control was closed.  Anyways, I just couldn't get it to work properly.

I tried doing the following:
                Form1.Owner = Me.Parent         

But then I had to set the Form1 as the TopMost control.  And this didn't allow me to click on other parts of the control.

Anyways, the things that worked was thie following line:
                Form1.TopLevel = False                'This is the key line in order to make Form1 a Child of this Control

I also do the following (which I did before):
                Form1.Parent = Me
                Form1.IsMdiContainer = False
                Form1.BringToFront()

I no longer do the call to Form1.Owner = Me.Parent as it is not needed and having that call there messes things up.



Friday, February 8, 2008

How to completely validate input in a TextBox even on paste

I had a textbox and I wanted to make sure that the text entered was only alpha numeric.  I put in validation logic in to KeyPress which you can find just by doing a google search.  However, most of the sample code out there limits the data as you enter it, one character at a time.  What if you want to allow people to paste text into your textbox, and still validate the data pasted.

The solution was to put validation logic into the TextChanged event as well.  I don't remember exactly the scenario, but there might be situations where your validation logic my interpret the Ctrl-V as an invalid character, and it won't allow you to paste your values in.  The way I was able to work around this was in the KeyDown event to check if the Ctrl button was pressed, if it was pressed, I would set a flag.  In the KeyPress event I checked if the flag was set.  If it was set, then I didn't go into my validation logic (which would have interpreted the Ctrl-V as an invalid character), I just exited the sub.  This way the text was pasted, but in TextChanged, I would then validate the text that was pasted.


How to get the Short Filename to a Windows File in VB.NET

I had an application that needed to call an external exe and supply it with an input and output filename.  The problem I had was that if the filename had a space in it, the executable would give me an error.  I tried putting double quotes around the entire call, double quotes around the filenames, single quotes.  But nothing would work.  I didn't have access to the code of the executable.

What I ended up doing was to use the Short Filename.  Below is code that helped me do it.  I found it on some message board, but didn't save the reference.

    Private Const MAX_PATH As Integer = 260

    Private Declare Auto Function GetShortPathName Lib "kernel32" ( _
    ByVal lpszLongPath As String, _
    ByVal lpszShortPath As System.Text.StringBuilder, _
    ByVal cchBuffer As Integer) As Integer

    Public Function GetShortFileName(ByVal LongPath As String) As String
        Dim ShortPath As New System.Text.StringBuilder(MAX_PATH)
        Dim BufferSize As Integer = GetShortPathName(LongPath, ShortPath, ShortPath.Capacity)
        Return ShortPath.ToString()
    End Function


Tjust call GetShortFileName with the filename you want to change and it will return the Windows Short Filename.

-- Ted

Monday, February 4, 2008

Debug Visual C++ DLL from VB.NET

FYI, I am running .NET 2003.  I have a VB.NET project that calls functions in a C++ DLL.  Every once in a while, there is a problem with functions in the DLL, and usually we just put MessageBox statements in the DLL, rebuild it and then run the VB.NET program to try to figure out what is wrong.  However, this process takes up time as we have to continually go back and forth until we figure out the problem.  What I wanted was to just debug my C++ code, from the debugger.  I did some google searches and came up with bits and pieces.  The one thing that I found was that a lot of people have asked this question in a number of message boards without any answers.

Using some of the hints suggested, I was able to come up with a method that works.

I created a brand new Visual C++ dll project (called testcplusplusdll), with one cpp file (mainfile.cpp).  This is what it had:

#include <windows.h>
#define CALLINGCONV __declspec(dllexport) __stdcall

int CALLINGCONV Trace() {
    int hgt,wid;
        hgt = 2;
        wid = 2;
        hgt = hgt + wid;
        MessageBox(NULL,"trace1","Trace13",0);   

  return 1;
}

In the Project Properties.  Project->testcplusplusdll Properties
Under Configuration Properties -> Debugging.  For the command I entered the path to the executable of the VB.NET program (we'll get into that in a little bit).  For Attach, I have the value "No", for Debugger Type, I have "Native Only" but others might work (just haven't tried it yet), everything else on that page was left to the default. 

Under the Linker Folder, I kept the default settings, but they are:
under Debugging. for Generate Debug Info I have "Yes (/DEBUG)"
Generate Program Database File - "$(OutDir)/testcplusplusdll.pdb"

That's it for the C++ project.  Build it, and make sure it compiles.  The compiled dll ended up in a Folder called "Debug"

Now, on to the VB.NET project.  I created a new Project.  The location where it put the project wasn't in the same location as the C++ project.  On the form, I placed a button.  And Below is the code that I added to the Form.

    Private Declare Function Trace Lib "..\..\testcplusplusdll\debug\testcplusplusdll.dll" Alias "?Trace@@YGHXZ" () As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Trace()
    End Sub


A couple of points.  To figure out the Alias of the C++ function, I used the Depends application that came with VB6.  The value for Lib, is  the path to the testcplusplusdll.dll that I compiled earlier.

I build the project.  I am still in the Debug Configuration.

Now, I go back to my C++ project, and as I mentioned earlier, and under onfiguration Properties -> Debugging->Command, I entered the path to the executable of the VB.NET project I just built.

Now I set a beakpoint in the C++ code, and run the C++ project (it takes a while but be patient).  It will popup the Form1, and when I click on Button1, it will stop in my C++ code.  Yeah.


A couple of things that I read about that I tried that didn't make a difference:
1.  In the VB.NET program, under Project Properties under Debugging, there is a checkbox for "Unmanaged Code Debugging", checking it or unchecking it didn't make a difference.
2.  In the VB.NET, if you click on the Solution, and then go to Project->Properties.  You can do "Debug Source Files" and "Debug Symbol Files".  I had entered the location of the c++ source files to this location, and the location of the c++ pdb file in the Symbol Files location, but when I removed it, it didn't seem to make a difference.

Hopefully this is helpful to someone.