Developing For .NET

Real World .NET Methods, Tricks, and Examples

My First Podcast Interview



I had the pleasure of being interviewed in December by Dane Morgridge and G. Andrew Duthie for my first podcast.  My interview is the second installment of their new Community Megaphone Podcast (the first episode featured my good buddy Kevin Griffin).

The topics in my interview range all over, from WPF and Blend to Community, Mentoring, and more.  It was a great experience and I want to thank them both for including me. I’m especially thankful for the great editing work that made me sound good!

Listen to the full interview and be sure to subscribe to the podcast.  As always I’d love to hear your feedback.






No comments    

My Community Year in Review



Well, December is here and another Community Year is almost over.  Fortunately for me, there are a high number of quality events and user groups within reasonable driving distance, so for 2009 I thought I could swing a presentation a month. It turns out I was mistaken: while I did not present in every month, I actually ended up speaking at 15 events!

  1. Feb: RVNUG – Using Blend and Visual Studio Together
  2. Mar: Roanoke Code Camp – Templating and Data Binding in Blend
  3. April: RVNUG – Styling a WPF/EF Application (with Tolga Balci)
  4. April: Richmond Code Camp 2009.1 – Using Blend and Visual Studio Together
  5. May: HRNUG – Using Blend and Visual Studio Together
  6. May: Nova Code Camp – Using Blend and Visual Studio Together
  7. June: Blue Ridge Community College – Application Design Careers for Graphic Artists
  8. August: CHODOTNET – Using Blend and Visual Studio Together
  9. September: Raleigh Code Camp – Using Blend and Visual Studio Together
  10. September: Richmond NUG – Using Blend and Visual Studio Together (unscheduled)
  11. October: Richmond Code Camp 2009.2 – Using Blend and Visual Studio Together & Data Binding in Blend (2 presentations)
  12. October: Philly Code Camp – Data Binding in Blend
  13. October: CapArea Silverlight SIG – Silverlight 3 & Blend 3 Launch
  14. November: CMAP Code Camp – Using Blend and Visual Studio Together & Data Binding in Blend (2 presentations)
  15. November: RVNUG – Silverlight 3 & Blend 3 Launch

I also had the chance to attend several other events, most for the first time:

  1. Spring: Roanoke MSDN Road Show
  2. July: CodeStock
  3. August: DevLink
  4. October: Richmond Java User Group (to see Andy Hunt speak)
  5. November: PDC09
  6. December: Roanoke MSDN Road Show

And of course, about 20 regular RVNUG and CHODOTNET User Group meetings. I’ve been taking it easy in December, but I’m already looking forward to hitting the road again next year.  Here are events I already have on my calendar:

  1. Jan 12th: HRNUG – Presenting on Silverlight 4
  2. Jan 30th: Richmond SQL Saturday
  3. March 13th: Roanoke Code Camp
  4. March 18th: CHODOTNET (topic TBA)
  5. April 6th: CMAP NUG (topic TBA)
  6. May 22nd: Richmond Code Camp 2010.1
  7. May 19th: Innsbrook NUG (topic TBA)
  8. June 3rd: Richmond NUG (topic TBA)
  9. June 24-26: CodeStock
  10. October 2nd: Richmond Code Camp 2010.2

I’ll be adding more as the year goes.  There are a bunch of great Code Camps I don’t have on here yet: Raliegh, NoVa, CMAP, Philly, and more.  If you have a User Group and would like to schedule a presentation for the second half of the year, just email me (link is in the right hand sidebar) or hit me up on Twitter (@joelcochran).

No really, Thanks!

I want to say thanks to all of you out there, it’s been a great year for the for the community, and the people make it all worthwhile.  Have a great holiday season, and I’ll see you next year!






No comments    

You know what they say about assuming



I had an experience I thought was worth sharing.  As much as we all try to write reusable code, we still end up riding the Copy-Paste express more often than we care to admit.  (Come on, admit it… you know you do it…)  Of course, it helps if you do this with code that actually works…

The Copy-Paste Express

Several months ago, I write an application that required a custom Validation Rule for IP Address format validation. It worked great, and that project was the inspiration for my post on Custom Validation Rules.  Now, 2 months later, I want the same functionality in a new application.  While the ValidationRule class itself is reusable, the XAML code to implement it is not, so to add it to the new application, I copied the XAML over for the IPAddressTextBox.  Much to my dismay, when I executed the program the validation did not work.

Knowing that Application 1 worked as desired, I pored over the code and logic, line by line, and they were the same.  Since the XAML was a straight copy, they even used the same field and template names.  I was pulling my hair out, so I finally went back and read my post, comparing it to the code I had in place.  I was able then to find that some of the validation code was missing from the XAML.  Now I was really confused: the code on both applications was missing the same critical piece, so how could Application 1 possibly work?

If you don’t see it coming yet, it turns out that Application 1 no longer worked either.  I was testing the installed application and not the current copy of the code: when I ran it from Visual Studio, I experienced the same issue.  It took me a while to figure out what happened, but in this case my reliance on Blend was the culprit.  At some point I used Blend to update the Binding (I don’t remember why).  Normally this wouldn’t be a problem, but Custom Validation rules have to be inserted into the XAML manually.  By using Blend to update the binding, Blend removed all my custom validation logic.

Lesson Learned

The lesson here, of course, is not to make assumptions, but more importantly you need to test all changes to an application.  In this case, testing the IPAddressTextBox and the Custom Validation had been completed a long time ago, and since the functionality was never intended to be changed, it was never tested after it was complete.  And the working copies in the field were correct, so the bug would never have been reported, at least not until another release.  Even then, this particular piece of the software is typically only used once when the user configures the application, so even subsequent releases of current installs would never have revealed the issue.  Instead, it would have only shown up on a new install or a reconfiguration.  Even worse, since this is purely for validation to prevent the user from entering erroneous data, a new user would never realize the functionality was missing: instead, they would just be able to enter any value for an IP Address.  So who knows how long this bad code would have been out there, me all the while assuming it worked.

I’m still floundering in the Unit testing area, and I honestly don’t know if Unit testing would have caught this error or not since it was a GUI issue.  It does indicate to me that I need to get a better grasp on all things testing: what if this had been a similar but far more serious bug?  It also tells me I need to find more ways to get off the Copy-Paste Express.  While we’ll never get away from it completely, if I had taken the time to develop a custom control for IPAddressTextBox that implemented the validation, I never would have had this issue.  In any case, it really is the little issues that can teach you the most.






