Speednet's Blog

Page 2 of 2

Simulating VB.NET's lost String() function

Before VB.NET was around — back in the days it was just called "Visual Basic" — there was a function named String(), which was used to repeat a character sequence multiple times.

For example, String("Abc", 5) produced "AbcAbcAbcAbcAbc".

In VB.NET, the String() function was dropped, probably because the name conflicts with the construction of a new String value, as in Dim str As New String().

When they dropped it from the language, I would have thought that they would replace it with a new function — maybe something like StrClone().

Searching the web, the question seems to be asked quite often, "How can you do the equivalent of String() in VB.NET?"

Annoyingly, every time it is asked, someone invariably answers by saying they should use the StrDup() function, or New String("A", 5).  They completely disregard that the person wants to concatenate a multi-character string, not one character.

One person even suggested incorrectly that the String constructor could be used to do it — as in New String("Abc", 5) — which it can't.  That would produce a string of 5 "A" characters.

So here is an easy solution.

To create a string consisting of 10 copies of "Abc":

MyString = Replace(Space(10), " ", "Abc")

Or, if you want to use all .NET framework methods (instead of the Visual Basic library), use the following:

MyString = New String(" "c, 10).Replace(" ", "Abc")

Note: the "c" character after the space (" "c) means a literal character (Char) instead of a character string.  It is a tiny efficiency.

Yes, there are other ways of doing this, perhaps by manipulating character arrays.  This, however, is a simple, maintainable — and quick — way of doing it.

With this little trick, you can create your own StrClone() function as follows:

Function StrClone(ByVal Source As String, ByVal Count As Integer) As String
    Return Replace(Space(Count), " ", Source)
End Function

A nice simple function to complete anyone's String library!  Hopefully Microsoft will consider adding this convenience function back into the Visual Basic library.

Entry #9

ASP.NET: Resolve Virtual Paths with a Simple Anchor Tag

Web developers have traditionally struggled with the issue of maintaining relative links throughout a web site that would always point to the correct folder/directory, no matter where the link was referenced from.

A relative link shows the web browser how to navigate up or down the folder structure of the site in order to get to the target page.

Lottery Post, being a very large and deep site, has this problem extensively.

For example, let's look at the link to the contact page located at www.lotterypost.com/contact.aspx from a forum topic located at https://www.lotterypost.com/thread/161745.

As you can see, the contact page is located in the site's root folder, and the forum topic is located in a folder called "thread".  From the perspective of the forum topic, the contact page is located "up" in the folder structure (or "down" is you envision your root folder at the bottom), so a relative link would look like this:

<a href="../contact.aspx">Contact</a>

The two dots and the slash ("../") before the file name indicates that the browser needs to "go up one folder" to find the contact page.  If I was viewing an individual post at https://www.lotterypost.com/thread/161745/887609 I would need to go up two folders, so I'd put "../../" before the file name.

When coding a huge site it becomes immensely difficult to maintain all those relative paths, especially when you decide to change the folder of a page.

For example, if I changed the topic page one level deeper (like www.lotterypost.com/forums/thread/161745), I would need to remember to change the relative page to the contact page inside it.

With hundreds of links on every page, the problem is magnified.

ASP.NET helps with "Web application root operator"

ASP.NET introduced a tilde character ("~") as a new operator called the "Web application root operator".  The operator only works with server controls.

Its function is to substitute the proper relative path at runtime from the current page to the target page.

So if the topic page uses a server control like this:

<asp:HyperLink NavigateUrl="~/contact.aspx" Text="Contact" runat="server" />

It outputs the following HTML to the page at runtime:

<a href="../contact.aspx">Contact</a>

Because I used the tilde character in the path, if I later move the topic page to a different folder depth, it will automatically adjust to the correct relative path in the final HTML page output.

Use the same concept with a normal <a> tag

Because the tilde character only works with server controls, if you tried to insert the tilde into a normal anchor ("<a>") tag, the literal text of the tilde would be output exactly as you wrote it — no relative path substitution would occur at runtime.

For example:

<a href="~/contact.aspx">Contact</a>

..would yield exactly the same thing on the final HTML page, with no relative path substitution:

<a href="~/contact.aspx">Contact</a>

If you clicked that link, the browser would try to go to the page www.lotterypost.com/thread/~/contact.aspx — obviously not what you want, and would produce a 404 Not Found error.

The trick to making the simple anchor tag ("<a>") substitute the relative path at runtime?

Simply add runat="server" to the tag, which transforms it into a server control, as follows:

<a href="~/contact.aspx" runat="server">Contact</a>

