Never write another RequiredFieldValidator again

A time saving class that writes them all for you

I don't know about you, but I seem to write RequiredFieldValidators (referred to as RFVs from here on out) on pretty much every project I work on.  You just can't count on the user to fill stuff out unless you make them do it.  On a recent project, I was going to have to write a TON of RFVs, and to top if off, this form was likely going to change a lot, so I didn't want to have to re-write them later over-and-over again.  So I figured out a way for .Net to do the work for me... isn't that the point of programming?  As long as you follow good naming conventions, all you need to do is call the function and blam, you have RFVs.

 

Generally, I just use RFVs for DropDownLists and TextBoxes (DDLs and TBs from here on out), so I came up with a class that finds DDLs and TBs and makes RFVs for them.  BUT, I soon realized that I don't want to have everything be required, so I made the class look for only those with '_req' (required) on the end of their ID property.  So, a TB with ID="txtFirst_Name" will not get a validator, but one with ID="txtFirst_Name_req" will.

 

With that out of the way, I wanted it to give you somewhat readable and clear error messages, so the validators will be very descriptive and readable if you use good naming conventions.  For example, the DDL with ID="ddlCity_and_State_req" or ID="City_and_State_req" will both have a validator with the message "Must select a City and State".  As you can see, the prefixes, if present, are trimmed (which you can specify if you want, the defaults are 'txt' and 'ddl') and underscores (_) are replaced by spaces ( ).

 

The function takes in:

  • Panel pnl  - Required - The Panel you want this to run on
  • string[] trimFromFront - optional - An array of prefixes you wanted trimmed off for the ErrorMessages; default is 'ddl' and 'txt'
  • string defaultDropDownValue - optional - a string that is considered 'empty' in a DDL; default is '-'
  • string validationGroup - optional - the ValidationGroup that you want these controls to validate to; default is null

 

Examples of how to call the code (remember to pass null if you are not specifying an optional value): 

  // all defaults, but you DO have to include the HtmlForm of your page
validators.setupValidators(pnl, null, null, null);
   // defaults, but sets the ValidationGroup
validators.setupValidators(pnl, null, null, "valGroup1");
   // sets the DropDown 'empty' state to 'select'
validators.setupValidators(pnl, null, "select", null);
   // trims off prefixes 'TextBox', and 'DropDown' in the ErrorMessage
validators.setupValidators(pnl, new string[] {"TextBox","DropDown"}, null, null);

*IMPORTANT this does require a ValidationSummary control on your page, otherwise you will not see the errors, though they will be working.  This was the only way to keep the output clean as teh RFVs are added to the bottom of the page. Also, I recommend calling this in your OnInit event.

 

Here is the code if anyone wants to check it out: Show/Hide


Download just the class here:


 

 

Full working example (see it in action): 



 

kick it on DotNetKicks.com

Awesome JavaScript DateTime Picker

A great tool that is completely customizable and can do more than your current DateTime picker!

The folks at Zapatec came up with an amazing and free customizable DateTime picker.  You can check out all sorts of demos here, or make your own and use their javascript (no downloads) here.  I love this tool, gonna be using it a lot from now on, thanks Zapatec!  I included an example of how to use it in asp.net.  Also, I have found it easiest to use the wizard on their page to customize it.



Content type 'bla-bla' not found in web

This was the error I was receiving after a restore from MOSS 2007 Standard to MOSS 2007 Enterprise

It seems that MOSS is still a bit buggy as I often run across random errors that have no place happening.  I ran into this error when I was migrating a Site Collection from Standard to Enterprise.  All of the other collections went off without a hitch, but this one was giving me problems from the start.  First I want to explain what I think was causing this.  Site Collection B (SCB from here on out) was at a root level on the old farm; it had it's own url and was it's own stand-alone 'portal'.  Now when I migrated, I needed to move SCB under a root collection SCA.  I have done this before, but never run in to any problems, but this was different for some reason?

 