1 comment    

Use the Right Container for ListBox Scrolling



I learned something interesting about ListBox and scrolling content this week.  In hindsight it is obvious, but trust me: that’s the last thing you want to hear after two hours of beating your head on your keyboard trying to figure out why the stinking ListBox isn’t scrolling.  Read on for the gory details.

The Obvious

What I learned is this: if you have an Auto sized ListBox, meaning that it will stretch its Width and Height to match its contents, the ListBox will never produce a Scrollbar.  Even if the content stretches far off the window: in that case, it just so happens that the ListBox will also stretch far off the window.  Since the content is not larger than the ListBox itself, no Scroll bars.

Obvious, right?  Not so: I have recently had several frustrating experiences with the Auto settings for Width and Height.  Don’t get me wrong! I use them a lot, and I tell others to do the same because they greatly assist in laying out elements, especially in Grids.  The problem comes when they don’t do what I *think* they are going to do.  In the case of the ListBox, I was operating under the assumption that they would Stretch the ListBox to fit the available space in the container, and that is not how they behave.

The Solution

Realizing my error, I understand now that the scrolling behavior is controlled by the relationship of the size of the Content to the defined (as opposed to visible)size of the ListBox itself.  In my case, I had the ListBox as the last element of a StackPanel.  I needed the ListBox to fill the remaining space, and I needed the Content to scroll vertically. 

The Width of the ListBoxItems was set to the width of the ListBox, minus a little to allow for the Scrollbar, so that wasn’t an issue.  I wanted the Height to fill the remaining space, but I had no way to bind to the measurement and I wanted to avoid a lot of code behind hijinx.  As discussed above, setting it to Auto would actually prevent the desired behavior, so that option was out. 

In this particular case, the application is a fixed size, so I was able to set the size of the ListBox manually to fill the remaining space in the StackPanel, and lo and behold my Scrollbar appeared.  All was well, or so I thought…

The Case of the Changing StackPanel

In testing, we realized that the element above the ListBox (an Image control) should only be visible when it had an Image to show.  So naturally I wired up a ValueConverter and bound the Visibility to a boolean property in the ViewModel reflecting whether or not there was an Image to display.

This worked great for the Image, but an ugly thing occurred with the ListBox.  Since its Height was hardcoded, it shifted up in the StackPanel and no longer stretched to the bottom.  It look pretty strange just hanging there, Scrollbar happily in view, but a bunch of empty space beneath it.

спални комплекти

So just when I thought I had it licked, I’m back in the same boat with the same problem: I need my ListBox to automatically fill all the remaining space in the StackPanel.  Wait, what was that?  I want a child to fill all remaining space?  That sounds familiar…

Enter the DockPanel

Once I got back up from having the ton of bricks fall on my head, I realized that I was using the wrong container!  What I really needed in that scenario was a DockPanel.  I was just using StackPanel out of habit, I had no specific need for it, and the items within it would never be more than the Image and the ListBox.

So using Blend’s trusty “Change Layout Type” feature, I changed my StackPanel to a DockPanel.  I made sure the LastChildFill property was checked and docked the Image control to the Top and set the VerticalAlignment to Top.  I then did the same thing for the ListBox and set its Height property to Auto.  Running the application gave me exactly the result I was after in the first place: an automatically resizing ListBox restrained to it’s Parent container’s remaining available space.

Lesson Learned

So aside from learning a lesson about ListBox and scrolling, I also learned that I need to probably pay a little more attention to the container type I am using.  Grid and StackPanel have always been my defaults since they cover most scenarios, but I also need to remember we have other options.






2 comments    

Another Silverlight Centering Trick



Not too long ago, I posted about how to center a Silverlight Application within an HTML page.  Last weekend, I was working on a Silverlight application and I wanted to stretch the background brush across the entire browser, but retain the content in a fixed space in the middle of the screen.  I could have done this by creating a gradient and then applying it as the background in my HTML/CSS, but I wanted the ease of design and flexibility that I have within Silverlight.  Here is what I did to accomplish the trick.

Sizing the UserControl

To begin with, we have to set the width and height properties of the UserControl so that the UserControl will stretch to fill all available space within the browser.  We’ll start by setting Width and Height of our UserControl to Auto and the LayoutRoot container to a fixed size with a Background color so we can see it along with our Background work.  This way we can tell what effect we are actually having. 

[Note: click on the images for full size]

Content1

So far, it seems like we are centering, so all we should need to do is add a Background to the UserControl, right?  Unfortunately, that will not do what we want.  Adding a Background to the UserControl gives us the exact same results.  Since the screen shot is the same, I won’t repeat it, but if you are following along at home try it and you should get the same result. 

Also, not to be picky, but this is not actually centering: this is setting the HorizontalAlignment and VerticalAlignment properties to their default values of Stretch.  The Grid is actually stretching to fill up the available space, but it is still limited to its fixed size, so it gives the appearance of centering.  Not to worry, it’s a minor technicality.

Setting the Background

Our goal is for the Background, in this case a GradientBrush, to stretch across the entire surface of the browser window.  Above we saw that fixing the LayoutRoot to the size of our desired content won’t allow this to happen.  Instead, we’ll need to set the LayoutRoot Width and Height properties to Auto, and set the Background of the LayoutRoot element to the desired Gradient. 

Running the application at this point will show our Background filling the entire browser space.

StretchedGradient 

And here is the XAML:

StretchedGradientXAML

Centering the Content

Now that we have our Background properly visible across the entire surface of the browser, we need to center the Content inside our LayoutRoot Grid.  This is easily done by adding another Container to the LayoutRoot to act as a wrapper for our fixed size content.  This example shows a Grid, but I initially did it with a Canvas. I’ve added a Black Background color so you can see the content Grid.

ContentOverStretchedGradient

Now set the HorizontalAlignment to Center.  I think the default VerticalAlignment value of Top looks best, but of course you could center it or add some Margin around the content Grid to suit your preference. 

CenteredContentOverStretchedGradient

Now just add your content to the internal Grid, and you will have an automatically sized Silverlight page with centered, fixed size content.

Adding a Clipping Region

One last thing you should be aware of: if you are doing any animations with content off screen, using this method will make them visible outside the bounds of your content.  While this has some interesting potential, it is probably not the behavior you want. 