Now when you open the page it will correctly substitute the relative path, and will look like this:

<a href="../contact.aspx">Contact</a>

This substitution occurs because ASP.NET pre-processes the URLs of all built-in server controls, substituting the tilde character with the proper relative path. 

Note, this substitution does not automatically occur on your own custom controls.  For that, you need to transform the tilde yourself, like Rick Strahl's solution (see the comments on that post too).

All HTML controls can be "upgraded"

This is a very simple example of the power that many people do not know even exists with regular HTML controls.

You can add the same runat="server" to any regular HTML control and change it into a powerful server control.

Want to change whether or not a regular HTML control is rendered to the page at runtime?

Simply add an id and runat attributes to the control, and then you can manipulate the visible property from your code behind, just like a regular server control.

After experimenting with this you may find, like I did, that there are several uses for "upgrading" regular HTML controls.  However, I have found in my own experience that uses of this method should be restrained to very simple cases, such as changing the visibility or class at runtime.

The reason is that the ASP.NET controls are more powerful, so if you intend on doing powerful things you don't want to start with a control that will limit you at some point.

Enjoy your new set of "upgraded" controls!

Entry #8

Simple function to split camelCase words

The phrase "camel case" is used to describe a common method of naming variables, properties, and methods in a programming language.

It is useful for stringing several words together, while maintaining readability.  (More here)

If you ever have the need to split apart the words in a camel case word, here's just about the easiest way to do it (shown in both VB.NET and C#):

SplitCamelCase() - VB

Function SplitCamelCase(ByVal Source As String) As String()
    Return Regex.Split(Source, "(?<!^)(?=[A-Z])")
End Function

SplitCamelCase() - C#

string[] SplitCamelCase(string source) {
    return Regex.Split(source, @"(?<!^)(?=[A-Z])");
}

 

Entry #7

Never use a MultiView inside a Repeater

...Unless you want a ViewState larger than the rest of your page

First off, without the help of Fritz Onion's ViewStateDecoder, my life would have been messed up a lot longer than it was tonight, as I was searching for a reason to explain a 60K ViewState embedded in a page.

I meticulously turned off the ViewState in every server control that did not need it with no apparent effect on the ViewState size.

Googling, I came upon Frtiz's ViewState tool, which allows you to see the contents of the ViewState.  The utility actually has quite a few nice features, such as the ability to pull ViewState out of any published page -- directly.

What the tool showed me was that a lot of what I was seeing in the page was actually the ControlState, not the ViewState.  Of course, it all looks the same when you see it in the page, but there is an big difference between the two.

ControlState is something you can't turn off.  It is a control's fail-safe way of being able to save state information, just in case ViewState is disabled in the page.

So I traced almost the entire bulk of ViewState in the page to 3 MultiView controls that were sitting inside templates  of a Repeater control.  I could disable ViewState for the entire page, and it still couldn't touch that ControlState.

Who designs these things?

Anyway, I came up with a solution that allows me to keep the MultiViews, and even leave them inside the repeater.

I built a very simple MultiViewLite control, which is inherited from the MultiView, and disables the ControlView.  When I was finished, I just replaced <asp:Multiview... with <MyControls:MultiviewLite... and everything worked perfectly!

The complete code code listing for the MultiViewLite control can be found below.  It has the identical functionality of the standard MultiView control, with the exception that both ControlState and ViewState are disabled.

MutliViewLite.vb

Public Class MultiViewLite : Inherits MultiView

    Protected Overrides Function SaveControlState() As Object
       
Return Nothing
   
