- Creating an instructor guide from a PowerPoint deck.
-
I recently came across the situation where I had several PowerPoint decks that were VERY well documented. Essentially, each slide had reams of notes in the Notes panel of the deck. This is both good and bad. It was good because for preparation purposes, it was very easy to review the notes as you reviewed the slides. It was bad because on many machines where I presented from, you could not split the monitors so the slides were on one machine and the notes were on another.
This presented a conundrum because it’s nice to be able to have the notes handy as I present the material. That way, I can refer back to the main bullets to make sure I covered everything before moving on. For other workshops I teach, we have an instructor guide on paper that you can have up next to your presenter machine on the podium and all is good to go.
Unfortunately, there was nothing like that setup already for this workshop. Fortunately, PowerPoint 2007 provides a feature to export your presentation to Word 2007. Full steps are located here but essentially you are looking for this screen:

This is a nice solution but the main problem that I had is that it embeds PowerPoint objects into the slide deck. This *sounds* like a good idea but it grows the file size of the document to a huge degree. For example, one deck had 36 slides and the file size of the resulting Word document was >50MB!
Fortunately, with some nice VBA code, you can do the following:
- Iterate through every shape in the document.
- Copy the PowerPoint object.
- Paste that object as a JPEG.
- Delete the PowerPoint object.
That brings the file size back down to where it should be, from my perspective. For example, in that file I mentioned previously, it brought the size down from 55MB to 642KB. Talk about a tremendous improvement!
The VBA code to use is as follows:
Sub ConvertPowerPointToImage()
Dim j As InlineShape
For Each j In Word.ActiveDocument.InlineShapes
j.Select
Selection.Copy
Selection.PasteSpecial Link:=False, _
DataType:=15,
Placement:=wdInLine,
DisplayAsIcon:=False
j.Delete
Next j
End Sub
Just copy/paste the above code into a new module. Then execute the “ConvertPowerPointToImage” macro. That will clean everything up and make the file a lot more manageable.
Enjoy!
- XmlDataSource and HTTP 401.2 Errors
-
I was talking to a co-worker last weekend and he was troubleshooting a seemingly very simple issue with an XmlDataSource and an ASP.NET Menu control. Essentially, his customer had an XML file that they wanted to use as a data source for the menu control. It looked something like this:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<menuitem url="http://www.bing.com" text="Bing!"/>
<menuitem url="http://www.live.com" text="Live!"/>
</root>
The XHTML on their ASP.NET page was something like the following:
<asp:Menu ID="Menu1" runat="server" DataSourceID="XmlDataSource1">
<DataBindings>
<asp:MenuItemBinding DataMember="menuitem"
NavigateUrlField="url"
TextField="text" />
</DataBindings>
</asp:Menu>
<asp:XmlDataSource ID="XmlDataSource2" runat="server"
DataFile="http://localhost/MenuSample/SampleXMLFile.xml" />
Can you spot the potential problem here? Well, when they ran the above code and got this error:
Now, one thing we found out was that their simple page (and XML File):
<asp:XmlDataSource ID="XmlDataSource2" runat="server"
DataFile="http://localhost/MenuSample/SampleXMLFile.xml" />
is being hosted in a VDir/Site protected with Windows Authentication. In-fact, if we look at the IIS Logs, we see that the IIS is returning a 401.2 response:
2009-09-03 05:33:47 ::1 GET /MenuSample/SampleXMLFile.xml - 80 - ::1 - 401 2 5 1
After examining the call stack and the source code for the XmlDataSource, we see that in order to retrieve the XML file, we do something akin to the following:
System.Xml.XmlDocument document = new System.Xml.XmlDocument();
System.Xml.XmlTextReader reader = new System.Xml.XmlTextReader("http://....");
document.Load(reader);
The XmlDocument and XmlTextReader will eventually instantiate an XmlDownloadManager, which instantiates an HttpWebRequest – which is responsible for downloading the XML file. At no point, though, are Credentials applied to this request, so the HTTP 401.2 response is returned from IIS.
Well, in this customer’s case – this was enough information for them and they moved the XML file to their local site and adjusted the DataFile attribute to a local file:
<asp:XmlDataSource ID="XmlDataSource1" runat="server"
DataFile="~/SampleXMLFile.xml" />
So, for this customer – the problem was solved. However, my mind started churning with ideas on how to solve this type of problem. So, here is some ridiculous fun that I had solving the problem.
First, a disclaimer:
********************************************************
These postings are provided as is with no warranties, and confers no rights. Additionally, views expressed herein are my own and not those of my employer, Microsoft.
In addition, the following solution is intended to provide a sample of what can be done to solve this problem and is not an optimal solution for a number of reasons outlined below.
This is, simply put, some ridiculous fun that I had the other night bending the .NET framework to my will.
********************************************************
Okay, glad that’s out of the way. So, how would you resolve this? Unfortunately, the XmlDataSource does not have any extensibility points to add an authentication element to its xml file loading process. So, there are really two things you can do:
- Subclass the XmlDataSource and build-in that level of authentication.
- Handle the Credentials piece when the HttpWebRequest gets created.
I wrote both pieces but for this posting, I’ll only cover the second option because it uses an extensibility point that is not often utilized.
So, in this instance, we would like to make sure that the HttpWebRequest that gets instantiated by the application always has the Credentials properties supplied to it. Because we have no control over the code that consumes the HttpWebRequest – in this case the XmlDownloadManager class, if we want to control this, we need to go even further down.
So, ideally, when you call something like:
System.Net.HttpWebRequest request = (System.Net.HttpWebRequest)
System.Net.WebRequest.Create("http://...");
We would want to make sure that the System.Net.HttpWebRequest being returned from the Create(…) method has the Credentials property populated. So, how would we do something like this?
Well, the .NET Framework exposes a little known extensibility mechanism called webRequestModules. From the linked MSDN page, it states:
“The webRequestModules element registers descendants of the WebRequest class to handle information requests to network hosts. Web request modules must implement the IWebRequestCreate interface.
The .NET Framework includes Web request modules for URIs that begin with http://, https://, and file://. You can override the default modules only by registering a custom module in the configuration file.”
So, with this in-mind, we can create our own class that implements the IWebRequestCreate interface. This class will just do the following:
- Create an HttpWebRequest object.
- Set the Credentials to the HttpWebRequest
- Return the HttpWebRequest to its caller.
So, here’s the class to do exactly that:
public class CustomWebRequestCreator:System.Net.IWebRequestCreate
{
public System.Net.WebRequest Create(Uri uri)
{
// Step 1 - Instantiate the HttpWebRequest.
// We must use reflection since the constructor isn't public.
Type t = typeof(System.Net.HttpWebRequest);
object[] args = { uri, null };
System.Reflection.ConstructorInfo[] cons =
t.GetConstructors(
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
System.Net.HttpWebRequest request = null;
request = (System.Net.HttpWebRequest)cons[2].Invoke(args);
// Step 2 - Set the Credentials property.
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Step 3 - Return the object to its caller.
return request;
}
}
So, now that we have the code, we can compile it into an assembly and then add it to the webRequestModules section of the Web.Config:
<system.net>
<webRequestModules>
<remove prefix="http:"/>
<add prefix="http:"
type="WebRequests.CustomWebRequestCreator, WebRequests"/>
</webRequestModules>
</system.net>
The configuration element above removes the default .NET creator used for paths starting with “http:” and then substitutes ours in its place.
So, now when you run the project with the above code, the requests logged in IIS are:
2009-09-03 21:28:16 ::1 GET /MenuSample/SampleXMLFile.xml - 80 - 401 2
2009-09-03 21:28:16 ::1 GET /MenuSample/SampleXMLFile.xml - 80 - 200
The first request returns an authentication request and because we are setting the credentials of the HttpWebRequest, we can authenticate correctly and thus retrieve the Xml file.
Now, there are a few things to keep in mind with this approach:
- This is a GLOBAL approach. This means that ANY HTTP web request made by your application will go through this code. So, caveat emptor.
- This code uses reflection to instantiate the HttpWebRequest – which can have some performance implications. You can do things to optimize this code-path but your mileage may vary.
Like I said above, this was just a bit of ridiculous fun on a Sunday night.
Enjoy!
- What’s new in .NET 4.0 SOS
-
***************************************************
PLEASE NOTE – THIS POST IS BASED ON THE .NET 4.0 BETA THAT WAS MADE PUBLICLY AVAILABLE IN APRIL/MAY. THIS INFORMATION IS SUBJECT TO CHANGE.
***************************************************
So, anyone that I’ve worked with during my tenure knows that I have a big love affair with WinDBG and SOS. In-fact, once you know how to use it, I’ve found that it’s my first choice in terms of debuggers – beating even Visual Studio. So, when we released the first beta of .NET 4.0, I rushed to download it and see what’s available.
Here are some new commands and new ways of doing things. If you are not familiar with WinDBG and SOS, you can read a walkthrough here.
Loading SOS:
Looks like mscorwks is gone and “clr.dll” is the new base assembly. So to load SOS, the command you would use is:
.loadby sos clr
Granted, you can still use:
.load c:\windows\microsoft.net\framework\vXXXXXX\sos.dll
But the “.loadby” command is shorter.
Some New Commands:
!ThreadState
In the !threads output, there was that “State” field:
0:000> !threads
…
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 820 0015e990 a020 Enabled 00000000:00000000 00164ed8 0 MTA System.OutOfMemoryException (00d1deb8)
2 2 244 0016fa88 b220 Enabled 00000000:00000000 00164ed8 0 MTA (Finalizer)
This is a bit field that represents the state of the thread. So, you put the value from the State field into the !ThreadState command and you get the current state of that thread. For example:
0:000> !threadstate a020
Legal to Join
CoInitialized
In Multi Threaded Apartment
!GCWhere
If you give it an object address, it will tell you which generation, heap and segment the object is located in. For example:
0:000> !gcwhere 00d1db20
Address Gen Heap segment begin allocated size
00d1db20 0 0 00d10000 00d11000 00d1e010 0xc(12)
!FindRoots
When you are debugging a live process (doesn’t work in a dump file), you can use it to be notified when a collection is occurring on a specific generation:
0:000> !findroots -gen 0
0:000> g
(c3c.9c0): CLR notification exception - code e0444143 (first chance)
CLR notification: GC - Performing a gen 0 collection. Determined surviving objects...
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012f124 ebx=00000000 ecx=00000000 edx=00000006 esi=0012f1e8 edi=00000003
eip=7c812afb esp=0012f120 ebp=0012f174 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
KERNEL32!RaiseException+0x53:
Then, at this point, you can give !findroots an object address to get the cross-generational references to the object, for example (from the help file):
0:002> !findroots 06808094
older generations::Root: 068012f8(AAA.Test+a)->
06808094(AAA.Test+b)
!HeapStat
Will list out where the managed memory falls in the various heaps:
0:000> !heapstat
Heap Gen0 Gen1 Gen2 LOH
Heap0 193888 12 12 16976
Free space: Percentage
Heap0 12 0 0 80 SOH: 0% LOH: 0%
!AnalyzeOOM
When an OutOfMemoryException occurs, you can execute this command to do some preliminary analysis on why the OOM is occurring. For example:
0:000> !analyzeoom
Managed OOM occured after GC #8 (Requested to allocate 100000016 bytes)
Reason: Didn't have enough memory to commit
I think that’s it at an initial glance. Please feel free to comment if I’ve missed anything.
Enjoy!
- When are my LINQ db connections closed?
-
So, I was working with a customer who is writing their first application using LINQ. They had previously been bitten by the failure to close and dispose their SqlConnection objects. This is actually a fairly common problem and usually leads to those pesky SqlExceptions detailing that there are no connections left in the pool.
So, since LINQ to SQL abstracts out much of the direct database interaction, they were concerned about when the underlying SqlConnections are closed. I will walk through how I answered their question using a few of my favorite tools:
To start off, I created a simple SQL Table called Users:

Then, I created a simple LINQ to SQL dbml:

Now that the plumbing is in place, I can write some simple code to return the data from the table and display it to the console window:
LinqConnectionSampleDataContext db =
new LinqConnectionSampleDataContext();
Table<User> users = db.GetTable<User>();
IQueryable<User> userQuery =
from user in users
orderby user.firstName
select user;
foreach (User user in userQuery)
{
Console.WriteLine("ID={0}, First Name={1}",
user.id,
user.firstName);
So, now when the application is executed, the output is as follows:

So, since Linq to Sql uses an underlying SqlConnection to do its work, we can set a breakpoint on the Close() method of that class in WinDBG. If you are unfamiliar with this great debugging tool, you can find a simple walkthrough on how to set it up here.
There are a number of ways to set a breakpoint in managed code in WinDBG. Here are the steps that I followed:
Step 1. Launch WinDBG and attach to the process in question.
Step 2. Load the SOS extension into WinDBG by executing:
.loadby sos mscorwks
Step 3. Set the managed breakpoint using the !bpmd command. For this step, the !bpmd command accepts a variety of parameters. Basically, you can pass it either:
I chose the latter method because it’s relatively quick and I knew exactly what I wanted. So, the syntax for this method is:
!bpmd <module name> <managed function name>
You can get the module name from visiting the SqlConnection page up on MSDN. On this page, we can get the module name and the namespace to the class:
From this, we can get both parameters necessary:
- Module Name: System.Data.dll
- Managed Function Name: System.Data.SqlClient.SqlConnection.Close
So, our command in WinDBG becomes:
!bpmd System.Data.dll System.Data.SqlClient.SqlConnection.Close
Once you enter in this command, you should get output similar to the following in the WinDBG window:
0:014> !bpmd System.Data.dll System.Data.SqlClient.SqlConnection.Close
Found 1 methods...
MethodDesc = 544a0418
Setting breakpoint: bp 5455DC80 [System.Data.SqlClient.SqlConnection.Close()]
Step 4. “Go” in the debugger and wait for your breakpoint to be hit.
For this, the command is simply “g”.
0:014> g
Eventually, your breakpoint will be hit in the debugger and you should get output similar to the following:
Breakpoint 0 hit
eax=5457da68 ebx=04d7e9dc ecx=0185cd30 edx=018e56b0 esi=01870d80 edi=04d7e9a4
eip=5455dc80 esp=04d7e860 ebp=04d7e868 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
System_Data_ni+0xcdc80:
5455dc80 55 push ebp
Step 5. Print out the call-stack.
The command to print out the call stack in SOS and WinDBG is “!clrstack”:
0:008> !clrstack
This will print out the managed call stack, which turns out to be:
OS Thread Id: 0x1d70 (8)
ESP EIP
04d7e860 5455dc80 System.Data.SqlClient.SqlConnection.Close()
04d7e864 77e20586 System.Data.Linq.SqlClient.SqlConnectionManager
.CloseConnection()
04d7e870 77e20554 System.Data.Linq.SqlClient.SqlConnectionManager
.ReleaseConnection(...)
04d7e87c 77e1da35 System.Data.Linq.SqlClient.
ObjectReaderCompiler+ObjectReaderSession`1[...].Dispose()
04d7e888 77e1ddac System.Data.Linq.SqlClient.
ObjectReaderCompiler+ObjectReaderSession`1[...].CheckNextResults()
04d7e894 77e1df2c System.Data.Linq.SqlClient.
ObjectReaderCompiler+ObjectReaderBase`1[...].Read()
04d7e8a0 77e1ea2d System.Data.Linq.SqlClient.
ObjectReaderCompiler+ObjectReader`2[...].MoveNext()
04d7e8ac 004f1a12 LINQ.SqlConnection.Program.Main(System.String[])
So, if you’re having trouble parsing this, the take away here is that when you iterate through a Linq resultset and you get to the end, the ObjectReaderSession will automatically close the Connection to the database.
Now, this is a simple HelloWorld code sample for retrieving a result-set and there are obviously a number of ways to do the same thing. The customer’s code was closer to the following:
using (IEnumerator<User> enumerator =
context.ExecuteQuery<User>(sqlStatement).GetEnumerator())
{
while (enumerator.MoveNext())
{
// Do something here
}
}
In this situation, we get an IEnumerator<T> back from the database call and iterate through it. Now, this part is very important. If you are iterating through the result set to completion – the connection will be closed the same as the above. However, if you do something like this:
using (IEnumerator<User> enumerator =
db.ExecuteQuery<User>(sqlStatement).GetEnumerator())
{
while (enumerator.MoveNext())
{
Console.WriteLine("ID={0}, First Name={1}",
enumerator.Current.id,
enumerator.Current.firstName);
// Stop iterating after this record.
break;
}
}
Please note the “break” statement. Essentially, if you are NOT iterating through to completion, the call stack looks like:
OS Thread Id: 0x251c (11)
ESP EIP
0522e73c 5455dc80 System.Data.SqlClient.SqlConnection.
Close()
0522e740 77e20586 System.Data.Linq.SqlClient.SqlConnectionManager.
CloseConnection()
0522e74c 77e20554 System.Data.Linq.SqlClient.SqlConnectionManager.
ReleaseConnection(...)
0522e758 77e1da35 System.Data.Linq.SqlClient.ObjectReaderCompiler+
ObjectReaderSession`1[...].Dispose()
0522e764 77e1ea12 System.Data.Linq.SqlClient.ObjectReaderCompiler+
ObjectReader`2[...].Dispose()
0522e768 00691bde LINQ.SqlConnection.Program.Main(System.String[])
The connection will NOT be closed until you call Dispose() on the ObjectReader (IEnumerable) object. This means that if you happen to write some code without the Using… statement when returning data like this:
IEnumerator<User> enumerator =
db.ExecuteQuery<User>(sqlStatement).GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine("ID={0}, First Name={1}",
enumerator.Current.id,
enumerator.Current.firstName);
// Stop iterating after this record.
break;
}
The SqlConnection.Close() method will NOT be called. This is because you have full control over the lifetime of the IEnumerator<T> object and you should know when you are done with it.
Now, along those lines, you may be asking yourself – what if I did something like this:
LinqConnectionSampleDataContext db =
new LinqConnectionSampleDataContext();
Table<User> users = db.GetTable<User>();
IQueryable<User> userQuery =
from user in users
orderby user.firstName
select user;
foreach (User user in userQuery)
{
Console.WriteLine("ID={0}, First Name={1}",
user.id,
user.firstName);
break;
}
Where you break before you iterate through to completion? In that situation, Dispose() will still be called on the IQueryable<T> object. How? Because of a compile-time optimization we do. We insert a finally statement after the userQuery has been used. This compiles down to (in IL):
try{
L_005d: br.s L_0084
L_005f: ldloc.s CS$5$0002
L_0061: callvirt instance !0...get_Current()
L_0066: stloc.3
L_0067: ldstr "ID={0}, First Name={1}"
L_006c: ldloc.3
L_006d: callvirt instance int32 LINQ.SqlConnection.User::get_id()
L_0072: box int32
L_0077: ldloc.3
L_0078: callvirt instance string LINQ.SqlConnection.User::get_firstName()
L_007d: call void [mscorlib]System.Console::WriteLine(string, object, object)
L_0082: br.s L_008d
L_0084: ldloc.s CS$5$0002
L_0086: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_008b: brtrue.s L_005f
L_008d: leave.s L_009b
}finally{
L_008f: ldloc.s CS$5$0002
L_0091: brfalse.s L_009a
L_0093: ldloc.s CS$5$0002
L_0095: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_009a: endfinally
}
L_009b: ret
.try L_005d to L_008f finally handler L_008f to L_009b
The text in red is my emphasis. So, the moral of this story, when you take control of the data yourself, you MUST call dispose on the IEnumerable<T> object when you are done with it.
Enjoy!
- Working for Microsoft: Week 98
-
So, I was listening/watching a Hanselman webcast today and I journeyed over to my site and realized that I haven’t blogged anything in almost 2 years. There are certainly some reasons for it but being we have just started a new fiscal year, I thought I would try to step-it-up a bit.
First, some house-cleaning. About 97 weeks ago, I had to turn off anonymous comments. I’m not happy about it but the amount of spam that I got in my blog comments was ridiculous. Eventually I’ll upgrade the blog engine to include some captcha stuff but until then feel free to shoot me messages if you don’t want to register.
In any case, on with the show. The obvious question is – what have I been doing during these weeks? Well, the obvious answer is that I’ve been working a lot. A large part of my (newish) job at Microsoft is traveling to customers and helping them with their applications. As a result, while I’ve gained a lot of status with airlines and hotels, it hasn’t left me with a lot of time to spend on this medium. I’ve set some things in motion to free up more of that time so expect to see some more entries coming soon.
In the interim, I have been involved in the community in small ways for a while via 2 social media platforms:
I’ve also been helping other folks out there in the blogosphere – most notably Jeff Atwood of Coding Horror.com. He had a nasty deadlock on his StackOverflow.com site related to his use of log4net. You can read more about the work I did with him here. I’ll post my version of the tale at a later date as it’s an interesting problem and while Jeff’s solution worked – it was a bit extreme.
So, work-wise, what have I been doing as a Premier Field Engineer? Well, it is generally broken into 2 areas – reactive and proactive work.
The reactive work is where a customer is experiencing a problem (usually in production) and they need someone onsite to help the resolve the issue. These are generally pretty fun and challenging.
The proactive work is more along the lines of:
- Workshops
- White Boarding Sessions (Chalk Talks)
- Code Reviews
- Performance Optimizations
- Bottleneck Analysis
- Scalability Reviews
- Architecture Reviews
- Proofs of Concepts
- etc…
The above are challenging as well – but in a different way. I love to teach and help people write better code – and both categories allow me to do that – so I’m good.
Of course, we also get the items in between. For example, I had a customer with a custom ISAPI filter that needed to be upgraded from Windows 2000 to Windows Server 2003. Because the model changed a bit from IIS 5 to IIS 6, we needed to overhaul some of that ISAPI code. Oh, did I mention that the filter was written in VC++ 6.0? Yeah, we upgraded that as well to the latest and greatest. All over 2 weeks.
So, in addition to that stuff, I’ve also written some tools (some public, most private), created workshops and achieved Premier Executive status on United airlines and Platinum status with Marriott. It’s been a fun 98 weeks. Can’t wait to see what happens next.
- Working for Microsoft: Week 1
-
Initially I was planning to blog every day or so about my experiences within Microsoft, but alas, this has been the first free moment I've had so far after my last post. The reason? Well, as I mentioned in a previous post, the first three weeks of my new career are spent at something called "MSSU" (Microsoft Services University). This is affectionately referred to as "Boot Camp" by those in the know. The boot camp analogy is not far from the truth, I must say. Having never gone through boot camp in the traditional sense of the term, having never been in the armed services, I referred to a military site and realized that it has been pretty close to my experience, thus far - Minus the disparaging drill sergeant type atmosphere.
Basically, the intention is to get us up to speed on Microsoft, the products, tools, etc. as fast as possible. Most days start with a 7am shuttle ride to one of the campuses and then you are in class until sometime after 5pm'ish. So, here's a few things I've learned in my relatively short tenure:
- Be Prepared to Eat Well - When I was in college, they had something they called the "Freshman 15". Basically, in the first semester of college, you expect to gain about 15 pounds. I think I am going to start the "Microsoft 15". Every day, I am given 3 square meals. Breakfast usually consists of eggs, bacon, etc. Lunch is usually a trip to one of the many Cafeterias on campus. You're usually on your own for dinner, but they have a lot restaurants within walking distance from the Apartments. It has been years since I've consistently eaten 3 square meals. I'm going to have to start hitting the gym while I'm here.
- Be Prepared to Work - As I mentioned above - each day starts at 7am and goes late. During that time, you will learn about the MS Products, the Tools available, the culture, etc. Everyone pretty much uses OneNote to track all of the various information provided. In the past, I would generally use a Text editor to take notes - but I have certainly started drinking the OneNote kool-aid. It's a great tool and I wish I had had a license in my previous life.
- Take the Time to Socialize - It's not often I have gone through a training regiment where everyone is really intelligent. Most people are smart, no doubt about that, but at a class like this - everyone is scary smart. Part of this boot camp is meant to allow you to build relationships with other people in your position. Chances are, you will be communicating with these same people throughout your career. It really behooves you to get to know them and build those relationships now. Every night, the other PFE's and I have gone out for drinks and dinner and the conversations have ranged from Exchange to .NET to Active Directory to Windows Server to BizTalk to SQL Server. Everything is fair game and you will learn a lot about a ton of products during these informal conversations.
- Set up your Outlook Rules - As Scott Hanselman mentions here, one of the things that has amazed me so far are all of the e-mails that get sent around. In my third day or so on the job, I opened up my inbox and had several HUNDRED e-mails there waiting for me. Therefore, it was a necessity to starting setting up my Outlook rules and start the forwarding process into folders. I'm sure as I get added to more and more lists, my rules list will get longer and longer.
- Start Making Redmond/Seattle Connections - During my first day at NEO, I got a call from a .NET PFE based out of Redmond welcoming me to the team. Since there are few other .NET PFE's starting, he offered to go out for drinks one night to pick his brain. I highly highly highly recommend doing this. I learned more about the position and what challenges I will be facing during that few hour session than I did throughout the several week hiring process. The bottom-line is that the culture of MS is to help your peers and other employees. Truly incredible.

- Explore the Campus - Although, you may not be located in Redmond, that doesn't mean you shouldn't take a walk around the campus and pop into the offices. As part of a team building event yesterday we had to walk around the campus. It was truly a great experience and I got to see the homes of teams that I've long followed as an external admirer. For example, just imagine sitting in Bill Gates' old office in Redmond. You get a real sense of history in certain areas. Very cool stuff.
Honestly, I think most of these things can be applied to anyone starting a new job, but it just may be more prevalent in a MS field based role.
Overall, I've really enjoyed myself. So far, this has been an incredible experience and I can't wait for the next 2 weeks.
Enjoy!
- Working at Microsoft: Day 1
-
Well, I have officially been in the Seattle area for about 24 hours. My flight in was fine and yes, the weather was cold and rainy. I'm very happy that I picked up a raincoat before I left.
After arriving and checking into my long term housing, I took a walk around the area and ended up at a nice little bar and grille called 520 in Bellevue to catch the second half of the Bears game - and for once, the Bears didn't lose. I consider that a good omen.
Here's a pic of my home for the next 3 weeks:

The view from the apartment:
The next morning, the other PFE's and myself took a nice shuttle ride from our housing to the Microsoft campus. We knew we had arrived when we saw this:
The entire day was spent in New Employee Orientation. The day was good, learning much about the company and the tools available to us as Microsoft employees.
This new stage in my career is finally beginning to feel real. I expect it will feel even more so the longer I'm out here.
I'm still working on the samples for the SamuraiProgrammer.Web.UI - and I should have that up soon.
Enjoy!
- Releasing the Source Code for .NET
-
I was just about to head to sleep when I read this great post by Scott Guthrie. Apparently, Microsoft will be releasing the source code for the BCL (Base Class Libraries), ASP.NET, WinForms, etc. when .NET 3.5 and VS2008 (aka Orcas) is released later this year.
I have to admit that I was looking forward to reading some of that code when I started my new gig over there next week. Silly me, I considered it a perk for working over at MS <grin>.
In any case, us developer type folk could always use Reflector to browse through the code, but the level of integration with the IDE (along the line of the Symbol integration) is really slick. Plus, we get to see the comments in that code. I know that they'll probably clean them up prior to release or maybe they won't. I've seen some pretty wacky (and humorous) comments over the years, so I can only imagine the type of things in that code.
Some funny comments I've read that come to mind:
- "It's late, I'm tired and this is as good as it's going to get."
- "I know this shouldn't be hardcoded. Leave me alone."
- In a summary tag for a method: "Blah blah blah"
- I even recall someone that had pasted lyrics into their code comments. Go figure.
Anyway, now I'm really off to bed.
Enjoy!
- Developers Love Their Tools...
-
Seriously, I think there are few things that Developers are more passionate about than the tools and toys they use to code. Carl has compiled a great list of Free Visual Studio 2005 Addins. You can read the list here.
The addins that I use on a (almost) daily basis for VS 2005 are (in no particular order):
- AnkhSVN
- CodeKeep Add-in
- GhostDoc 2.1.1 (wish there was a VB version of this one)
- Modeling Power Toys
- Pinvoke.Net Addin
- Refactor! family of Addins
- Smart Paster 1.1 (HUGE time saver)
- Dpack
If you know of one that is not listed on Carl's blog, just drop a comment and he'll evaluate and add it. Please be aware that I think he has a 15-20 addin backlog at this moment.
As Orcas (ahem, Visual Studio 2008) is right around the corner, I'd be interested to see a list of addins for that IDE. Wishful thinking, but if you see a list - drop me a line.
Enjoy!
- Giving XBOX Live a Chance
-
I am far from a hardcore gamer. I don't log on and play for hours upon hours because well, I just don't have that much spare time to play. It's a shame, but hey, that's life. In any case, one thing that I've been meaning to do for years was sign up for an XBOX Live membership. Several of my friends are avid gamers and have weekly Thursday night Halo battles (was Halo 2 and now it's Halo 3). One of my closest friends has been trying to get me online for years to join them but I never took them up on it.
The main reason that I never signed up was because I simply didn't have any free ports on my Router. I know that's a lame reason, but it's true. I only had a 4 port router - with Wireless - but with a few servers, a few desktops and a laptop (or 2) in my home office my measly 4 ports were filled up pretty quickly. And Yes, I could have purchased the XBOX 360 Wireless Netowrking Adapter but maybe I'm just old school in that I always worry about network lag when I'm wirelessly gaming. I remember when I was first playing some online games on my PC back in the day (ie: 10 years ago), specifically Star Wars Jedi Knight: Dark Forces II, network lag was a big issue in the online component. I would be having a heated Light Saber duel and my opponent would disappear for a second or two. All of a sudden, he would reappear and I'd be dead having taken a Saber hit to my head. Aaah, those were the days. :: Sigh ::
Well, last night, since I was already at Micro Center picking up a new Hard Drive Enclosure, I stopped by the networking section and picked up a new 8 Port Router and a new 100 foot cable. I swear, Micro Center is a very very dangerous place for a geek to wander around within. I went there looking to spend about $50 and left spending about 5 times that. :: Grin ::
In any case, once I was home, installation was a breeze - with one caveat. Plastered all over the internal router were these stickers and labels saying "Run the CD before hooking up the router". Skeptical, I did pop in the CD and found it to be a step by step guide on how to plug everything into your network from the perspective of the newbie. What they should have said is - if this is your first router, then run the CD. Oh well - what a waste of about 30 seconds of my time.
After hooking everything up, the XBOX Live membership process was a breeze, although, I couldn't use "SamuraiProgrammer" as my handle (too many characters). I had to settle for something else:
Yes, I realize that my Gamer Score is lame but in all fairness I played the heck out of my original XBOX and none of my achievements (nevermind that they didn't exist back then) carried over to my 360. Feel free to add me to your list of friends and perhaps we can jam on some multiplayer Frogger one day.
I do have a few tiny tiny nitpicks, though.
1. When you are online via the XBOX, you have a lengthy list of icons you can use to decorate your gamer card. However, when you view your gamer card online - you have much fewer. This means that if you pick an icon that is not on the XBOX Live site, your icon will default to the fella featured
on the right. It would seem to me that any icons they allow you to select from online should be available on the XBOX Live site.
2. Points. This was something my friend told me about last night. Apparently if you choose to buy points in the store, they're sold in batches of 1600 or 4000 at about 80.04 points per dollar (USD). If you buy them on the XBOX Live site or while you're in XBOX Live, they're sold in increments of 500/1000/2000/5000 at exactly 80 points per dollar. So, if you discount tax and shipping/handling, it's actually cheaper to buy your points in a place like Best Buy instead of online.
Like I said, though, those are just a few tiny tiny nitpicks. Overall, the XBOX Live experience is really incredible! It is truly much superior to the Wii and PS3 equivalent. I can already tell that I'll be spending a lot more time playing my 360, which I guess is the whole point.
One last plug for Scott Hanselman, though. He, Rich Claussen and Jason Mauer are hosting a Halo 3 tournament on October 20 as a benefit to raise money for Diabetes. Since I'll be out that way for my MS Bootcamp during this time, I'm going to try and show-up. But if you are in Vancouver, WA and would like to meet some MS guys and play Halo 3 on a 50 foot screen in Super Hi-Def, check out his blog entry and sign-up.
Enjoy!
- The Road Not Taken
-
"...Somewhere ages and ages hence:
Two roads diverged in a wood, and I--
I took the one less traveled by,
And that has made all the difference."
-- Robert Frost
You may have noticed that I've been more quiet than normal for the past month or two. There is a reason for that. I have decided to leave my current company of almost 4 years for another opportunity. Starting Monday, October 8, 2007, I will be a full time employee of Microsoft on the Premier Field Engineering team! If you're not familiar with this position at Microsoft, the description on the Microsoft Careers site states:
"The purpose of the Premier Field Engineer (PFE) position is to provide Microsoft customers with reliable technical solutions to the complex integration problems associated with business solutions built on the Microsoft platform. The PFE Team supports a diverse variety of technical solutions built with Microsoft technology and products Typical tasks performed in this role include specific problem isolation and correction, user and kernel mode debugging, conducting application design and supportability reviews, performance tuning, application stability consulting/troubleshooting, code reviews, and porting/migration assistance, configuration management, pre-rollout testing and general development consulting. The prospective PFE candidate should draw upon all resources at Microsoft, to advise and consult on the use of Microsoft technologies to avoid such problems in the future."
This position immediately appealed to me because of its customer focus and technically challenging environment. Plus, I get to help developers do their job better! That alone is a great high for me and I'm really looking forward to this new adventure! From speaking with members of my new team and my future manager, I can tell that this team is composed of incredibly intelligent folks and I am honored to be joining them.
All that said, though, this decision was far from easy for me to make. I have had the distinct honor to work with some wonderful folks at my current company. Plus, I was able to architect and design some great applications. In the end, though, I knew that I had to take the road "less traveled by" and join a company that is constantly changing the world.
Here's a quick FAQ on the process and my new position:
- Do you have to move?
Nope. I get to stay in good ol' Chicago for the time-being. Even better, I get to work from home when I'm not at a client site.
- Wait, does that mean you don't get to go to Redmond at all?
Well, actually, I do. For all folks in this arm of Microsoft's service group, your first three weeks are spent in Redmond at a bootcamp. This is where I'll get wonderful things like my ID badge, laptop and learn all about the Service leg of MS.
- How did you find out about the job? How did you apply, interview, etc.?
Well, one of my friends sent me a link to the initial Job Details page saying that it might be a good fit for me. After that, it was just a matter of signing up on the MS Careers site and submitting my resume. This was the first time that I had ever applied to Microsoft before, so I wasn't entirely sure what to expect. Pretty soon after I officially applied, though, I received an e-mail from an Recruiter over there and everything kind of snowballed from there. All in all, the process took just over a month, from initial communication to the Offer stage. I probably could have shortened that time-span, but I wanted to provide adequate time to prepare for each interview.
- What were the interviews like?
Well, my interview came in three stages:
1. HR phone interview. This was there the Recruiter got to tell me about the position, the company, normal tasks, etc. He also asked some fairly basic .NET questions.
2. Technical Screen. This was an hour-long phone conversation with an incredibly sharp guy. The questions were NOT easy, in-fact, this was probably the most difficult tech screen I have ever encountered. They were simply not interested in how you develop a webpage, but instead asked some really difficult questions about garbage collection, the JIT compiler, and loads of .NET memory related questions. We also got into some Windbg debugging and memory dumps. Certainly not for the faint of heart.
3. In-Person Technical Interview and Managerial Interview. This was about 2 hours long at one of the local MS offices and included a technical interview with 2 PFE's and a managerial type interview with 2 PFE Managers. The technical interview included more of the same types of questions mentioned above but also included some best practice-type questions about Exception Handling and debugging issues. The managerial interview included a lot of behavioral questions and also about how you manage difficult situations.
- How did you prepare for them?
I took a page from Jayson Knight's blog and studied my little heart out. My average was about 4 hours every night studying everything I could find and then some. I re-read several books and tons of great blogs.
- What's going to happen to your blog?
For the time being, I'm planning to keep it updated, but the focus may change a bit. As my new position will include a lot of deep technical dives into some great technologies - the focus may be a bit more deeply technical in nature. As I have always tried to keep the focus on simplifying development, though, expect more of those same types of entries.
- How did management at your current company take the news?
Honestly, their reaction surprised me. LOL...in a good way, I suppose. They didn't even try to keep me. As soon as I said "Microsoft" their eyes lit up and they offered their congratulations and gave me many hearty handshakes and hugs. They all knew that this was a dream for me and it's nice to know that they were as happy for me as I was. It was truly a testament to the wonderful and supportive folks that they employ.
All in all, I feel very fortunate to have been given this incredible opportunity. I can't wait to start my new adventure!
- SamuraiProgrammer.Web.UI - v0.5
-
One of my friends suggested that I should provide a download for all of the little code snippets, controls, etc. that I throw up on my blog so people don't have to constantly copy/paste into their IDE of choice.
I thought about it and it sounded like a good idea but I think I'll do things a little bit different. I fully intend to provide code downloads for each of my blog entries, but in addition to that, I'm going to expand upon them in a separate download. The reason for this is that many times, I post very very simple solutions to common problems or issues. I always leave it as an exercise for the reader to further enhance the simple code I provide.
I like the way this works, but a lot of times I do the enhancing myself. So, I might as well make that code available as well. In this initial release (arbitrarily set as v0.5), I provide the following controls:
- ValueCheckBox - This was a control that I described here and is simply a CheckBox with an additional property (CheckBoxValue) that can be used in a grid/list/anywhere where you want to store an actual useful value for quick reference later.
- ConditionalDisplayPanel - This was a control that I initially described here and was a simple way to hide portions of a templated control from the user at runtime. Since its first creation, though, I've actually enhanced this quite a bit. Using something similar to the GridView/SQLDataSource binding model, I have made this control a lot more flexible and extensible.
When you add this control to your page, you can specify in your XHTML a SuccessTemplate and a FailureTemplate. Within each template, as with most templated controls, you can specify the controls/HTML to appear for each condition. For example:<sp:ConditionalDisplayPanel
runat="server" ID="cdpTest"
EvaluationSourceId="cesCustomCheck">
<FailureTemplate>
<asp:Label runat="server"
ID="lblFailure"
Text="Failure"></asp:Label>
</FailureTemplate>
<SuccessTemplate>
<asp:Label runat="server"
ID="lblSuccess"
Text="Success"></asp:Label>
</SuccessTemplate>
</sp:ConditionalDisplayPanel>
- EvaluationSource - In order to provide looser coupling between the uses of this control and the control itself, these simple controls provide a nice abstracted method of providing the condition for which the FailureTemplate and SuccessTemplate appear. All EvaluationSource controls implement the IEvaluationSource interface which provides a method of evaluating success or failure. The EvaluationSources included in this release are:
RoleEvaluationSource: Provides a method to evaluate against a user's role membership. <sp:RoleEvaluationSource runat="server"
ID="resRoleCheck"
RoleName="Test">
</sp:RoleEvaluationSource>FieldEvaluationSource: Provides a method to evaluate against a a null or empty field value in a DataView.<sp:FieldEvaluationSource runat="server"
ID="fesAttributeName"
FieldName="AttributeName">
</sp:FieldEvaluationSource>CustomEvaluationSource: Allows the developer to specify their own logic to be evaluated. <sp:CustomEvaluationSource runat="server"
ID="cesCustomCheck">
</sp:CustomEvaluationSource>Then, in the code behind, you simply handle the CustomValidate event raised in the control to provide your own logic to evaluate. Protected Sub cesCustomCheck_CustomValidate( _
ByVal source As Object, _
ByVal args As CustomValidateEventArgs) _
Handles cesCustomCheck.CustomValidate
args.IsValid = True
End Sub
- ControlPersister - This allows you to specify the values in one or more controls on a page and save them to a cookie when a certain event is raised by another control on the page. In addition, the control will place the values back into the controls when the user next visits that page. An example of the XHTML would be useful here perhaps:
<sp:ControlPersister runat="server"
id="fpSearchFields"
PersistenceKey="Search"
PersistenceTypeName="cookie">
<Sources>
<sp:ControlSource ControlName="txtSearchCriteria"
ControlProperty="Text"
Name="SearchCriteria1"
Type="String" />
<sp:ControlSource ControlName="ddlSearchCriteria2"
ControlProperty="SelectedValue"
Name="SearchCriteria2"
Type="String" />
<sp:ControlSource ControlName="chkSearchCriteria3"
ControlProperty="Checked"
Name="SearchCriteria3"
Type="Boolean" />
</Sources>
<SaveTriggers>
<sp:SaveTrigger ControlId="btnSearch"
Name="SearchButton" />
</SaveTriggers>
<ResetTriggers>
<sp:ResetTrigger ControlId="btnReset"
Name="ResetButton" />
</ResetTriggers>
</sp:ControlPersister>
Here is an explanation of each property on the base ControlPersister control:
PersistenceKey: A string value representing the name of the cookie for this page. This is useful if you have more than one ControlPersister on a page.
PersistenceTypeName: A string value representing the medium in which these values should be stored. For this version of the control, only a "cookie" is available. In future versions, you will be able to store the values in Session, Database or even using a File Based persistence model.
Here is an explanation of each property on the ControlSource object:
ControlName: A string value representing the name of the control to persist.
ControlProperty: A string value representing the property of the control from which you should persist the values of the control. This property is also used to *set* the value of the property on the initial page load.
Name: A string value representing the name of the instance of the ControlSource.
Type: A string value representing the type of data to be stored.
Here is an explanation of each property on the SaveTrigger object:
ControlId: A string value representing the name of the control that would trigger the save operation of this control. The control referenced here must implement the IButtonControl interface which exposes the Click event.
Name: A string value representing the name of the instance of the SaveTrigger.
Here is an explanation of each property on the ResetTrigger object:
ControlId: A string value representing the name of the control that would trigger the reset operation of this control. When the user clicks this button, any values being persisted would be cleared and would not load the next time the page loaded. The control referenced here must implement the IButtonControl interface which exposes the Click event.
Name: A string value representing the name of the instance of the ResetTrigger.
I think that's it for this version of the SamuraiProgrammer.Web.UI. You can download the assembly here. I'll try to get some samples posted within the next week or so.
Any questions or comments, please feel free to comment or contact me via the link on this page.
Enjoy!
- String Concatenation
-
This is an old topic, but as I work with developers more and more, I find that there is still some gray area on this topic.
Concatenating strings is probably one of the most comment tasks that most developers perform in their work-lives. If you've ever done some searching around, though, you'll find that there are 2 main ways that people perform those concatenations:
First, you can use the "+" or the "&" operator with the native string object:
Sub ConcatWithStrings(ByVal max As Int32)
Dim value As String = ""
For i As Int32 = 0 To max
value += i.ToString()
Next
End Sub
Second, you can use the System.Text.StringBuilder() object to perform the concatenation:
Sub ConcatWithStringBuilder(ByVal max As Int32)
Dim value As New System.Text.StringBuilder()
For i As Int32 = 0 To max
value.Append(i.ToString())
Next
End Sub
So, which is better? Well, if you do any Google'ing or Live Search'ing on the topic, you'll find some great articles on the topic. Mahesh Chand has a great article, which I'll quote:
"You can concatenate strings in two ways. First, traditional way of using string and adding the new string to an existing string. In the .NET Framework, this operation is costly. When you add a string to an existing string, the Framework copies both the existing and new data to the memory, deletes the existing string, and reads data in a new string. This operation can be very time consuming in lengthy string
concatenation operations."
Now, I'm a big fan of short descriptions like the above, but I always find that without actually showing what is happening behind the scenes, you might lose some folks in the translation. To illustrate what happens behind the scenes, I wrote a quick console application that uses the first code sample - and then took a memory dump using ADPlus to show what actually gets kept around in memory after the String level concatenation.
I'm not going to go into a lot of detail on what I did to mine through the memory dump, but if you're interested in getting down to this detail on your own - I highly recommend the book Debugging Microsoft .NET 2.0 Applications. After you read that great book, you should read Tess's Great Blog to get even more practice on this area.
In any case, what was uncovered after the memory dump was that with the listing in the first example, there will be a separate String object placed into memory each time you perform a concatenation. Here is an excerpt from that dump:
# Size Value
1 28 "0123"
1 28 "01234"
1 32 "012345"
1 32 "0123456"
1 36 "01234567"
1 36 "012345678"
1 40 "0123456789"
1 44 "012345678910"
1 48 "01234567891011"
1 52 "0123456789101112"
1 56 "012345678910111213"
.....
1 124 "0123456789101112131415161718192021222324252627282930"
1 128 "012345678910111213141516171819202122232425262728293031"
1 132 "01234567891011121314151617181920212223242526272829303132"
1 136 "0123456789101112131415161718192021222324252627282930313233"
1 140 "012345678910111213141516171819202122232425262728293031323334"
1 144 "01234567891011121314151617181920212223242526272829303132333435"
65 17940 ""012345678910111213141516171819202122232425262728293031323334353"
First, the command I used outputs the first 65 characters or so within the String object. This is why the last entry has a count of 65 instances. In any case, as you can see, there is a separate copy of each string made into memory during each concatenation operation. Ouch. This can get expensive very quickly!
Now what about the StringBuilder operation?
# Size Value
1 52 "0123456789101112"
1 84 "01234567891011121314151617181920"
3 956 "012345678910111213141516171819202122232425262728293031323334353"
Gee, that seems a lot simpler - but if you're paying attention, you'll notice that there are a few other instances of this lengthy string in memory. Any ideas why?
Well, this appears to be my bug. When you instantiate a System.Text.StringBuilder object, one of the constructors allows for a integer parameter called "Capacity". If you do not specify an initial capacity, the noargs constructor defaults to "&H10" - which is 16 characters. If, during your string operations, you exceed that 16 character capacity, the StringBuilder will create a new new string with a capacity of 32 (16 * 2) characters. The next time, you perform an operation that needs more than 32 characters, the StringBuilder will double the capacity again - this time to 64 characters. This will continue to happen over and over again as you continually append more characters to the StringBuilder object.
So, what does this mean? Well, it means that we're still not achieving the maximum efficiency here. If we want to build a String like this - without creating extra instances of the base string object - even when using a StringBuilder object, you should specify a capacity in the constructor. To prove this hypothesis, we can rewrite the second code listing to be:
Sub ConcatWithStringBuilder(ByVal max As Int32)
Dim value As New System.Text.StringBuilder(193)
For i As Int32 = 0 To max
value.Append(i.ToString())
Next
End Sub
Now, when we take a memory dump and look for the String objects for the above loop:
# Size Value
1 404 "012345678910111213141516171819202122232425262728293031323334353"
We see that there is only one instance of the String object in memory.
Moral of the story? Even when using the StringBuilder object, if you know the final string is going to be lengthy, you should set an initial capacity to most efficiently perform your string concatenations.
Enjoy!
- Visibility of Fields Within FormView
-
A quick trick that I'm sure many of you know. Suppose you had a FormView on a page bound to a datasource. Within that FormView, you have several fields that may or may not contain data. Now, suppose that for whatever reason, your sponsor would like you to make a control and its label disappear if the corresponding field contained no data. What are the different ways to achieve this simple requirement?
- You can handle the DataBinding event of the FormView and set the visibility of controls in the depending upon a field's value.
- You can set the Visible property of the controls based upon a method defined in your page's code behind. Possibly something like this:
Public Function ShouldDisplay(ByVal text As Object) _
As Boolean
If text Is DBNull.Value OrElse _
String.IsNullOrEmpty(text.ToString.Trim) Then
Return False
Else
Return True
End If
End Function
And then you can set the visible property of the controls like this:
<asp:Label ID="Label"
runat="server"
Visible='<%# ShouldDisplay(Eval("Description")) %>'
Text='<%# (Eval("Description")) %>'>
</asp:Label>
3. You can combine #1 and #2 and encapsulate this into something a little reusable.
Enter the ConditionalDisplayPanel. This is something very simple which has potential to save you time over and over again. To show how it works, here is the definition of a simple FormView control before applying the logic from above:
<asp:FormView ID="FormView2"
runat="server"
DataKeyNames="AttributeId"
DataSourceID="SqlDataSource1">
<ItemTemplate>
AttributeId:
<asp:Label ID="AttributeIdLabel"
runat="server"
Text='<%# Eval("AttributeId") %>'>
</asp:Label>
<br />
AttributeName:
<asp:Label ID="AttributeNameLabel"
runat="server"
Text='<%# Bind("AttributeName") %>'>
</asp:Label>
<br />
</ItemTemplate>
</asp:FormView>
Now, in this example, suppose you don't want to display the AttributeName text or Label if there is no data in the AttributeName field in the datasource. In that case, you simply wrap the AttributeName HTML with the ConditionalDisplayPanel like so:
<asp:FormView ID="FormView1"
runat="server"
DataKeyNames="AttributeId"
DataSourceID="SqlDataSource1">
<ItemTemplate>
AttributeId:
<asp:Label ID="AttributeIdLabel"
runat="server"
Text='<%# Eval("AttributeId") %>'>
</asp:Label><br />
<sp:ConditionalDisplayPanel runat="server"
ID="pnlAttributeName"
FieldName="AttributeName">
AttributeName:
<asp:Label ID="AttributeNameLabel"
runat="server"
Text='<%# Bind("AttributeName") %>'>
</asp:Label>
<br />
</sp:ConditionalDisplayPanel>
</ItemTemplate>
</asp:FormView>
Please note the FieldName property on the ConditionalDisplayPanel. This property tells the ConditionalDisplayPanel which field should be evaluated to determine if the contents of the control should be displayed. At runtime during the binding of the controls in the FormView, the ConditionalDisplayPanel will evaluate the contents of that particular Field and if there is any data in the field, it will display the AttributeName HTML. If not, then the opposite will happen.
Simple, no?
Well, how does this work? Actually, the ConditionalDisplayPanel was relatively simple to construct. Here is the full class:
Namespace WebControls
Public Class ConditionalDisplayPanel
Inherits Panel
Private _fieldName As String
Public Property FieldName() As String
Get
Return _fieldName
End Get
Set(ByVal value As String)
_fieldName = value
End Set
End Property
Private Sub ConditionalPanel_DataBinding( _
ByVal sender As _
Object, _
ByVal e As _
System.EventArgs) _
Handles Me.DataBinding
'// The naming container here (in a bindable context)
'// implements the IDataItemContainer
Dim container As IDataItemContainer = _
DirectCast(Me.NamingContainer, _
IDataItemContainer)
'// Using the property of that container,
'// DataItem, you can get at the DataRowView being bound.
Dim row As Data.DataRowView = _
DirectCast(container.DataItem, _
Data.DataRowView)
'// Retrieve the value from the row.
Dim value As Object = row(Me.FieldName)
'// Perform the comparison and set the Visible
'// Property Accordingly.
If value Is DBNull.Value OrElse _
String.IsNullOrEmpty(value.ToString.Trim) Then
Me.Visible = False
Else
Me.Visible = True
End If
End Sub
End Class
End NamespaceBy using this simple control, you can control this situation in one place for your entire application. It could obviously be enhanced to perform many other checks, but as this was a 15 minute thing, I thought it would be best to keep it simple.
I hope this helps!
Enjoy!
- ConfigurationFileWather?
-
Just something I thought was a bit funny/interesting. I was debugging someone's code earlier today and I happened to glance down at my threads in VS and noticed this:

Any idea of what caught my eye? Yup - the text that says: "_ConfigurationFileWatherThread". After switching to the thread, I noticed that the mistype is actually in the Enterprise Library v3.0 code, in the class called Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Storage.ConfigurationChangeFileWatcher. The method is called "BuildThreadName" and is:
protected override string BuildThreadName()
{
return "_ConfigurationFileWatherThread : " + configFilePath;
}
Since I was working with v3.0, I checked in v3.1 to see if the "bug" was fixed. Unfortunately, looking at the released source for v3.1 - it was not fixed. I know this is really picky, but I even went so far as to open an issue. <sigh>.
In any case, it's nice to know that nobody is perfect.
Technorati Tags:
.NET,
Debugging