To correct this, you need to add a Clipping Region to your content Grid.  This will ensure that child elements of that Grid are only visible inside its visual boundaries. Since you can’t do this visually, you’ll need to edit the XAML directly.  The key here is to set the Clipping geometry, in this case a Rectangle, to be the same width and height as the content Grid.

ClippingRegionXAML

Conclusion

It’s important to note that since we are letting Silverlight do all the work, you do NOT want to use the HTML and CSS from the previous Centering post.  Doing so will result in only the width defined in the CSS being displayed.  Instead, just use the default HTML and CSS settings.

I like this approach better since it gives my application an integrated background.  It gives me more creative options and more control.  Let me know how it works for you!






2 comments    

Wrox Silverlight 3 Programmer’s Reference Book Review



In July, I was offered the opportunity to review Wrox Publishing’s newly released “Professional Silverlight 3”, a massive full-color book covering nearly ever aspect of Silverlight 3.  I’ve long been a Wrox fan and as a WPF developer with a web background I have a keen interest in all things Silverlight, so naturally I jumped at the chance to review this new book.  Unfortunately, life did what it always does and prevented me from completing the review until now. 

NOTE: I finalized this article while attending PDC09, where Scott Guthrie announced Silverlight 4.  This release interval for Silverlight has been unprecedented: only three months after the official Silverlight 3 launch!  I am confident though that you will still find this book timely and valuable.

As expected, Wrox does not disappoint.  Authors J. Ambrose Little, Jason Beres, Grant Hinkson, Devin Rader, and Joseph Croney, all from Infragistics, have provided a wonderful edition that should capture the attention of both fledgling and experienced Silverlight developers.  In fact, the introduction and first four chapters should be mandatory reading: they provide the perfect overview to this game changing technology all .Net professionals should understand whether or not their particular interests run towards Silverlight.

If you have never read a full color technical book, you’ll quickly find this is wonderful addition.  The numerous graphics and screen shots jump off the page, making the material easily consumable.  In addition, all code and XAML samples are in full color as well, mimicking the default IntelliSense color scheme.  This is perhaps the best part of having a .NET volume printed in full color because it allows us to read code on the page in the same manner in which we are used to reading it on screen.  I’ve always found it a bit unpalatable to read code on the printed page, but this feature makes the code imminently more readable.

The book itself is very well written and easy to follow.  The style of the authors reveals their mastery of the material without being overbearing.  This is an extremely accessible book to those new to Silverlight but contains plenty of material for the more experienced developer.  While the text is clear and concise, in no way is this a light read.  Silverlight is a very large topic and any book that attempts to do it justice needs to be sized to the task.  That being said, I would not attempt this book, or many like it, cover to cover.  While none of the chapters is superfluous, if you are new to Silverlight or WPF, beyond the requisite 4 chapters mentioned above, I would begin with chapters 7, 8, 12 and 14.  I would tackle the rest of the chapters on an as needed basis.

Another item I really appreciated in this book was the frequent inclusion of Microsoft Expression Blend. Blend is an invaluable tool for developing WPF and Silverlight applications and yet I find it frequently passed over in technical publications.  Going forward, the more complex XAML based applications become, the more imperative it is going to be for developers to learn Blend, so it is nice to see a Silverlight book give Blend the attention it deserves.

On a scale of 1-5, I give this book 4.5 stars.  This book is exactly what it needs to be, a great reference aimed at professional developers.  I know that as I get more serious about my Silverlight development efforts, I will be reaching for this book frequently.






2 comments    

You say Tomato, I say Pomodoro



Earlier this year I was exposed to The Pomodoro Technique.  I tried it out and was impressed by the near instantaneous productivity increases I saw. I even raved about it in a couple of my Rants and Raves posts.  I’ve had several people ask me about it, and I always give it high marks. I’ve been meaning to give it a more thorough treatment, and to share how I use it daily to get awesome results.

What is The Pomodoro Technique

The Pomodoro Technique is a low tech, analog approach to time management.  Created in the early 90’s by Francesco Cirillo, the main premise of Pomodoro is to carve up your work time into discrete, 25 minute blocks.  We’ll call these “poms” for short.  Then, you select a task to work on in that time and do everything within your power to only work on the chosen task during that time.  Set a timer and get to work: when the timer goes off, stop working and take a short break.  Rinse and repeat.  After 4 consecutive Poms, take an extended break.

Interruptions are the enemy of productivity. Sometimes interruptions come from the outside, such as phone calls, text messages, or fellow co-workers.  But many times we invite them, actively seeking them out (even if we don’t realize it): email, the Internet, Twitter, and so on. We believe that we can handle it because we believe we can multi-task.  Truthfully, we are usually deluding ourselves.

I recently visited the Richmond Java User Group to see Andy Hunt, of The Pragmatic Programmer fame give a presentation.  In it, he talked about the dangers of multi-tasking, otherwise known as “context switching”.  The basic gist is that our brains are not designed for multiple avenues of concentration, and a lot of our effort and brain power gets lost when we constantly toggle from one task to the next. 

I have definitely found this to be true: if I can lock myself in to a short block of time and truly devote 100% of my concentration and effort to my task in that time, I can accomplish a great deal.  If you are looking for a way to increase productivity without increasing the amount of time you spend in front of your computer, then Pomodoro may be the solution.

How I Pomodoro

In this section, I’m going to outline how I implement Pomodoro.  I believe that, like most things, you have to adapt Pomodoro to what works best for you, so in no way am I suggesting this is the best way.  Don’t feel you must rigidly adhere to these standards: I frequently break my own rules, but I also acknowledge that I am most productive when I stick to my guns.

Make an empty schedule

At the beginning of each day I write an empty schedule on paper.  Yes, write and paper are the key words in that sentence.  I find that the physical act makes the whole process more concrete.  I map out a schedule for each time block during the day, scheduling lunch and breaks.  A typical office day for me is 9-6, so it breaks down something like this:

< 10 Free Time
10:00-10:25  
10:30-10:55  
11:00-11:25  
11:30-11:55  
12:00-1:00 Lunch
1:00-1:25  
1:30-1:55  
2:00-2:25  
2:30-2:55  
3:00-3:30 Free Time
3:30-3:55  
4:00-4:25  
4:30-4:55  
5 > Free Time

As you see, I end up with 5.5 hours of structured work time a day.  This doesn’t sound like much, but I can get more done in those unfettered 5.5 hours than I used to get in 8-9 hours of multi-tasking. The Free Time entries are essential.  These are the times that I check email, interact with coworkers, read articles, etc.  I call it Free Time, but it might better be described as unstructured time.