End Function

    Protected Sub Control_Init(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Init
       
Me.EnableViewState = False
    End Sub

End Class

 

Entry #6

Updating Large Value Data Types in SQL Server 2005

SQL Server 2005 added some new large value data types that are extremely useful for storing large chunks of text.

Such is the case with any large forum web site, such as Lottery Post.  Each forum post — whether humongous or a couple of words — occupies just one row [record] in the database.  In fact, the entire content of a forum post is contained within just one cell [field].

The Lottery Post database was formerly stored using SQL Server 2000, before it was upgraded to SQL Server 2005 in November 2005.  At the time, the only way to store the forum posts was using a text data type, which was fairly inefficient.

A text data type stores all of its data — for the one column — in an external text file, and each row in the data table contains a pointer into the text file, to where the data for each row is stored.  Using that model, the performance of data updates can be handicapped by all the file I/O necessary to maintain the linkages and separate data files.

SQL Server 2005 introduced new data types for storing huge blocks of data: varchar(MAX), nvarchar(MAX), and varbinary(MAX).  Like the regular versions of these data types (using a number, 8,000 or less, to represent the maximum size instead of MAX), the new large value data types store the data in the data file itself, not an external file.  Thus, the new data type are far more efficient.

It turns out there are additional benefits to these new large value data types as well.  By accident, and because of my curiosity, I discovered that there have been modifications to the SQL UPDATE statement specifically for the new data types.

The new syntax allows the developer to modify a large value in the same way that the STUFF() function works.  That is, it can insert a character string directly into the current value, and optionally replace the number of characters you specify.  It's called a partial update.

This is a tremendous leap in efficiency, as the data in the cell does not need to be deleted and replaced, as would happen in a normal update.  At some point, every database operation boils down to file I/O, and this is about as minimal a file I/O operation as you can get for inserting data.

The main difference between the partial update and the STUFF() function is the the partial update cannot create

Partial update syntax (using common options)

UPDATE table_name
    SET column_name .WRITE ( expression , @Offset , @Length )
    [ FROM{ <table_source> } [ ,...n ] ]
    [ WHERE { <search_condition> ]
[ ; ]

  • column_name is the original value into which expression will be inserted.
  • expression is the value to insert.
  • @Offset is the starting point in column_name at which expression is written.
  • @Length is the number of characters deleted from column_name, starting from @Offset, before expression is inserted.

Notes

  • column_name cannot be NULL and cannot be qualified with a table name or table alias.
  • @Offset is a zero-based ordinal position, is bigint, and cannot be a negative number.
  • If @Offset is NULL, the update operation appends expression at the end of the existing column_name value and @Length is ignored.
  • @Offset cannot be greater than the length of column_name, or else an error is returned.
  • If @Offset plus @Length is longer than the value in column_name, column_name is truncated starting at @Offset.
  • @Length is bigint and cannot be a negative number.
  • If @Length is NULL, the update operation removes all data from @Offset to the end of the column_name value.
  • If expression is set to NULL, @Length is ignored, and the value in column_name is truncated at the specified @Offset.

Examples

For each example we will be updating a table called MyTable, which has a column named MyCol.  We will look at the effect of updating a simple character string.

Starting value of MyCol = 'Hello there.'

Example 1 — Inserting a value with no deletion.

UPDATE MyTable SET MyCol .WRITE(', my friend', 11, 0);

Result = 'Hello there, my friend.'

Example 2 — Truncate all existing data from the @Offset position. (@Length = NULL)

UPDATE MyTable SET MyCol .WRITE('Sam!', 6, NULL);

Result = 'Hello Sam!'

Example 3 — Append value to the end of the column. (@Offset = NULL)

UPDATE MyTable SET MyCol .WRITE(' How are you?', NULL, 0);

Result = 'Hello there. How are you?'

Example 4 — Remove characters from the end of the column. (expression = NULL)

UPDATE MyTable SET MyCol .WRITE( NULL, 5, 0);

Result = 'Hello'

Example 5 — Remove some characters and replace them with expression.

UPDATE MyTable SET MyCol .WRITE('Joe', 6, 5);

Result = 'Hello Joe.'

Final thoughts

In addition to the previous benefits mentioned, the .WRITE clause of the UPDATE statement also has logging efficiencies.  Partial updates to large value data types using the .WRITE clause are minimally logged.

The .WRITE clause cannot be used with the text data type.  In fact, if you are currently using a text data type in your database (or ntext or image) it would be a good idea to convert them to the new large value data types, because Microsoft has stated that they will stop supporting them in some future version of SQL Server.

Now that you have this new technique in your bag of tricks you can look in your SQL Server database for places to use it.  The performance gained should be worth your while.

Entry #5

How to restore the default Windows zip file association

Windows XP and Vista have built-in support for .zip files.  When you click a .zip file, the contents open in a nice Windows Explorer view panel.

When you install some compression programs, like WinRAR, or Zip Genius, or a hundred other programs, they usually like to change the .zip file association, so that when you click a .zip file, it opens their program instead of the default Explorer view.

I had that happen to me, and it's very hard to change back.  If you do the normal "Properties ... Change Program", there is nothing listed there for "Compression" or anything that would indicate the default .zip view.

After some searching on the web, I came across a command that did the trick.  To run the command, first open a command window.  In Vista, it needs to be a command window with Administrator rights, so right-click the icon for the command window, and choose "Run as administrator".

Then, type the following command and press Enter:

cmd /c assoc .zip=CompressedFolder

Voilà!  Everything should be set to the default in an instant.

Entry #4

ASP.NET Tip: How to prevent the title tag from rendering

In ASP.NET, those who want complete control over the contents of the <head> section of a page get frustrated by the fact that ASP.NET automatically inserts a <title> tag.

You can even set Me.Page.Title to String.Empty, and it will still generate and render a <title> tag.

The answer is maddeningly simple:

Put a <title> tag into the <head> section as follows:

<head>
      <title visible="false" runat="server"></title>
</head>

This turns the <title> tag into a server control, and then the visible attribute prevents the automatically-generated title from rendering to the page.

Entry #3

Discovered source of nasty Opera bug

What's the first thing that comes to mind when you hear the word "Opera"?  Is it a long, boring series of songs that you can't understand the words to?

If that's the case, then you really need to check out the Opera web browser, which happens to be a nice alternative browser.  It may end up becoming your favorite, because it is very fast and features unique and picturesque graphics.

Opera is sort of at the low end of the top-tier of web browsers.  It's been around for years and is feature-rich, but it suffered for a long time because the company that created it tried to charge money for it.  Opera does have a loyal following though.

There aren't a ton of people using Opera to browse Lottery Post, but there are always some Opera users online, so I try to make sure LP works OK in that browser.

For the longest time I've been struggling to figure out why Opera was not displaying images on Lottery Post correctly.  It mainly affected things like bullets, toolbar buttons, command buttons — basically the small graphic elements.  They would be displayed as repeated images, or mis-sized, or in the wrong place.

I must have gone through the style code a million times to figure out the cause of the problem.  I researched the Internet for others who may have had a similar problem, and scoured all my reference books to be sure every line of CSS style code was up to spec. 

Nothing I tried had any effect.  The images continued to display properly in every web browser — except Opera.  What a nightmare!

Then I started noticing that there were a few buttons where the image was being shown correctly.  For example, the "Delete" button on a forum post had the red "X" symbol in the right spot, perfectly sized, and not repeating.  Right next to it, every other button was messed up.

So of course my first step was to examine the code to see what I was doing differently to the Delete button.  But the code was identical to all the other buttons!  Grrrrr..... 

Sitting here writing this, I can't remember why I did it, but I guess for s**ts and giggles I opened one of the messed-up images and re-saved it.  At that point the image for that button started appearing properly!  HUH?!

I racked my brain to figure out why the heck re-saving the file would have any effect to the way Opera displays images.  The only possible explanation is that I had optimized all the GIF image files, to make them as small as possible, and Opera cannot handle the optimizations.

I know the software I used manipulates the graphics palette embedded in the file as part of its optimizations, so my guess is that Opera cannot handle advanced GIF palette optimizations.  It seems to get tripped up by transparent edges on the optimized images.  (Optimized images with no transparency appear normally in Opera.)

The strange thing is that I can't find any information about this specific bug on the web.  I see lots of blogs and forums posts about background image bugs in Opera, but nothing that talks to this specific issue.  So they always say if you find something, blog about it.

I can't remember exactly which GIF optimizer I used, but I'll post the name if I figure it out.  They all pretty much work the same way, so i doubt the optimizer is at fault.  Plus, the fact that the images work properly in every other web browser makes me think it's Opera's fault.

So to fix the problem I had to literally open and re-save every GIF image on Lottery Post, which is not quite as straight-forward as it sounds.  A web site the size of Lottery Post that's almost 8 years old has a ton of images — all over the place. 

And 50% of the time when you de-optimize images with palette manipulations, strange things happen to transparent portions of the images, like all the sudden turning black or some other color.  So I had to re-draw by hand lots of images, pixel-by-pixel.  Nothing inspires more grumpiness (not to mention an aching hand) than manipulating the pixels of a thousand images.

Now Opera users can now enjoy the same splendor of images as the rest of us!

Entry #2

My new blog for technical articles

From Todd — A.K.A. Speednet


I want to post a brief intro to my new Blog, which I will be using to post technical articles about Microsoft development (such as ASP.NET, .NET in general, VB, C#, SQL Server), as well as other general technology-related topics.

The reason I created this Blog was to keep my technology articles separate from all the personal stuff I like to post also.  I figure that someone who came here looking for development info doesn't want to have to wade through my posts about how global warming is a crock (for example). Big Grin

There are so many really cool things I have created, developed, learned, and figured out as a result of this on-going development project called "Lottery Post" that I wanted to have someplace to put all that knowledge and experience.

I may also move some of my previous technology posts from my personal Blog into this one, but I'm not sure about that yet.

Anyway, I just wanted to give the heads-up on this new Blog, and I hope all you propeller-heads (like me!) enjoy it!

-Todd

Entry #1
Page 2 of 2