I did my restore with the stsadm command line tool and it cheerfully told me that the 'Operation completed successfully', so I tried to go to the site, but what do I see:

Error

File Not Found.

Damnit... but the site rendered, so I did some checking around.  Everything else on the site seemed to work, if I typed in any other url under that site root, I could get there.  I could get to site settings, to create site, anything, but the main page was bunked out.  I went back to the old version and saw that that page was pretty customized, not to mention not being default.aspx but some odd name that the admins came up with.  So, on a whim, i made a totally blank welcome page (default.aspx) and backed it up, restored, and boom... everything worked.  Not exactly sure why the other page wasn't working, but that is water under the bridge as far as I am concerned, re-making a single page isn't all that tough... sorry users!

 

Now I figure, as long as they can re-make a page, no big deal right?  So I click on Site Actions -> New Page and I get this beauty:

Error

Content type '0x01010007FF3E057FA8AB4AA42FCB67B453FFC100E214EEE741181F4E9F7ACC43278EE811' not found in web

Now after countless searched on the internet I turn up basically nothing, TechNet is dry, and no one seems to have had this error (not a first for me sadly).  I ask a desperation post on Experts-Exchange and run through the drills.  Checking my xml config files, looking at error logs, etc.  Then the guy helping me suggests I try something simple... run the SharePoint Products and Technologies Configuration Wizard on all the machines.  Surely it can't be that simple after I bashed my head into the wall for over 24 hours?!

 

It was; just run SharePoint Products and Technologies Configuration Wizard on all your machines and this will go away.  Would that have been so hard to include in the Error message?  Would it?

VB vs C# experiment - Random shift generator for your employees

Some random programming in both C# and VB, using lists, objects, ternary operators, etc.