Make a to do list

On the right side of the paper I make a to do list.  This is a granular list of tasks, so I don’t write things like “build application”.  Instead, it would more like “Develop view model for page X” or “test ListBox control Y”.  Once you get going, you will find that the task list usually begins with incomplete items from the previous day.  Just remember this is a living list: I am constantly adding items to my to do list throughout the day.  Bugs I find that need to be fixed, new code that needs to be written, new problems that arrive, etc.

Assign an item to the current pom

At the beginning of each pom, examine your list and assign an item to that work unit.  Many items take multiple poms, but when an item is complete mark it off the to do list.  If you finish an item and have time remaining on a pom, then quickly scan the list and assign another item to the remaining time.  One theory says you can always spend more time on the initial task, but frankly I find that wasteful: I’d rather move on to the next item on the list.

Obviously, this requires some prioritization.  I tried to place the items on my to do list in order of priority, but it took too much work, and since the list changes during the day I needed it to be more flexible.  When I create the list now, it is more of a brainstorming session, and on any given pom I am just as likely to work on item #7 as I am to work on item #1.

Use a Timer

The whole reason this is called Pomodoro is because the original timer used was a kitchen timer in the shape of a tomato.  The creator was Italian, and pomodoro is Italian for tomato, hence the name.  In the interests of being a purist, I actually bought a tomato shaped kitchen timer online.  Unfortunately, it made a “tick-tick” noise which turned out to be extremely distracting and thus counter productive.  One of these days I’ll pick up a digital timer, but for the time being I have been using http://e.ggtimer.com/. It works well, makes an audible tone when the time is up, and pops up an alert message interrupting what I am doing. 

Walk Away

Now for the hardest part of Pomodoro: when the timer goes off STOP WORKING AND WALK AWAY.  Yes, get up from your desk: no email, Twitter, or working “just another few minutes.”  Following the schedule and taking numerous mini-breaks are integral parts of the technique.

Stick With It

I use a notepad on a clipboard and each day I flip the page over and start fresh. As I mentioned above, I can get a head start on the day’s to do list by reviewing the previous day’s log.  And log is a great term for it, because what I end up with is a historical log of my activities.  An unexpected benefit of this is that for staff meetings and stand ups, I just grab my clipboard and my status report is ready to go.  It’s pretty impressive when your boss asks you what you’ve done the last few days and you immediately run down a list of items you’ve accomplished or worked on.  Also, for billable situations you can easily figure out how many hours you’ve put in on certain projects.

The Importance of Breaks

Breaks are essential to the success of the system.  While it may not count as exercise, getting out of your chair is physically good for you.  Take a little walk around the office, go to the bathroom, get some water, check in with the boss, whatever: just get away from your work.  The typical break is only 5 minutes, but you’ll be surprised how long it can feel.  When I am deep into a problem, I find the 5 minutes to be invigorating. I can’t wait for my next pom, so when the break is over I’m eager to work, itching to get back to my computer.  When I am stuck on a problem, the few minutes gives me a chance to step away from the issue and reboot a little.  And sometimes the solution to the problem just presents itself during my break.

The longer breaks are important too, because you still need time to do other things.  I spend about half the time doing nothing (hey, it’s a break!) and half the time checking email, making phone calls, reading articles, etc.  Also, since my coworkers know my schedule, it is important that they know when I will be available, which helps to keep them from interrupting me too often.

Respect the Tomato

The most challenging aspect of implementing Pomodoro is not following a schedule or tracking blocks of time: it is getting outside forces to “respect the tomato.”  In other words, how the heck do you get friends, family, coworkers, or especially the boss, to work with you in this effort?  Honestly, if you can find a one-size fits all solution I expect to see you on the rubber chicken circuit and late night infomercials in the very near future.

I will say that overall I have been able to successfully engender by-in with my coworkers and management.  It did take a little effort though, and some training, both for them and for me.  The first thing I did was introduce the concept of Pomodoro at a staff meeting.  I explained that I was going to be experimenting with a new productivity technique.  I walked through the method and explained my schedule.  And I specifically asked that they bear with me while I tried this thing out and to do their best to work around my schedule.

I posted my schedule on the door to my office, clearly denoting when I had available time.  I then hung an inbox on my door with a note indicating to put all messages, notes, or request for work in the box, which I would check every half hour on break.  The general rule of thumb is that if the door is open, feel free to come in.  Otherwise leave me a note or come back later.  For the most part, this worked pretty well from the beginning, although I did have trouble with one of my bosses, but I’ll cover how I handled that a little later.

I chose the timing of this intentionally: we had a major project due in a severely truncated timetable.  I had immediate management support because of the urgency of the project.  When I came back to the next staff meeting only 2 days later they were amazed at the progress I was able to make in such a short time span.  I gave all the credit to using Pomodoro, and it was essentially a done deal.

Dealing with Interruptions

It is a fact of life that you will have interruptions.  Pomodoro does not remove your interruptions, but it does require that you learn how to handle them without breaking your pom. 

EMail, IM, and Twitter

In our world of constant connectivity, this is the toughest category of interruptions to handle.  For years, I have worked with my email client open, checking it every few minutes and always immediately handling any email.  Recently, Twitter has been even worse, especially since TweetDeck alerts me every time there is a new tweet for me to read.  In both cases, they are very difficult to ignore and not immediately switch context.

The good news is that these are actually the easiest to control: just turn them off.  [I’ll wait a moment while you pick yourself back up off the floor…]  Yes, I said turn them off: minimizing isn’t good enough, the temptation is just too great.  Oh, and if you are a TweetDeck user, closing the application just minimizes it to the tray, so you will still get the audio notice and a pop up every time a new tweet arrives.  To really turn it off, right click the icon in the tray and select “exit”. 

When I started doing this, I even tweeted what I was doing and that I wouldn’t be on much for while so no one would think something had happened to me.  I allow myself to have them up during lunch and Free Time, but otherwise I shut the clients down.

I haven’t mentioned IM yet, because it’s the one that I haven’t addressed in practice.  I only have a few people I IM with, and in those cases I can usually wait to respond.  I haven’t found it to be an issue yet, but if I do I’ll turn it off too.

The Phone

Fortunately I have access to a receptionist who has standing orders to take messages and interrupt me if it is an emergency.  In our company, I’m the last line of Customer Service, so when my phone rings it is usually a customer with a technical issue no one else can solve.  In those cases, I try to schedule a call back at the end of the pom, but if it really needs attention now, I give it to them.  Business is business and these people pay the bills.

