Wednesday, January 17, 2007

SQL Server Compact Edition

On January 17th, Microsoft released SQL Server Compact Edition (http://www.microsoft.com/sql/editions/compact/default.mspx). I'm very excited about this release as it finally makes the development of the disconnected smart client practical. It is ideal for any application that needs a very small but capable single-user database to ship with their product, and it is free. Not surprisingly, it has features to synchronize with Microsoft SQL Server and Microsoft Access. There are too many features to mention in this post, but check it out!

Tuesday, November 28, 2006

Project must be startup project for Create Instance to be available for Object Test Bench in Visual Studio 2005

I am trying to do a better job of testing (and fixing) my code as I write it, and a big part of that is using a great new feature of Visual Studio 2005 called Object Test Bench which allows you to easily create and call objects on the fly and essentially test them immediately after writing them, without having to create any test projects or forms or classes as drivers.

When you open up the Object Test Bench window, it tells you to right click the class in the Class View window and choose "Create Instance" to test it. Except that option was not on the right click menu for any of my classes. Everything looked right, and I was at a loss to explain it. Finally I found a statement on MSDN that the project has to be the startup project in order for the Create Instance option to be available.

I set the project as Startup Project and it worked perfectly. Once again, the answer is simple and obvious once you know it.

Hope you find this information useful.

Joe Kunk

Tuesday, August 01, 2006

Awesome presentation by Joe Stagner

On July 20th, the Greater Lansing User Group for .Net (www.glugnet.org) was honored to host Joe Stagner as speaker. Joe is a program manager for ASP.Net at Microsoft. Joe spent 90 minutes demonstrating the capabilities of Microsoft's Atlas, their implementation of Ajax. It was very impressive to see what this technology can do in terms of providing a much richer and more responsive user experience in ASP.Net web pages.

After the meeting, I heard several user group members say that it was among the best presentation that they had ever atttended at GLUGnet. Another member told me that he went back to the office the next day and implemented Atlas in his current ASP.Net project and was able to demonstrate a much nicer interface to his supervisor by the end of the same day. Great stuff!

I want to extend a very sincere "Thank You!" to Drew Robbins, the Microsoft Developer Evangelist that was instrumental in making Joe Stagner available to GLUGnet and to other Michigan user groups that week.

Needless to say, if you ever get the chance to hear Joe Stagner speak at a user group or conference, don't miss it!

Joe Kunk

Wednesday, May 24, 2006

Create a quick report in Word

Sometimes I just want to create a very quick report for a client but I don't want to deal with the complexity of setting up a report in a tool like Crystal Reports or SQL Reporting Services.

What I have found works well is to create a Word template with the report headers and footers that I want, and then simply create all the report lines as a large string and then just have the application paste that text into a blank instance of that template.

Sample code follows:

Dim sb as new System.Text.StringBuilder

... place here all the statements needed to create the report data as a large string in sb ...

Dim oWord As Object
oWord = CreateObject("Word.Application")
oWord.Visible = True
oWord.Documents.Open("MyReportTemplate.dot")
oWord.activedocument.bookmarks.add("Top").select()
oWord.selection.typetext(sb.ToString)
Application.DoEvents()

Note that this is only for very simple reports because it does not allow for any formatting within the text or within the document header or footer. Note that a tab is chr(9) and a form-feed (new page) is chr(12).

Often time clients just want the data and don't mind doing the formatting themselves in MS Word when it is finished.

Hope you find this tip useful...
Joe Kunk

SQL Reporting Services and Word are registered trademarks of Microsoft Corporation.
Crystal Reports is a registered trademark of Business Objects SA.

Monday, March 13, 2006

Getting Ctrl-Shift-F2 back in Visual Studio .Net

One feature I used a lot while working in Visual Basic 6 was the ability to press Shift-F2 on the name of a routine and instantly jump to that routine's code to investigate something about that routine. Once finished, I could press Ctrl-Shift-F2 and jump back to the calling program where I left off and continue on with my previous task. Very productive.

In Visual Studio.Net, under the default keyboard mappings, Shift-F2 is replaced by F12. This is an improvement since I don't have to press the Shift key to go to a code routine's definition. But the equivalent for the Ctrl-Shift-F2 to jump back to the original source code location is nowhere to be found.

In Visual Studio.Net terms, the equivalent for the VB6 Shift-F2 functionality is Edit.GoToDefinition and the equivalent for Ctrl-Shift-F2 is View.NavigateBackward. I used the Visual Studio.Net keyboard customization options to assign these functions to F12 and Shift-F12 respectively. Now I once-again have the ability to investigate a routine, jump back to where I left off, and the keystrokes are more convenient and intuitive than before. I'm diggin' it.


Hope you found this helpful.

Joe.

Tuesday, March 07, 2006

Dealing with Concurrency issues in ASP.Net

When working on ASP.Net systems where multiple users could potentially change the same information in a database such as SQL Server, there is a real concern that one user could make a change and then another user could erase that change by updating the record with a slightly older copy that they had been working on for a while.

Handling this issue efficiently is essential to writing larger systems in .Net so I have done some research and my findings for at least a partial solution appear below.

While there are many reasons why ADO.Net is a huge improvement, it does introduce one very major problem that we must solve before we can use .Net to write larger database systems. The issue is in managing concurrency, i.e., preventing multiple users from overwriting each others' changes when sharing a table in the database. The email explains how the problem can occur and how I have arrived at what I believe is the best possible solution based on extensive reading and testing of many suggested solutions.


The Problem
ADO.Net uses a disconnected data access model, i.e, a connection is made to the database only long enough to perform a specific action and then the connection is immediately closed. This approach is the opposite of how Visual Basic 6 typically worked, which opened a connection to the database and kept it open as long as the data would be needed.

ADO.Net's disconnected data model provides superior performance and scalability since "expensive" database connections are used very sparingly and can be easily shared among applications. From an application developer's perspective, the biggest disadvantage is the inability to lock a record for updates. This means that ADO.Net developers must explicitly manage concurrency issues. Reading a record, allowing the user to change it, then updating the record without any further checking can easily overwrite someone else's changes.


Possible Solutions
Two possible solutions are generally discussed on managing database concurrency; Pessimistic Locking vs. Optimistic Locking. Pessimistic locking means locking the record from the time that the data is read until the time that the update is completed. Optimistic Locking means re-establishing the connection to the database to perform the update and checking to make sure that the record has not changed since it was read.

While Pessimistic Locking is possible in ADO.Net, it is not recommended. It prevents any other access to the record while it is locked and maintains lengthy database connections, resulting in reduced performance in the database.

Optimistic Locking is the generally recommended solution but a reliable method must be determined for ensuring that the record has not changed prior to applying the update or deleting the record.


Implementing Optimistic Locking
The challenge in Optimistic Locking is to have a reliable method to ensure that the table row has not changed before being updated or deleted, even when there is heavy usage of the table. This challenge is even greater when there is a Master-Detail relationship that must ensure integrity across multiple tables.

The first requirement is to have a field in the table row that automatically changes when the record is created or updated.

If you need to know when the change was made and by whom, then you must add a varchar field for the username, a datetime field for the update time, and write a database trigger for the table that automatically updates these fields at each SQL Insert or Update. This introduces the possibility of application errors due to an incorrect or missing trigger. This is not enough reason to avoid this triggers, but care must be taken to make sure the triggers are properly implemented and maintained.

If it is not critical that you know when the change was made or by whom, but are just interested in managing the concurrency issues, then using a Timestamp column on the table is the preferred method. The Timestamp data type is a bit mis-named since it does not contain any date or time, but instead is just a 64-bit integer that is incremented each time that any change is made to any table in the database. Thus it serves as a reliable unique indicator that a record has not changed, but the number itself has no meaning.

The Timestamp column can appear in a Select query like any other column but cannot be directly updated. When displayed by SQL Query Analyzer, it may show a value like Ox000023 where the "23" is actually a hex number, i.e., decimal value 35. A row can be selected by its Timestamp value if you use the decimal value, for example, "Select * from MyTable where MyTimestampField = 35" is a valid query.

To implement Optimistic Locking, the Timestamp value must be returned with the data (as data type UInt64) for each row in the query. That Timestamp can then be used as the single condition in the WHERE condition of the UPDATE statement to ensure that the unchanged row is being updated. To use a UInt64 value as a string in a WHERE condition, it must be converted with a custom function such as GetSQLTimestamp function (shown at the end of this posting).

If the UPDATE command returns 0 rows affected, then the record was changed by someone else during the time that the application had it and the update is invalid. Typically you would notify the user that the data had changed, re-query and re-display the data, and ask the user to make the change again with the new data and re-attempt the update. This process is repeated until an update succeeds or the user declines to try again.

For single table updates, using the GetSQLTimestamp function is the easiest way to manage optimistic concurrency as long as you ensure that your UPDATE SQL command always uses a "WHERE tsTimestamp = " condition and you verify that one and only one row changed.

To enforce optimistic concurrency on updates against multiple tables, use of a stored procedure is required so that a transaction can span the updates against all the tables, and succeed in making any change only if ALL the timestamp values are a match. I'm still working on the easiest solution for that, I'll post more info when I finish. Comments/Suggestions are welcome.


Here is the function that gives you the string value of a timestamp field that can used in the WHERE condition. Note that you can pass the datarow field in directly, no CAST or CTYPE is needed first.



Public Function GetSQLTimestamp(ByVal tsTimestamp As Object) As String
Dim oTimestamp() As Byte = CType(tsTimestamp, System.Byte())
Dim strTimestamp As String = "&H"
For i As Integer = 0 To oTimestamp.GetUpperBound(0)
Dim strHex2Dig As String = Hex(oTimestamp(i)).PadLeft(2, "0"c)
strTimestamp += strHex2Dig
Next i
Return Val(strTimestamp).ToString
End Function


I hope you find this helpful.

Joe

Great free DHTML pop-up calendar for ASP.NET

A current project requires the ASP.Net web page to prompt for a Start Date and an End Date.

To make data entry easier for the user, I wanted to provide a small calendar icon that pops-up a monthly calendar which can be used to fill the ASP:textbox field that will be used to hold date. I found many commercial solutions but I was looking for something for free.

The requirements of the calendar are:
1. A small pop-up that does not overwhelm the screen
2. Ability to move prev/next year as well as prev/next month
3. Does not shift around the contents of the page when it appears
4. Honors the date in the text field, i.e., changing the date field then changes the default date for which the calendar appears.
5. Available at no charge.

I have found just such a control and it works GREAT!. The link to this great work of Javascript is Dynamic HTML Lab Pop-up Calendar 1.2.

The only change I made was to modify the javascript to default to Sunday as the first day of the week, to default the date format to mm/dd/yyyy, and to allow javascript and all its images to exist in a sub-folder to avoid cluttering up the application's main folder. Feel free to email me at joekunk at gmail.com if you wish to get a copy of the sample Calendar.aspx page that I used to ensure it was all working properly.

I hope you find this helpful.

Joe

Tuesday, November 22, 2005

List of Invalid Keyboard Characters for a Windows XP filename

What keyboard characters are not valid for a Windows XP filename?

This has come up several times over the years during my programming tasks when I have tried to automatically generate a filename based on some collection of data. It seems a good time to document this relatively simple question for my future reference as well as yours.

The keyboard characters that cannot be used in a Windows XP filename are:

* Asterisk

Pipe (vertical bar)

\ Back slash

/ Forward slash

: Colon

" Quote

< Less Than

> Greater Than

? Question mark


Hope that helps!
Joe Kunk

Friday, October 14, 2005

A solution for converting C++ MFC code to C# or VB.Net

As Program Director for the Lansing MI based Great Lakes User Group for .Net (http://www.glugnet.org), I received an email from a member that has found a very useful method to accomplish a needed conversion from MFC C++ code into C# or VB.Net. While C++ MFC is outside my area of expertise, I read through his explanation and it sounds like a very useful technique. For those who can benefit, I have included the text of his email below. If you wish to explore this topic further, post a comment and I'll be sure that he is asked to contact you. Thanks. Joe Kunk



Greetings!

As a .NET user group member I wanted to share my experience of"migrating my MFC apps to .NET using C# and VB.NET" so that it will be beneficial for all our members. It might not be an ideal solution for all but I am hopeful it will add at least few new pointers in your knowledge base.

The problem was "How to migrate MFC applications to .NET in C# orVB.NET?" and there were no guidelines or samples. As we know MFC will besupported in Windows Vista (earlier codenamed longhorn) so we can keep MFC apps running for at least few years but question was how to enhanced and use with .NET managed assemblies, keep maintenance low, less compatibility issues, less interops, less performance hiccups and less complicated install program.

Above all new customers were asking on which technology our product wasbuilt? And if answer was "MFC" then we started loosing new business opportunity. Then what to do? What is the trend? Most of C++ programmershave already moved to C# and wish to convert their apps in C# or VB.NET but again there is lack of information on how to do it.

My first problem was ATL or MFC UI conversion to Windows Form resources and there was no help and that was the reason we never went ahead with even prototype test for migration. But thanks to Google where I found acompany called DudeLabs( http://www.dudelabs.com )which has a product"RC Converter" and it changed our migration policy because only in minutes we had our hundreds of dialogs, form views, all standard controls with their properties, menus, string resources and accelerators converted in Windows form resources and we chose our own class name,file name, namespace name and control names naming conventions. We used C# on Visual studio 2005 format but it also generates Visual studio 2003 format and supports C# and VB.NET both.

This quick conversion of our UI gave us ample time to focus on business layer and data layer rewrite.

Now thanks to Microsoft whose application block for SQL Data Access fixed our data layer rewrite in minutes because we were using MS SQLServer 2000. But even if you are using other databases please use SQLData Access application block code as sample and you will be done in hours or in worst case 2 or 3 days.

Business logic rewrite needs to be done manually and we knew that so we started rewriting it and let me tell you writing code in Visual Studio 2005 takes one third or less time as compared to MFC or ATL and then if you have any issues visit http://www.codeproject.com orhttp://www.GotDotNet.com and be assured you will find your solution there and if not the exact answer then at least you will find something to start with. I am really happy as we are near completion of our product migration from MFC to .NET managed world.
As .NET user group and our .NET community always helped me in my migration path I wanted to share it with you all so that it will make your migration at least a little bit easier.


What I learned was - It takes less time and money to migrate to .NET from MFC/ATL than we are estimating in present scenario.

But be careful as only migration to .NET will not increase you sales immediately because this migration first makes sure that your product will survive in future and then it increase your new sales opportunities and gives you flexibility to add new functional modules with less development time, ability to interact with managed world tools and components, enhances performance, keeps you on cutting edge technology, provides you click once deployment and has a very low maintenance cost.

So what you are waiting for?

Wish you good luck with your migration!

Monday, October 10, 2005

Finally a great utility for managing Internet bookmarks

I work on several computers, a laptop, a desktop, a server, etc. It has always frustrated me that my Internet Explorer bookmarks are different on each PC. For years I have wanted access to all my saved bookmarks, on any of my computers that I routinely use, or from any other computer via a web page.

I finally found it. The web site http://www.sync2it.com/ allows you to create a free member account where a master copy of all your bookmarks are stored. You download a small utility for your system tray that allows you to upload your current bookmarks and then provides easy one-click access to your bookmarks at any time. Add or delete a bookmark? The other computers are automatically synchronized with the same change. It is as if all the bookmarks are coming from a single shared directory. Wonderful!

If you are on a computer that does not allow you to install software, like a library PC, you can access all your bookmarks as a web page by signing into your account on the Sync2It web site.

And best of all, this service is provided free of charge.

This service has made my internet browsing so much more productive - I just love it. Try it out, I am confident you will be impressed.

Joe Kunk

Wednesday, October 05, 2005

Copy text contents of a listbox to the Windows Clipboard in VB.Net

When debugging a new program, I like to add log style messages to a listbox on a form as a very easy way for me to see what is happening with the program and verify if the proper actions are occurring. Recently I had a program that generated a LOT of messages and I wanted to copy the text contents of that listbox to the clipboard with a button so that I paste the text into WordPad and have a hardcopy record of the program's execution log. As with many things in VB.Net, it turned out to be very easy to do.

In case you have a similar need in the future, the code is listed below for your use. I hope you find it helpful.

Joe Kunk

'Copy contents of a string Listbox to the clipboard
Dim strList(Me.lbStatus.Items.Count) As String
Me.lbStatus.Items.CopyTo(strList, 0)
Dim strClip As String = String.Join(vbCrLf, strList)
Clipboard.SetDataObject(strClip)

A VB.Net function to return the text contained within the first set of quotes in a string

I needed a quick function to return the text within the first set of quotes in a particular string. In case you need a similar function at some point, it is listed below for your use. I hope you find it helpful.

Joe Kunk



'Returns the text within the first set of quotes found
Function GetTextWithinQuotes(ByVal strSource As String) As String
Const Quote As Char = Chr(34)
If strSource.IndexOf(Quote) = -1 Then Return String.Empty
Dim StartPos As Integer = strSource.IndexOf(Quote)
Dim EndPos As Integer = strSource.IndexOf(Quote, StartPos + 1)
If (EndPos - StartPos = 1) Then Return String.Empty
Return strSource.Substring(StartPos + 1, (EndPos - StartPos - 1))
End Function

A better error message from Try-Catch-Finally in VB.Net

I like to use Try-Catch-Finally blocks in my code in order to provide a more pleasant user experience. When I show an error message to the user, I like to include enough detail that if the user sends me a screen image, then I have a good headstart on solving the problem. I have played with several variations of the 'perfect' error message, and finally have settled on the format below that is user-friendly and still provides lots of needed detail information. I hope you find it helpful.

Dim ErrorMsg as string = "I was unable to perform <>." & vbCrLf & "Please make sure to <>." & vbCrLf & "Suggestions go here." & vbCrLf & vbCrLf & "Detailed Error Information below:" & vbCrLf & vbCrLf & " Message: " & exp.Message & vbCrLf & _" Source: " & exp.Source & vbCrLf & vbCrLf & " Stack Trace:" & vbCrLf & exp.StackTrace

Joe Kunk

Tuesday, October 04, 2005

Blank page when setting a tabcontrol's tabpage by its index

I am working on an application that has 30-40 tabs in a tab control. To keep it reasonable for the user, I need to limit the displayed tabs to just the 4-5 tabs that are relevant to the current action.

First I tried the obvious of just setting the visible property of the unwanted tabpages to false. This hid the contents of the tabpage, but the tabpage itself remained visible and in the row of tabs that could be selected by the user. If the tabpage had fully hidden itself as it should have (in my mind), I would have been all set. But instead I had a problem to solve.

To clarify, I have almost 40 tabpages total in the tabcontrol. There are four tabpages that always should be shown in a particular order (tabpages 2-5) and I wanted the first tabpage on the left to be different and selected depending on which item was selected in a tree control on the form.

In other words, I want all 40 tabs on the tabcontrol for ease of maintenance on the form, but to only show 5 of those tabpages at any time and to be able to dynamically change which tabpage from the non-display tabpages was to be shown at the first tabpage, depending on what is selected on a tree control on the form.

The first issue was that I could not insert a tabpage to a certain position. The .add method only adds the tabpage to the end of the existing tabpages collection.

The second issue was that any direct assignment to a tabpage always produced a blank tabpage, i.e., none of its child controls were visible and all attempts to get them to appear failed. That was disappointing as I could have just added a dummy tabpage as the first one and then repeatedly replaced with with the tabpage of current interest as the user moved around the tree control.

Utilimately I created a second non-visible tab control on the form named tcHidden, moved all the tabs over to it at Form_Load, then moved the tabpages between the two controls in the desired order as the user moved around in the tree control. It works well and it is fast enough that the users are happy with it.

The tabcontrol is one of those controls that seems to give me fits, probably because I always try to push it to its limits, so I wanted to post one of my workarounds in case someone else has a similar need. If you found this helpful, please post a comment and let me know. Thanks.

Note that I am using Visual Basic .Net 2003 and Framework v1.1

Monday, August 22, 2005

Getting the volume label in .Net

To get the volume label of a particular drive, use the Dir method in the VisualBasic namespace. Note that you can ONLY use the Volume fileattribute if you want to get the volume name. Use of any other attribute will prevent the volume label from being returned. On my system, the DVD drive is my E:\, so the following statement successfully returned the volume label for me:

'Get the CD/DVD volume label
Dim VolumeLabel As String = _
Microsoft.VisualBasic.Dir("E:\", FileAttribute.Volume)


Joe Kunk

Sunday, August 21, 2005

Please feel free to comment on my posts

I just changed the settings on this blog to allow comments by anyone, not just members of blogger.com. I want to encourage comments. If you read one of my posts and have anything to add, please do so. I look forward to the dialog.

Joe Kunk

Using IO.Directory.GetFiles with a filename wildcard

I got an "Illegal characters in path" error on the following statement:
Dim TxtNames As String() = IO.Directory.GetFiles("c:\temp\*.txt")
and from the documentation it was not clear to me why I was getting the error.

Turns out that you must use the second overload version of the GetFiles method with two strings if you want to include a wildcard.

In the example above, the two possible correct versions of the GetFiles method include:

Dim TxtNames As String() = IO.Directory.GetFiles("c:\temp")
Dim TxtNames As String() = IO.Directory.GetFiles("c:\temp","*.txt")

The second statement provides the desired result of returning a string array of all filenames with a txt extension in the c:\temp folder. The first statement returns all files in the c:\temp folder which can be scanned with a loop for further processing.

Note that the single string overload verison of GetFiles can only accept a directory name.

I hope this helps clarify a point that managed to confuse me. As usual, it's obvious once you know the answer!

Joe Kunk

Friday, August 19, 2005

GLUGnet Presentation 08-18-05

Last night I gave the feature presentation to GLUGnet, the Greater Lansing User Group for .Net. The topic was language enhancements and conversion recommendations for Visual Basic 2005. The Powerpoint presentation is available for free download at http://portal.artemis-solutions.com/glugnet/go/iris/3373/en-US/DesktopDefault.aspx. Download the file named, "GLUGnet August 18, 2005.zip".

There was an error on the example for the CONTINUE statement which has been corrected.

The Internet web address for all the sites I mentioned at the GLUGnet meeting were added as the last slide of the presentation.

Thank you to GLUGnet for allowing me to give this presentation. I learned a lot by doing the research for the presentation and I really enjoyed the experience. GLUGnet is a great user group and I'm very proud to be a part of it.

If you find yourself in the East Lansing Michigan area on the third Thursday of the month, please feel free to join us for free food and drinks and an educational meeting. See the web site http://www.glugnet.org for more information.

Joe Kunk

Thursday, August 11, 2005

Two VB.NET blogs you don't want to miss

Today I ran across two blogs about Visual Basic that are awesome resources.

The first is the blog from the Visual Basic .Net IDE team. From their introduction, "This blog is hosted by the program managers, developers, and testers who work on the Visual Basic Integrated Development Environment (VB IDE). " This blog has an awesome FAQ and further tips on the cool new feature of Code Snippets. Other topics include code generation for overrides, a snippet editor, refactoring tool, new debugger features, and an awesome list of the 10 best new features in Visual Basic 2005. It can be found at http://blogs.msdn.com/vbide/default.aspx

The second is "Don't be Iffy" by Julie Lerman. This is a extensive informal blog by a self-employed programmer who clearly loves programming and Visual Basic.Net. Her personality really comes through on her blog. I think you will find it both informative and enjoyable. http://www.thedatafarm.com/blog/

Joe Kunk

Friday, July 22, 2005

When is True not True?

When saving a boolean value to a Microsoft® SQL Server 2000 database. In SQL Server 2000, boolean values are represented as data type "bit", with valid values of numeric 0 or 1. When building a SQL INSERT or UPDATE statement, we need to convert a value of TRUE or FALSE easily to a 1 or 0 respectively.

I have found the easiest way is to use the IIF command. Depending on your coding preferences, you should find one of the two choices below will work well for you:

Dim MySQLBoolean as string = IIF(MyBoolean, "1", "0")
or
Dim MySQLBoolean as integer = IIF(MyBoolean, 1, 0)

Compared to an IF-THEN-ELSE approach, I like the IIF approach since it delivers the desired result in a single line of code.Readabilityy in code is very important, but with all other things being equal, one line of code is generally more readable than five lines of code.

Joe Kunk