Now normally, I would never write anything in VB, it kind of annoys me.  But I was bored this last weekend (nothing to do in Kuwait) and I was helping someone out on Experts-Exchange (it's something I do when I am bored) when I came across a problem that looked kind of fun:  The guy had a bunch of employees, three shifts (24 hours) and weekends were in no way tied to any specific day.  He wanted to randomly generate a schedule (I am assuming this would be to eliminate the idea of favoritism/complaining).  Either way, I was bored and this sounded interesting - it was a request in VB, but since I hadn't done VB in a while, I figured brushing up wouldn't hurt.  I wrote it first in C#, then translated to VB.  Here are the two programs in action (not that there is any functional difference):

 

Shift Shuffler in C#

Shift Shuffler in VB

 

I did originally write it in C#, then translated to VB, though some things are a bit different in each, so don't expect a one-for-one comparison, though the logic is the same.   One thing I was really surprised at was the fact that my VB code was considerably shorter (86 vs. 116 lines) than my C# despite my C# being a lot more concise and cleaner.  For example I used ternary operators in C# as opposed to if-thins in the VB which are a lot longer (1 line as opposed to 5).  I did have a 10 line summary in C#, but it was still surprising.  Looking at this, I figured it was due to {'s and }'s as I like to have them on their own lines, but at least half of those could be directly compared the 'End' statements of VB.

 

I found that the Dim statements in VB got do me a little annoying, and the whole byVal, As, and all that redundant junk seemed to be unnecessary.  I also found it hard to kick the old habit of ending every line with my trusty semi-colon as well.  And I noticed it took about 5 minutes to change 'modes' in my head from one language to the other, but overall, they are really the same thing and knowing one basically means you know the other. 

 

Either way, I still like my C# better, but doing an exercise like this helps keep even the best programmers sharp, as knowing cross-language translation and even being able to read the 'other' language is often helpful when searching for solutions or helping out a co-worker that has gone to the dark-side. If you ask me C# is better!  It's my site, and what I say goes.

 

Anyways, here is the code if anyone is interested, it requires you set up your own data access (I used Linq) or just manually add in employees.  It's not perfectly balanced with smaller groups of people, but it does a pretty good job.



Great Visual Studio Theme for download

After using this theme, I can barely use the old white background default at all

   The default color scheme for VS is just terrible, the guys at: Wekeroad came up with an awesome 'Vibrant Ink' theme which was apparently stolen from TextMate (Apple) is just great as you can see (the colors are a bit muted in my picture).  Easy on the eyes, and very distinct - I think i actually helped my coding! Thanks Wekeroad!





And my modification with a lighter background, better font/size and improved highlighting:


allowing/disallowing asp:menu links based on user roles

The asp:menu control is pretty useful for making  menus and easy 'popout' links, but what if you don't want certain users to be able to navigate to some pages?

If you are using a menu control, it is likely because you want stuff to be easy.  But if you have certain links in there that you do not want a user to be able to click if they are not in certain roles, it is not that obvious what you can do to implement that.  First think you will notice is that MenuItems do not have IDs, so you can't access them in the 'normal' asp.net way.

 

What you have to do is to access them through Menu.FindItem(), and the then you can manipulate them.  But then there is the question, if they do not have IDs, how do I find the item?  The items are looked up by their Text property in a hierarchical manner.  To access things in a hierarchy, you need to first set your Menu's PathSeperator property; set this to a delimiter (I use a comma) that you will use to seperate levels.  Here is a sample menu with this implemented:

<asp:Menu ID="mnu" runat="server" PathSeparator=",">
    <Items>
        <asp:MenuItem Text="home" NavigateUrl="~/Default.aspx"  />
        <asp:MenuItem Text="A" NavigateUrl="#">
            <asp:MenuItem Text="A-1" NavigateUrl="#" />
            <asp:MenuItem Text="A-2" NavigateUrl="#" />
        </asp:MenuItem>
        <asp:MenuItem Text="B" NavigateUrl="#">
            <asp:MenuItem Text="B-1" NavigateUrl="#" />
            <asp:MenuItem Text="B-2" NavigateUrl="#" />
            <asp:MenuItem Text="B-3" NavigateUrl="#" />
        </asp:MenuItem>
    </Items>
</asp:Menu>

Once that is done, you now can simply iterate through the items (that you already put in a list, and disable those you do not want the non-admin user to have access to:

 string[] adminOnlyItems = {"A,A-1", "A,A-2", "B", "B-3"};
if (!User.IsInRole("admins"))
{
    foreach (string s in adminOnlyItems)
    {
        MenuItem mi = mnu.FindItem(s);
        mi.Enabled = false;
    }
}
Notice how the items are written out in a hierarchical string, written like this: <Level 1 Item> <delimeter> <Level 2 Item> <delimeter> <Level 3 Item> and so on with as many or as few levels as you are looking for.  Also notice that "B-3" is unnecessary in the adminOnlyItems array, as disabling "B" disables that whole path of links, anythign under "B" will now be unreachable.  Here is a working example (you must have roles enabled and an "admin" role for this to work):


Updating to SP1 in Sharepoint MOSS 2007 Nightmare

This 'production' service pack that microsoft released is harder to install than my linux wireless NIC

NOTE

This seems to only be a problem if you are already joined to a server farm, not if it is a fresh install

Ok, I love Microsoft, I might even call myself a fanboy, but this Service Pack 1 is a disaster if you are not working with fresh install.  This is the third time I have had to install it and each time it was a mess, so I am writing it down, step-by-step for anyone who has to go through this.  Keep in mind that this particular install was done on a completely naked 6 server farm with no data on it.  Here is the guide:

 

The first service pack must be the WSS 3.0 Service Pack and must be installed on each machine one-by-one, starting with the Central admin machines. 

 

First trying installing it normally (double clicking on it), THIS STEP IS NECESSARY to continue to the other methods. 

 

If that errors out, try this:

  1. Open a command prompt
  2. Navigate to C:\program files\common files\microsoft shared\web server extensions\12\bin
  3. Type psconfig -cmd upgrade -force

 

If that errors, try this from the same command prompt:

  1. stsadm -o upgrade -inplace -forceupgrade

 

If that errors 

  1. Check the Log file produced - if it complains about not having a web.config file:
  2. Copy the web.config file from the server that hosts the Central admin in the directory: C:\program files\common files\microsoft shared\web server extensions\12\TEMPLATE\LAYOUTS
  3. Paste that to identical directory on the error machine
  4. Run stsadm -o upgrade -inplace -forceupgrade again

 

Once that is installed, install the MOSS 2007 SP1 by double clicking on it, that can be done all machines in the farm at the same time.

 

If that fails: 

  1. Open a command prompt
  2. Navigate to C:\program files\common files\microsoft shared\web server extensions\12\bin
  3. Type psconfig -cmd upgrade -force
    • This will look now for the new failed update, even though the process is the same.
    • You HAVE to try to run it normally for this to work

 

Now you are ready to run the hotfix (wss-kb941422-fullfile-x86-glb.exe)

 

This will install by double-clicking the exe, and should not error out.

 

Now you need to run the Configuration Manager on each machine one-by-one.  And you should be done with the upgrade...

 

LINKS: 

Text Input Watermarks using Javascript (IE Compatible)

Simple way to add watermarks to your textboxes for a little flair in your input forms

I have always used the TextBoxWatermark control that is supplied with the AjaxControlToolkit which is very easy to use and I highly recommend it.  But recently I was asked to figure out a way without using 3rd party tools, hence in JavaScript.  At first I figured it was going to be simple, just clear the text when you click on the box and maybe change the background color.  Simple, that works.   Thats when I ran into my next problem.

 

I wanted a textbox that was forr passwords, it would start out and say 'password' but then when you clicked on it, that would disappear and it would become a type="password" box (display text as: ******).  It worked flawlessly! ...in non-crappy browsers.  IE7 dumped all over my dreams yet again and left me with a Javascript error.  Turns out you can't change the type property in IE7 (apparently it works in IE6???) which is completely stupid.

 

So, I did some good old googling which brought me here to find out that in order to change type, you now have to make an entire new object and swap it out with the old one.  So after that, it works, but my focus is all screwed up: I click on the blank, the password text disappears, but the cursor is not in the textbox I just clicked?!  That brought me here which showed me a crazy way to reset focus on a lost object:

window.tempObject = newObject;
   setTimeout("tempObject.hasFocus=true;tempObject.focus();",1);

Now everything seems to work just fine.  BUT, they aren't.  For one thing, I had had an onblur statement in my textbox originally (not in the example, but for discussion purposes) and since the new object did not have an onblur, I had to make sure to re-add that back in.  Also, the same loss goes for CSS class, but that might want to change, so I added a new field into the function to take into account a new CSS class.  If it is left blank, it will inherit the old CSS class if there is one, or simply have no class like Britney Spears. 

 

Ok, so I *thought* that would be easy, but IE comes back again to spite me.  For most browsers, you can set Object.setAttribute("onblur", "JSmethod") but IE decides that isn't good enough, so you have to to this Object.onblur = function(){js_function(god, damnit, ie);}; (from this thread).  Ok, now that we have that fixed, we can look at the CSS class.  It's mostly the same, but this is yet another IE specific problem.  You need to include two different declarations: Object.setAttribute("class", "css_class"); for most browsers and Object.setAttribute("className", "css_class"); for IE.  This time the IE fix/hack will not work for cross-browser compatibility.  So here is the finished JS:

 

[code:js]

 function makePassword(oldObject, newClass)
{
    //newclass is the new css class to pass to the textbox
    //otherwise leave it blank ('') to inherit it's old class
    //or if there isn't one, have no class
    var newObject = document.createElement('input');
    //only switched to password if the original text was password
    newObject.type = (oldObject.value == 'password')?'password':'text';
    if(oldObject.id) newObject.id = oldObject.id;
    /// this decided whether to keep the old class or get a new one
    var cssClass = (oldObject.className && newClass.length < 1)?oldObject.className:newClass;
    newObject.setAttribute('className', cssClass);// for IE
    newObject.setAttribute('class', cssClass);// for others (these are both needed)
    //the following is hard coded to add onblur functionality to the control
    //newObject.setAttribute('onblur', 'blurred(this)');// this would work if it wasn't for IE
    newObject.onblur = function(){blurred(this,newClass);}; // because of IE
    oldObject.parentNode.replaceChild(newObject,oldObject);//this is necessary because of IE
    //this is needed since a focus() will only work the second time since it happens too 'soon'
    window.tempObject = newObject;
    setTimeout("tempObject.hasFocus=true;tempObject.focus();",1);
}

[/code]

 

Remember though that you will have to validate that the original values are not in the textboxes when the user submits, as they will not be empty initially.  Here is a fully working example to mess with, it shows how to use it both for a simple watermark/mask and also with a password field change.  It also includes how to add javascript function calls and deal with changing (or persistent) css:


 

Allowing users to change their own Active Directory properties

Sometimes it is very convenient to allow users to make their own changes

**This is almost completely stolen from Ryan Dunn's Blog with just a few modifications.  You should be able to take this, drop it into your application and run it, as long as you follow the directions it will work.

 

I had a problem with Ryan's code and for some (still unknown) reason, without the using (HostingEnvironment.Impersonate()) I had added, it would not work.  So you may, or may not need that, be sure to test both.  Also, as Ryan points out: This requires you to use Integrated Windows Authentication with impersonation and your IIS server must be set for delegation.

 

I edited it to make it a bit more friendly for the user by dumping out the info aligned as it was not originally, I also added the static string[] allowUserToEdit array that allows you to limit the attributes the users can edit themselves.  Keep in mind that this only allows users to edit SOME of the AD properties, the 'AllowedAttributes' set in AD.

 

Thanks a bunch to Joe Kaplan and the rest of the guys at http://directoryprogramming.net/ who were a huge help.  They also have a great book out that I just bought that is about the best resource you can find for asp.net LDAP programming.



Getting the row or sql value/index of the gridview row you just clicked

Getting the row you just clicked is not as obvious as it seems first hand

There are generally two cases that I need information from the row I clicked in a GridView (or listview, etc.).  The first being simply to get the index in the gridview, whether or not it was the first, fifth or 50th row.  That is pretty easy, but the second case, getting the index (or any) value of the SQL entry of the row just clicked is a little less obvious, though still pretty easy.  Here is a better explanation of that second one if it isn't clear: Say you have a GridView bound to a SqlDataSource (or Linq, etc.) and you have an column in that table that is Product_ID.  Your user is going to click on a button in one of those rows, say it is row 15, but the item Product_ID is 547.  You want to get 547 with no other information than the _Click event.

 

This is how you do it:

 

[code:c#] 

protected void btn_Click(object sender, EventArgs e)
{
   Button btnClicked = (Button)sender;
   // This will get you your GridViewRow
   GridViewRow row = (GridViewRow)btnClicked.NamingContainer;
   // This will get you the actual Index out of your SQL table
   string getIndex = this.GridView1.DataKeys[row.RowIndex].Value.ToString();
   // And this will just print it out to show it works
   Response.Write("Row Index: " + row.RowIndex.ToString() + "<br />");
   Response.Write("Sql Index: " + getIndex);
}

[/code] 

***IN ORDER FOR THIS TO WORK, you *have to* remember to add DataKeyNames="Product_ID" to the GridView tag, otherwise you will throw an OutOfBounds exception because your program won't know what the hell you are calling with DataKeys. 

Keep in mind this trick will work for any column in your table. I use this in my own app to populate a modal popup depending on what row was clicked, works very slick.