Coworkers and Bosses

You have a lot less control over your coworkers.  Even though you communicate your plan to them and ask for their understanding and assistance, they will still need some “handling” on occasion.  It’s important to remember that you need to be diplomatic: you don’t want to do anything that will cause personnel problems or get yourself labeled as a prima donna.

What works pretty well for me is to give them about 10 seconds.  If I can answer their question or solve their problem immediately, I do.  If not, I let them know how long I have left in my current pom, and then I make them my first priority when I get out.  So far so good.  It also helps that they are reminded of what I am doing at every staff meeting.

Obviously, bosses are a little different because of their authority level.  How different will depend on your boss and your relationship.  In my case, I can typically handle the situation the same way I handle my coworkers, but there are exceptions.  When the boss is insistent, impatient, won’t take a hint, or just downright demanding, I do what I have to do (meaning what he wants me to do), but I make sure I let him know the impact he is having.  In our office the code phrase is “you know you are breaking the rules, right?”  At that point I just remember that the boss is the boss.

The Broken Pomodoro

In each of the cases above there is the potential that an interruption will take precedence over your Pomodoro: this is called a broken pomodoro.  In this case I make sure I note it on my log and the reason.  I then do the best I can to get back on schedule as soon as possible.  If something is going to eat into the next pom, then that issue gets added to the to do list and becomes the action item for the next pom.

Watch Your Productivity Soar

I hope this has intrigued you.  I know that at the time I heard about this I felt like I was completely floundering, adrift rudderless on a sea of work.  Adopting Pomodoro has helped me focus my efforts, increase productivity, and enhance my accountability.  Try it out, I think it could help you too.  As always, I’d love to hear any comments you may have below.






4 comments    

Working with AS400-iSeries-System i Stored Procedures in .NET



At my company our primary database is the IBM System i, formerly known as iSeries, formerly known as AS/400, and still affectionately referred to as simply “the 400” by most people I know.  The 400 is a fantastic database server: fast, reliable, secure, and best of all the darn thing just never breaks.

I spend a great deal of my time writing .NET apps that use the 400 as the backend database.  In the past this has meant a lot of dynamic SQL, which I am extremely comfortable developing.  Unfortunately, this can be pretty brittle, difficult to debug, and performance frequently lags.  I’ve done lots of things to improve performance, like using Parameterized Queries. 

In an effort to wring out the last bit of performance and improve the quality of our code, we have been making an effort to expose our data via Stored Procedures. How to create those procedures on the 400 is a topic I’m not going to delve into, mostly because I haven’t been doing it myself, but also because there are lots of options.  In our case we are writing them in SQL, but they could just as easily execute an RPG program.

Consuming a Stored Procedure

I use the IBM DB2 iSeries .NET Data Provider, V5R4.  I also have SP1 installed (SI35287) which corrects a nasty ObjectDisposedException problem that’s been floating around since .NET 2.0 came out.  The classes are all in the IBM.Data.DB2.iSeries namespace.  Be sure you add a reference to <installed_location>\IBM\Client Access\IBM.Data.DB2.iSeries.dll.

The process is actually pretty simple.  Create a Connection, create a Command, add some Parameters, Execute the Command, Process the results.

var connectionstring = String.Format(ConnectionStringFormat,
    IpAddress, User, Password);
_conn = new iDB2Connection(connectionstring);
_conn.Open();

if (_conn != null &amp;&amp; _conn.State == ConnectionState.Open;)
{
    _cmd = _conn.CreateCommand();
    _cmd.CommandTimeout = 0;
    _cmd.CommandType = CommandType.StoredProcedure;
    _cmd.CommandText = "LIBNAME.PROCNAME";
   
    // Add Parameters
    _cmd.Parameters.Add(name, type, size);
    _cmd.Parameters[name].Value = value;
    _cmd.Parameters[name].Direction = direction;

    _cmd.Prepare();

    // For individual value returns (direction intput/output or output
    _cmd.ExecuteNonQuery();
    var dict = new Dictionary<string  , object>();
    foreach (iDB2Parameter parm in _cmd.Parameters)
    {
        if (parm.Direction == ParameterDirection.Input)
            continue;
   
        dict.Add(parm.ParameterName, parm.Value);
    }

    // For collection value returns
    var adapter = new iDB2DataAdapter(_cmd);
    var ds = new DataSet();
    adapter.Fill(ds);

    _cmd.Dispose();
    _conn.Close();
    _conn.Dispose();
}

Testing Stored Procedures

I quickly found when we started down this path that I was missing a way to easily test Stored Procedures, and since we were planning on writing lots of them this just would not do.

Executing ad-hoc SQL against the 400 can be a draining experience.  There is a command line tool called Interactive SQL (STRSQL in 400 speak) and a Java tool embedded deep in Operations Navigator which I wrote an article about this back in my ITJungle days. Disappointed in the available tools, my first serious .NET application, written in .NET 1.1 back in 2003, was an ad-hoc SQL tool for the iSeries called iHoc Query.  We still use it in house today but I quit distributing it a while back for various reasons.  Mostly it needs an overhaul and I just don’t have the time to spare.

Unfortunately, I never could get ad-hoc execution of a Stored Procedure to work against the 400, which meant I needed to find a tool. There is a utility out there called SQLThing, which is where we started.  It seemed to work fine, with a few glitches here and there, but I needed to get a good handle on how to code for this data access approach.  It also did not have some of the features I wanted, so naturally I hacked together something myself.

It doesn’t look pretty (I wrote it in WinForms), but it works very well and has these goodies:

  • Retains Data Connection information
  • Supports Input, Output, and Input/Output parameter directions
  • Supports returning multiple result sets, displaying each in a tab
  • Allows saving and loading of Stored Procedures, so you can reuse them without reentering the parameters each time

Here is a screen shot:

TestStoredProcScreenShot

You can download this tool here.

Some helpful code

I wrote a wrapper class called SprocEngine to isolate the ugly Managed Provider code and make it more palatable to use them in my programs. Here is the code for the SprocEngine class:

using System;
using System.Collections.Generic;
using System.Data;
using IBM.Data.DB2.iSeries;

namespace SprocEngine
{
    public class As400SprocEngine : PropertyChangedBase, IDisposable
    {
        public enum As400DbType
        {
            Numeric,
            Character
        }

        public As400SprocEngine()
        {
        }

        public As400SprocEngine(string ip, string user, string password)
            : this()
        {
            IpAddress = ip;
            User = user;
            Password = password;

            ConnectToDatabase();
        }

        private Dictionary<as400dbtype idb2dbtype ,> _typeConverter =
            new Dictionary<as400dbtype idb2dbtype ,>()
                {
                    {As400DbType.Numeric, iDB2DbType.iDB2Numeric},
                    {As400DbType.Character, iDB2DbType.iDB2Char}
                };
       
        private iDB2Connection _conn = null;
        private iDB2Command _cmd = null;

        private string _ip;
        private string _user;
        private string _pass;

        private const string ConnectionStringFormat = "DataSource={0};UserID={1};Password={2};";

        public string QualifiedProcedureName
        {
            get
            {
                return _cmd.CommandText;
            }
            set
            {
                _cmd.CommandText = value;
                FirePropertyChangedEvent("QualifiedProcedureName");
            }
        }

        public bool IsConnected
        {
            get { return _conn != null &amp;&amp; _conn.State == ConnectionState.Open; }
        }

        public string IpAddress
        {
            get { return _ip; }
            set { _ip = value; FirePropertyChangedEvent("IpAddress"); }
        }

        public string User
        {
            get { return _user; }
            set { _user = value; FirePropertyChangedEvent("User"); }
        }

        public string Password
        {
            get { return _pass; }
            set { _pass = value; FirePropertyChangedEvent("Password"); }
        }

        public List<string> ParmTypes
        {
            get
            {
                var list = new List<string>();
                list.AddRange(Enum.GetNames(typeof(iDB2DbType)));
                return list;
            }
        }
        public void ConnectToDatabase()
        {
            if (IpAddress.Equals("") || User.Equals("") || Password.Equals(""))
                throw new ArgumentException("Required field is blank.");

            var connectionstring = String.Format(ConnectionStringFormat,
                IpAddress, User, Password);
            _conn = new iDB2Connection(connectionstring);
            _conn.Open();
           
            FirePropertyChangedEvent("IsConnected");

            if (IsConnected)
            {
                _cmd = _conn.CreateCommand();
                _cmd.CommandTimeout = 0;
                _cmd.CommandType = CommandType.StoredProcedure;
            }
        }

        public void ClearParameters()
        {
            _cmd.Parameters.Clear();
        }

        public void AddParameter(string name, As400DbType type, object value)
        {
            AddParameterIDB2(name, _typeConverter[type], null, value, ParameterDirection.Input);
        }

        public void AddParameterIDB2(string name, iDB2DbType type, object value)
        {
            AddParameterIDB2(name, type, null, value, ParameterDirection.Input);
        }

        public void AddParameter(string name, As400DbType type, int? size, object value, ParameterDirection direction)
        {
            AddParameterIDB2(name, _typeConverter[type], size, value, direction);
        }

        public void AddParameterIDB2(string name, iDB2DbType type, int? size, object value, ParameterDirection direction)
        {
            if (size.HasValue)
            {
                _cmd.Parameters.Add(name, type, size.Value);
            }
            else
            {
                _cmd.Parameters.Add(name, type);
            }
            _cmd.Parameters[name].Value = value;
            _cmd.Parameters[name].Direction = direction;
        }

        public Dictionary<string  , object> ExecuteSproc()
        {
            try
            {
                _cmd.Prepare();
                var q = _cmd.ExecuteNonQuery();
               
                var dict = new Dictionary<string  , object>();
                foreach (iDB2Parameter parm in _cmd.Parameters)
                {
                    if (parm.Direction == ParameterDirection.Input)
                        continue;

                    dict.Add(parm.ParameterName, parm.Value);
                }

                if (dict.Count <= 0)
                    dict.Add("AffectedRowCount", q);

                return dict;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public DataSet ExecuteSprocCollection()
        {
            try
            {
                _cmd.Prepare();

                var adapter = new iDB2DataAdapter(_cmd);
                var ds = new DataSet();
                adapter.Fill(ds);

                return ds;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #region IDisposable Members
        public void Dispose()
        {
            _cmd.Dispose();
            _conn.Close();
            _conn.Dispose();
        }
        #endregion
    }
}

It inherits a simple class that implements INotifyPropertyChanged called PropertyChangedBase.  If you aren’t planning on binding this to WPF you can remove it, but if you want it here is the code for it:

using System.ComponentModel;
namespace SprocEngine
{
    public class PropertyChangedBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        internal void FirePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Using SprocEngine

Here are a couple of samples showing how to use SprocEngine.

Processing a single returned value:

using (var sproc = new As400SprocEngine(ipaddress, user, password))
{
    sproc.QualifiedProcedureName = ProcName;
    sproc.AddParameter("libname", As400SprocEngine.As400DbType.Character, 10,
        null, ParameterDirection.InputOutput);

    // Execute SPROC
    var retVals = sproc.ExecuteSproc();
    if (retVals == null || retVals.Count <= 0 )
        return null;

    string DataLibrary = retVals["libname"].ToString().Trim();
}

Processing multiple result sets:

using (var sproc = new As400SprocEngine(ipaddress, user, password))
{
    sproc.QualifiedProcedureName = ProcNames[searchType];
    sproc.AddParameter("@prefix", As400SprocEngine.As400DbType.Character, localityPrefix);
    sproc.AddParameter("@record", As400SprocEngine.As400DbType.Numeric, record);
    var header = new ParcelHeaderCollection();
    try
    {
        var ds = sproc.ExecuteSprocCollection();

        foreach (DataRow dr in ds.Tables[0].Rows)
        {
            var parcel = new ParcelHeader();
            parcel.Record = Convert.ToInt32(dr["mrecno"].ToString());
            parcel.Card = Convert.ToInt32(dr["mdwell"].ToString());
            parcel.OwnerName = dr["mlnam"].ToString().Trim();
            parcel.Map = dr["mmap"].ToString().Trim();
            parcel.StreetNumber = Convert.ToInt32(dr["house"].ToString());
            parcel.StreetName = dr["mstrt"].ToString().Trim();
            parcel.OccupancyCode = Convert.ToInt32(dr["moccup"].ToString());
            parcel.SalesPrice = Convert.ToInt32(dr["msellp"].ToString());
            parcel.YearSold = Convert.ToInt32(dr["myrsld"].ToString());
            parcel.MonthSold = Convert.ToInt32(dr["mmosld"].ToString());

            header.Add(parcel);
        }
    }
    // – you should use a more descriptive Exception type here
    catch (Exception ex)
    {
        throw ex;
    }

Final Notes

As always, Your Mileage May Vary.  This is written specifically for the iSeries, but you should be able adapt it to any other provider.  I have not worked much with Stored Procedures on any other system, so I’m not sure how it may need to be varied.

I hope you find this helpful. If you work in .NET with the iSeries, please share any tips or tricks you may have in the comments below.






1 comment    

IBM iSeries .NET Managed Provider ObjectDisposedException problem SOLVED



I can’t believe how remiss I’ve been: this has been a big problem for me since early 2007, and we finally got it solved a couple of months ago, and I can’t believe I never blogged about it!

Two things to fix this:

1) Install iSeries Client Access V5R4.  Be sure to install the .NET Managed Provider if it is not already installed.  A full install will NOT do this, you must check the box to include it.

2) Install SI35287 on any PC that is connecting to the 400.  This is essentially V5R4 SP1 and will finally solve this little issue.

I do not know what the status of this bug on V6R1 is, but I will say that once I installed the SP this problem finally stopped.






1 comment    

Using Custom Validation Rules in WPF



Anyone who has ever developed data driven applications of any sort has had to deal with validation.  Let’s face it, users make mistakes, even if they are developers!  Validation can be a real pain to implement, and contrary to all our HelloWorld style applications, MessageBox.Show is not the best way to inform our users that we have an issue with their keyboarding skills.

I remember being ecstatic when I learned about the WinForms ErrorProvider. This handy little approach finally gave us a nice way to alert the user of the problem without being too heavy handed in the UI.  Where it failed, though, was in customization.  First of all, it took a lot of code behind to manage the ErrorProvider: checking field values against business rules, updating the provider’s list of errors, or clearing it out when the status was OK ended up being a lot of code, especially when there were a lot of data fields. And then, beyond deciding whether or not the Error symbol would blink (I always hated the blinking exclamation point), there wasn’t a lot of pizzazz we could add.  Of course, that’s all changed with WPF.

Getting Started

Before I go any further, you can find most of the basics in these articles:

I recommend reading these articles. The CodeProject article even links to a Project with a WPF version of ErrorProvider.  I will be using a couple code samples from these articles.

I’m not going to harp too much on the details presented in these other articles.  Instead, I’m going to layout what I did to get this working and hopefully give you enough information to get started.

Using IDataErrorInfo

When you first start looking into validating data in WPF you will no doubt run into IDataErrorInfo.  This seems to be the jumping off point.  Implementing the interface is pretty straightforward.  Here is a typical example:

using System.ComponentModel;
namespace ValidationRulesPlay.ViewModels
{
    public class DataErrorInfoSample : ViewModelBase, IDataErrorInfo
    {
        private int _int1;
        private int _int2;

        public int Int1
        {
            get { return _int1; }
            set { _int1 = value; FirePropertyChangedEvent("Int1"); }
        }

        public int Int2
        {
            get { return _int2; }
            set { _int2 = value; FirePropertyChangedEvent("Int2"); }
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get { return null; }
        }

        public string this[string columnName]
        {
            get
            {
                string result = null;

                switch (columnName)
                {
                    case "Int1":
                        if (Int1 > 9999999)
                            result = "Int1 number cannot be greater than 9999999.";
                        break;

                    case "Int2":
                        if (Int2 > 99)
                            result = "Int2 number cannot be greater than 99.";
                        break;

                    default:
                        break;
                }

                return result;
            }
        }

        #endregion
    }

}

[NOTE: the class above inherits from ViewModelBase, a class I use to implement INotifyPropertyChanged.]

And then you add a piece to your Binding syntax in XAML to activate it:

<TextBox
    x:Name="Int1TextBox"
    ValidatesOnDataError="True"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0"/>

This works and will by default wrap the offending textbox with a red border.  It’s not a bad experience for simple examples, but as I see it there are a couple of drawbacks.

First, IDataErrorInfo validated the data after the property has been set. So if I type in a value that is out of range, the target property is still invalidly set with the bad value.  This means that I have to do extra work to correct the error.  In other words, this approach lets me put bad data in that I must then take out.  Wouldn’t it be better if it just didn’t go in in the first place?  Second, this interface uses a custom indexer, which frankly just feels like magic.  It’s a personal preference, and a fear of the unknown, but I just don’t like it.

My other real issue with it is that it ends up being a lot of repetitive code.  Look at the example above and you’ll see two integer values that are both tested to be within a certain range.  While the ranges are different, the action is the same.  Of course, you could create a method, or better yet an Extension Method, to handle this situation.  Maybe it’s because of the hard-coded nature of the validation messages, but it still doesn’t feel right to me.

Creating Custom Validation Rules

Wouldn’t it be better to have a reusable piece of code to handle these similar situations?  One with no real hard coding?  In .NET 3.5 SP1 we have just such an animal, the Custom Validation Rule.

ValidationRule is an abstract class that we can inherit from to create a custom validation rule class of our own.  I could certainly create a custom rule class for each property in my DAL, and perhaps sometimes a custom class for a particular field would make sense, but many times all I need is a generalized validation rule. 

I mentioned previously that IDataErrorInfo checks the validation after the property has been updated.  This means if we attempt to insert a non-integer field with a non-integer value, the validity checking never occurs: instead, WPF swallows the error.  Using a validation rule causes the validation to occur before the property is updated, so we can easily prevent such an occurrence and report it to the user.  It does still allow the bad data into the TextBox, but that’s not necessarily a bad thing.

For this example, we need a validation rule that will report when the user attempts to insert a non-integer character.  We also want to be able to specify a range of acceptable values.  Additionally, we are going to add a few fields to help customize the messaging experience.  Here is the complete class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace CustomValidationRules
{
    public class IntegerValidationRule : ValidationRule
    {
        private int _min = int.MinValue;
        private int _max = int.MaxValue;
        private string _fieldName = "Field";
        private string _customMessage = String.Empty;

        public int Min
        {
            get { return _min; }
            set { _min = value; }
        }

        public int Max
        {
            get { return _max; }
            set { _max = value; }
        }

        public string FieldName
        {
            get { return _fieldName; }
            set { _fieldName = value; }
        }

        public string CustomMessage
        {
            get { return _customMessage; }
            set { _customMessage = value; }
        }

        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            int num = 0;

            if (!int.TryParse(value.ToString(), out num))
                return new ValidationResult(false, String.Format("{0} must contain an integer value.", FieldName));

            if (num < Min || num > Max)
            {
                if (!String.IsNullOrEmpty(CustomMessage))
                    return new ValidationResult(false, CustomMessage);

                return new ValidationResult(false, String.Format("{0} must be between {1} and {2}.",
                                           FieldName, Min, Max));
            }

            return new ValidationResult(true, null);
        }
    }
}

The magic all happens in the overridden Validate method.  There are lots of additional ways you could customize this, but there should be enough in this example to get you started writing your own custom validation rules.

I put generalized rules like this in a separate project and namespace so I can reuse them in many projects.  For rules that are specific to a particular DAL or Application you can just as easily put them in those namespaces.

Consuming Custom Validation Rules

Now that we have an IntegerValidationRule class, let’s add it to our XAML so we can put it into action.  Unfortunately, there is no facility in Blend for doing so, so we’ll have to edit the XAML directly.  The first thing to do is add a namespace reference to the XAML pointing to the location of the custom validation rules.

We are going to add the validation to a TextBox.  Normally, I would use the shortened syntax to specify a binding, but in this case I’m going to use long hand because there is a lot of detail to add. Here is the completed sample:

<TextBox
    x:Name="Int1TextBox"
    Validation.ErrorTemplate="{StaticResource validationTemplate}"
    Style="{StaticResource textBoxInError}"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0">

    <Binding
        Path="Int1"
        UpdateSourceTrigger="PropertyChanged"
        Mode="TwoWay">

        <Binding.ValidationRules>
            <ValidationRules:IntegerValidationRule
                Min="1"
                Max="9999999"
                FieldName="Int1" />

        </Binding.ValidationRules>
    </Binding>
</TextBox>

There are a number of things going on here.  First, notice we added the Validation.ErrorTemplate property.  This specifies a template to use when the error condition exists.  I’ll cover that shortly along with the Style property. Both of these are used to customize the display of the error condition.

Next, in the TextBox.Text property we’ve added a long hand version of Binding.  I use UpdateSourceTrigger to cue the validation check whenever the property changes.  If I use the default value the change will only occur once the element loses focus, and I prefer a more real time effect.

Within the Binding property, we’ve added a subproperty section called Binding.ValidationRules.  Inside this property we add references to the rules we wish to implement. This will create an instance of the custom validation rule with the specified property values.  Since I set default values in the class, I can leave off any property and the validation will still function. As you can see in the example above, this is where I can set the property values for this particular instance of my IntegerValidationRule.  In this case we are allowing the integer range to be between 1 and 9999999, and specifying the name we want the message to associate with any error.

That’s all we need to do to consume the custom rule.  Now we’ll take a look at the ControlTemplate and Style to see how we can control the display.

Styling a Custom Validation

I took both the ControlTemplate and the Style from the MSDN article How To Implement Binding Validation.  Here are the original values:

<ControlTemplate
    x:Key="validationTemplate">

    <DockPanel>
        <TextBlock
            Foreground="Red"
            FontSize="20">
<Run
                Text="!" />

        </TextBlock>
        <AdornedElementPlaceholder />
    </DockPanel>
</ControlTemplate>
<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">

    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">

            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />

        </Trigger>
    </Style.Triggers>
</Style>

Insert these into the Windows.Resources section of the XAML.  If you’ve been coding along, you can run the sample now and it will work.  The only thing you really get is a red exclamation point to the left of the TextBox and a Tooltip if you hover over it.  This is OK, but I didn’t really feel it was enough to grab the user’s attention, so I’m going to change it just a little.

Before I do that, though, take a look at the ControlTemplate: there is something interesting here called an AdornedElementPlaceholder.  This nifty little element allows us to wrap the original ControlTemplate with additional elements.  In this sample, we are placing a TextBlock with the exclamation point just in front of our original control.  Feel free to experiment with this and add some code of your own around the offending TextBox.

Back to the Style: I want the TextBox to pop a little more when an error occurs.  Looking at the original Style code, we see that we have a Trigger that changes the Style when Validation.HasError is True.  In the default example, we are providing a ToolTip.  Here I’ve added some code to make the Background color of the TextBox Red and the Foreground White whenever an error occurs:

<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">

    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">

            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />

            <Setter
                Property="Background"
                Value="Red" />

            <Setter
                Property="Foreground"
                Value="White" />

        </Trigger>
    </Style.Triggers>
</Style>

Now the user should definitely notice when an error occurs!  Again, I recommend you experiment and see what else you can do to spice up your validation errors.

And another thing…

Another thing that would be very useful is to be able to bind to the state of validation.  In other words, I have a button that I only want enabled if there are no Validation errors.

In WinForms, I used to process through all the Controls and check to see if any of them had a non-blank entry in the errorProvider.  In WPF, what I would really like is to be able to bind IsEnabled to a “HasErrors” property somewhere.  Unfortunately I couldn’t make something so simple work.  I did, however, come up with a solution using a RoutedCommand.  Commands and commanding are a topic for another time, but here is the short version of what I did. 

I created a static RoutedCommand object in my Window class:

public static RoutedCommand ValidateEntryCommand = new RoutedCommand();

I then added the supporting CommandBinding to my XAML file, including CanExecute:

<Window.CommandBindings>
    <CommandBinding
        Command="{x:Static ValidationRulesPlay:Window1.ValidateEntryCommand}"
        Executed="CommandBinding_Executed"
        CanExecute="CommandBinding_CanExecute" />

</Window.CommandBindings>

In the CanExecute event handler, I call Validation.HasError(DependencyObject) for each TextBox I want to validate against and set the event args CanExecute property based on the results.

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    bool hasError = Validation.GetHasError(Int1TextBox) ||
                    Validation.GetHasError(Int2TextBox);

    e.CanExecute = !hasError;
}

Lastly, I set the Command property of the SaveButton to my Command name.

<Button
    x:Name="SaveButton"
    Content="Button"
    Command="{x:Static local:Window1.ValidateEntryCommand}"/>

Now, the button will be disabled if either of the TextBoxes have an error condition.

Conclusion

I hope this distills some of the basics for you and gets you on your way to writing your own custom validation rules.  As always, comments are encouraged.

You can download the full solution for this project, which includes some supporting code.






1 comment    

Next Page »