Bart's Smarts

Smart .NET, Silverlight, and SharePoint Development.

Displaying HTML Content in Silverlight

Posted by sapientcoder on September 8, 2009

One of the challenges I ran across recently in Silverlight 3 was the need to display HTML content within a datagrid cell.

I did a Google search similar to ”silverlight html content” and quickly discovered that there aren’t any good solutions out there right now. Basically, the solutions I found were the following:

  1. Creative hacks using the IFRAME tag and clever positioning (this includes the C1HtmlHost control by ComponentOne, which basically uses this technique).
  2. Use the HtmlTextBlock control that’s been blogged about (supports only very basic HTML tags; no CSS).
  3. Create your own custom control.

None of these solutions worked well for me because the HTML I needed to display was slightly too complicated for the HtmlTextBlock control to handle, and I had problems getting the C1HtmlHost control by ComponentOne to work within a datagrid cell (the browser would lock up, for example, whenever the datagrid was scrolled or data was refreshed).

But, thankfully, ComponentOne has another control called C1RichTextBox that renders HTML nicely without using any IFRAME hacks. I wouldn’t necessarily recommend it if you need to host an external HTML page within your application (because, for example, it doesn’t supported externally linked stylesheets), but it works well for basic scenarios that require more HTML support than what HtmlTextBlock provides. It also works well within a datagrid cell, which was essential for my scenario.

My final XAML code for hosting HTML within a datagrid cell looks like this:

<c1dg:DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Grid>
            <c1rtb:C1RichTextBox Background="Transparent"
                    BorderThickness="0"
                    IsReadOnly="True"
                    VerticalScrollBarVisibility="Hidden"
                    Html="{Binding HtmlBackingField}" />
            <Rectangle Fill="Transparent" />
        </Grid>
    </DataTemplate>
</c1dg:DataGridTemplateColumn.CellTemplate>

The reason for the ‘Rectangle’ tag is that I needed to create a transparent panel (“glass pane”) in front of the C1RichTextBox control. This is both an easy way to shield the textbox from user interaction (making it truly read-only) and also allows the datagrid to behave normally when the cell is clicked. Without the transparent panel, the textbox intercepts the click event and prevents the datagrid from highlighting the row as it should.

And remember, this approach is geared towards creating an ”HTML viewer,” not a “web browser.” If you need a true “web browser” control (i.e. something that acts like an IFRAME and can fully host and support external web pages where many files, such as CSS files, have to be linked together to render the HTML), you’ll need to keep searching for another solution. This approach is what I’d call a middle-of-the-road solution, but in many cases, it’ll get the job done.

Posted in Silverlight 3, Uncategorized | 1 Comment »

Silverlight TextBox: Select text when focused with tab Key

Posted by sapientcoder on July 15, 2009

A common request I receive from Silverlight application users is for all the text within a TextBox to be selected when the control gains focus because of the tab key and not because of clicking on it with the mouse. In other words, if a user clicks the control with the mouse, they want the insertion cursor to appear where it normally would, but if they’re quickly tabbing through fields entering data, they want whatever value is in the field to be selected so it can be easily cleared out.

This can actually be accomplished pretty easily using attached properties in Silverlight and will yield XAML such as this:


<df:DataForm Name="myDataForm"
                   AutoEdit="True"
                   AutoGenerateFields="False"
                   CommandButtonsVisibility="None"
                   ui:TextBoxSelection.SelectAllOnTabFocus="True">

    <df:DataForm.EditTemplate>
        <DataTemplate>
            <StackPanel>
                <df:DataField>
                    <TextBox Text="{Binding Field1, Mode=TwoWay}" />
                </df:DataField>

                <df:DataField>
                    <TextBox Text="{Binding Field2, Mode=TwoWay}" />
                </df:DataField>
            </StackPanel>
        </DataTemplate>
    </df:DataForm.EditTemplate>

</df:DataForm>

Note the use of the TextBoxSelection.SelectAllOnTabFocus property. In this case, I’m attaching it to a DataForm, but it could be attached to any DependencyObject of type FrameworkElement. In this example, the TextBoxes bound to “Field1″ and “Field2″ will select all of their text when the user causes them to get focus by hitting the tab key. If I had wanted to have only the second TextBox exhibit this behavior, I could remove the line that sets the SelectAllOnTabFocus property from the DataForm tag and place it inside of the second TextBox tag.

Here’s the code for the TextBoxSelection class:


using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace MySilverlightApp.UI
{
    public class TextBoxSelection
    {
        public static readonly DependencyProperty SelectAllOnTabFocusProperty =
            DependencyProperty.RegisterAttached(
                "SelectAllOnTabFocus",
                typeof(bool),
                typeof(TextBoxSelection),
                new PropertyMetadata(OnSelectAllOnTabFocusPropertyChanged)
            );

        public static bool GetSelectAllOnTabFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(SelectAllOnTabFocusProperty);
        }

        public static void SetSelectAllOnTabFocus(DependencyObject obj, bool selectAllOnTabFocus)
        {
            obj.SetValue(SelectAllOnTabFocusProperty, selectAllOnTabFocus);
        }

        private static void OnSelectAllOnTabFocusPropertyChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement fe = (FrameworkElement)obj;

            if ((bool)e.NewValue)
            {
                fe.KeyUp += control_KeyUp;
            }
            else
            {
                fe.KeyUp -= control_KeyUp;
            }
        }

        private static void control_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Tab)
            {
                object element = FocusManager.GetFocusedElement();
                if (element != null && element is TextBox)
                {
                    ((TextBox)element).SelectAll();
                }
            }
        }
    }
}

That’s it. Now you’ve got a reusable way to apply this behavior anywhere where TextBoxes are used in your application.

UPDATE: I put together a small sample application that you can download to see a working example of this solution. The sample application was created in Visual Studio 2008 and requires the Silverlight 3 SDK.

Posted in Silverlight 3 | Tagged: , | 3 Comments »

Calculating relative (offset) dates for DOS commands and scheduled tasks

Posted by sapientcoder on March 5, 2009

I’ve had several cases come up over the years where I’ve needed to pass a date to a program that’s being run from the Windows command line (DOS prompt) or as a Windows Scheduled Task. Since different programs require dates for different meanings and in different formats, I’ve always thought it would be nice if there were a “standardized” way to calculate whatever date needs to be passed to the program and then format it in a way the program expects.

Up until now, the most common way I’ve seen documented on the web to do this is to parse the built-in %Date% variable (or the result of the ‘date’ command) into multiple variables that can then be strung together to generate a date in the desired format.

In my opinion, this approach has a couple of shortcomings:

  1. When a variable is set using the SET command in a DOS session, the variable retains its value until the DOS session is terminated. In other words, you must close and re-open the command prompt before its value can be reset. This is inconvenient if you want to re-run a program several times in the same session and pass a different date each time.
  2. Parsing the date returned by the ‘date’ command or %Date% variable is machine-specific and can break if the date settings on the machine are changed.

To avoid these issues, I used a different approach when I recently had the need to do this.

I wrote a small batch file that does two things. First, it calls a small utility program I wrote called getrelativedate to get a date relative to the current date and format it. Second, it calls the application that needs the date and passes it the output from getrelativedate.

Here’s a sample batch file that uses this approach to pass a date 5 days in the past to an application:

@echo off

FOR /F %%a IN ('path\to\getrelativedate -5 yyyyMMdd') DO (
    if errorlevel 0 (
        path\to\prog\expecting\date -date %%a -other params
    )
)

And here’s the syntax for the getrelativedate utility:

getrelativedate <interval> <format>

The first parameter, interval, designates the date offset and is formatted in a way that’s accepted by .NET’s TimeSpan.Parse() method. See that method’s documentation to see how interval should be formatted. It’s pretty easy. Also, intervals can be positive (in the future), negative (in the past), or 0 (if you just want to format the current date and don’t need an offset).

The second parameter, format, is a standard .NET date format string (like what you would pass to the DateTime.ToString() method).

Pretty simple and easy but also quite flexible and powerful, in my opinion.

If you’re interested, download getrelativedate.zip.

The zip file includes the utility program, a sample batch file, and a readme file with documentation.

Posted in .NET | Tagged: | Leave a Comment »

Customize the Text Editors in SharePoint 2007

Posted by sapientcoder on February 3, 2009

If you’re like me, you probably aren’t a huge fan of the text editors that ship with SharePoint 2007. Aside from known browser compatibility issues, they aren’t all that great from a usability standpoint either. I figured this out fairly early on when I was training our users on how to author content in SharePoint.

Take, for example, the “insert hyperlink” button that appears in the editor toolbar. I noticed that in some places it button would bring up a nice “browse” dialog that lets the user easily select a link target. In other places, I’d get a stripped down dialog box with two fields: Text to Display and Address. In this second case, I’d have the daunting task of trying to explain to non-technical users how to copy-and-paste URLs to documents and other targets into this dialog. Not great from a usability design perspective.

If you’re thinking of trying to customize the built-in editors to make them more usable or cross-browser, I’d recommend against it (at least until the process hopefully becomes easier). I started down this path and was soon dismayed to find that fixing the text editors involves modifying or overriding core JavaScript files in the 12 hive.

Furthermore, customizing the built-in text editors involves customizing multiple controls. On the one hand, SharePoint uses an HTML editor control for Content Editor Web Parts and for Web Content Management (authoring/editing pages). On the other hand, it uses a Rich Text editor control for rich text (and enhanced rich text) fields in lists.

(Note: If you want to specifically customize the HTML text editor in SharePoint without worrying about the Rich Text editor, you can modify the RTE2ToolbarExtension.xml file that’s stored in the Editing Menu folder of the Master Page gallery. This allows at least some level of customization without modifying core JS files.)

So instead of trying to customize the editors that ship with SharePoint, I recommend replacing them. Specifcally, I recommend using the free RadEditor control from Telerik (a company who works closely with Microsoft). It’s an ASP.NET control that renders a textarea with an editing toolbar, similar to how the built-in editors are rendered. The difference, though, is that it’s cross-browser, more usable, and easier to customize. Also, it gives you the option to use the nice “browse” dialogs (called Asset Picker Dialogs) built into SharePoint or use the cross-browser dialogs designed by Telerik.

Here are the steps to get it installed on your server farm:

  1. Install ASP.NET AJAX Extensions 1.0 on all web front-end servers in the farm.
  2. Download the RadEditor control from Telerik.
  3. Install it (can be done globally or on a per-web application basis).

Note that the RadEditor installation instructions (#3 above) differ slightly for WSS and MOSS. The documentation contains links to both sets of instructions.

The next step is to tell SharePoint where to use it (use any or all of these links, depending where you want it used):

  1. Using RadEditor in List Items
  2. Using RadEditor Web Part (in place of the Content Editor Web Part)
  3. Using RadEditor for Web Content Management (Page Authoring/Editing)

Lastly, you have two dialog choices for the “Insert Hyperlink” and “Insert Image” buttons on the RadEditor toolbar. The first choice is to use the cross-browser dialogs designed by Telerik. The second is to use the Asset Picker Dialogs built into SharePoint. You may want to examine the trade-offs before you decide.

If you decide to go with the Asset Picker Dialogs built into SharePoint, read Telerik’s instructions that talk about which config files to modify. To swap out the dialogs, go to each applicable config file, and look for the tools named ImageManager and LinkManager. To use the Asset Pickers, simply add MOSS (all caps) to the front of the tool names (e.g. MOSSImageManager). Note that this will only work in MOSS, not WSS (because the Asset Pickers aren’t included as part of WSS).

And that’s it. For information about how to further customize the toolbar (add/remove buttons, add new CSS styles, etc.),  see the RadEditor documentation.

Posted in SharePoint | Tagged: , | Leave a Comment »

“Unable to Display this Web Part” Error in MOSS 2007

Posted by sapientcoder on November 14, 2008

If you’ve worked with SharePoint (MOSS) 2007 for any length of time, you’ve probably seen this error by now:

Unable to display this web part. To troubleshoot the problem, open this page…

I won’t bother typing out the rest of it because what it says, like most SharePoint error messages, isn’t incredibly helpful anyway.

There are many things that can cause this error (like doing a site restore with SharePoint Designer, etc.), but I’m writing about a specific circumstance I encountered today.

I had a case today where I saw this error on ALL web parts on a page simultaneously. Now, if they had all been web parts hooked into RSS feeds or web services or something, then maybe it wouldn’t have surprised me as much that they all failed at once (perhaps because of a network problem or something). However, these were web parts like a Summary Link Web Part and a couple of Content Query Web Parts.

Upon turning on debugging in Web.config, I discovered that the problem was my ItemStyle.xsl file (in the Style Library) that’s used by the Content Query Web Parts. An XSL template that I had copied-and-pasted from a blog into the file had a slight syntax error in it (it had “<br/>” as “</br>”).

So, the moral of the story is that syntax errors in your ItemStyle.xsl file can cause all web parts on a page to fail if any of the web parts are making use of that file. It would be nice if SharePoint would give a slightly better error message (like “Problems with the item styles used by this page prevent it from being displayed correctly” or something along those lines), but that’s not the case.

Posted in SharePoint | Tagged: , , | Leave a Comment »

NSIS Integration with Visual Studio.NET

Posted by sapientcoder on August 26, 2008

Based on the amount of traffic my blog post that talks about auto-generating NSIS scripts with iNSISt has generated, I’ve decided to go ahead and create a product that nicely integrates NSIS with Visual Studio.NET and allows people to use NSIS from within their Visual Studio projects. I’ve created similar things before and believe I can do it in a relatively short timeframe.

To those of you reading this because you stumbled upon this blog while looking for just such a product: Please leave a comment and tell me how you think a product like this should work! What would you like to see? What should it do? What features do you like or dislike about similar products for other installers (like Lexpa’s Inno Setup, for example)?

I can venture forth using my best guess and create something, but we all know as developers that products tend to be much more successful and useful when built upon a solid set of requirements.

Also, I’ll probably use this product as a springboard to get my software business up and running (even though I know it means I’ll probably be branching outside of only serving the travel industry like I had originally talked about). Along those lines, if you’re an experienced developer who would like to join me and help create this product, please leave a comment with a way to contact you and let me know. And since this product will most likely be developed as a Visual Studio Package, experience with VS packages and/or programming against COM interfaces would be a plus.

Posted in NSIS | Tagged: , | 4 Comments »

Andertoons Daily Cartoon in SharePoint

Posted by sapientcoder on August 25, 2008

If you haven’t seen the cartoons at www.andertoons.com, I highly recommend you check them out when you get a chance. They’re friendly, non-offensive cartoons (at least the ones I’ve seen are) that are good for using in corporate environments such as on company intranets.

I stumbled upon Andertoons because I was looking for a way to increase traffic to the Information Technology site within our corporate intranet, and I decided (after some research) that posting a daily technology cartoon might be an easy way to drum up a little more interest in the site. I wanted something funny but a little less controversial than Dilbert.

Since our corporate intranet is built on SharePoint (MOSS) 2007, I initially decided to use the “RSS Viewer” web part in SharePoint to hook into the daily technology cartoon feed from Andertoons. At first, everything appeared to be fine… until I went to look at the site the day after and discovered that the cartoon hadn’t updated in SharePoint although it was clearly updated when I typed the feed’s URL in my browser and pulled it up that way.

After many hours of research and troubleshooting, I discovered that some RSS feeds don’t return the correct data when the Http request made to the feed doesn’t include a “User Agent” string in the request. The behavior when this happens can differ from one feed to the next but is usually along the lines of no data returned, “stale” (cached) data returned, etc. As it turns out, SharePoint’s RSS viewer leaves that field empty when requesting RSS feeds (see this discussion thread on MSDN).

The fix? Well, there are a couple of options. The first option is to ask the site hosting the RSS feed to change their processing logic so that an empty “User Agent” field returns correct data. The other option is to use a different method to request the feed so that the “User Agent” field isn’t empty. In my case, I used Visual Studio .NET 2005 to create a custom web part that goes out, grabs the feed, pulls the latest cartoon image from it, and renders the image. The key is that I manually set the “User Agent” field in my web request.

Without getting into all of the details of creating custom web parts, the majority of the code inside of my CreateChildControls method looks like this:

using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
// plus the normal SharePoint web part namespaces

public override void CreateChildControls()
{
    ...

    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(FEED_URL);

    // manually set the "User Agent" field so the current (non-cached) feed is returned;
    // it really doesn't matter what the value is just so long as it's not blank
    request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)";

    WebResponse response = request.GetResponse();

    string html;
    using (StreamReader reader = new StreamReader(response.GetResponseStream())
    {
        html = HttpUtility.HtmlEncode(reader.ReadToEnd());
        Match m = Regex.Match(html, @"www\.andertoons\.com\/cartoon\/(\d+)");

        if (m.Success)
        {
            html = "&amp;lt;br /&amp;gt;&amp;lt;img border=0 src=\"http://www.andertoons.com/img/cartoons/" +
                match.Groups[1].Value + ".jpg" + "\"&amp;gt;";
        }
    }

    if (html != null)
    {
        // output the contents of "html"
    }

    ...
}

Posted in SharePoint | Tagged: , , | 1 Comment »

Why Gas Isn’t Getting Cheaper

Posted by sapientcoder on August 22, 2008

Are you like me in that you’ve been wondering why the price of gas at the pump never seems to go down when the price of oil goes down in the market? You’ve probably heard the rumors that speculators are driving up the prices, right? Well, it may surprise you to find out the truth behind who some of those speculators are.

Read this recent article in Time magazine: http://www.time.com/time/business/article/0,8599,1834888-1,00.html.

The article was sent to me by my brother. He’s getting his Ph.D. in Materials Engineering at Stanford and knows one of the authors.

Posted in Economy | Tagged: , , | Leave a Comment »

Programmatically Converting/Printing a Web Page to PDF

Posted by sapientcoder on August 8, 2008

Something that came up recently at work was the need to programmatically convert a web page (rendered, not raw HTML) to PDF. Since I was less than impressed with the documentation I found on the web explaining how to do this in a Windows enviroment, I thought I’d post how I did it to make it easier for the next person.

Let’s start with the overall architecture. The first thing I did was install PDFCreator on a server on our network. PDFCreator is a free “virtual printer” that allows you to print documents to it and writes them out as PDF documents. While I use CutePDF on my own computer for this purpose, PDFCreator has the advantage of having a “batch print” capability, meaning it’s possible to print to it without a print dialog, and the files are automatically saved to a specific location. To do this, I installed PDFCreator as a service (instructions here). I then created a console application (using VS.NET 2005) that grabs the web page I’m interested in, renders it, and sends it to the PDF printer. I set this application up as a Scheduled Task in windows so it can run weekly.

Now let’s take a look at the code. (All code snippets include the relevant “using” statements.) Here’s my Main method (leaving out the exception handling for brevity’s sake):

using System.Windows.Forms;

[STAThread]
static void Main(string[] args)
{
    Application.Run(new CustomApplicationContext());
}

Now, I know that two aspects of that code seem unusual for a console application. The first is the [STAThread] attribute, which I’ll explain later, and the second is the call to Application.Run(). That call is there because I need the console application to handle events and to continue executing until I explicitly tell it to quit, which means it needs a message loop (or message “pump”). In this regard, it acts more like a GUI application but without the overhead of creating forms I don’t need.

NOTE: Although I used a console application, the same code will work in a Windows (GUI) application by changing the call to Application.Run() to run a form rather than an ApplicationContext.

Next, here’s the majority of my CustomApplicationContext class definition:

using System;
using System.Windows.Forms;

class CustomApplicationContext : ApplicationContext
{
    WebBrowser _browser = new WebBrowser();
    Int16 PRINT_ARGS = (
        Constants.PRINT_DONTBOTHERUSER |
        Constants.PRINT_WAITFORCOMPLETION );

    public CustomApplicationContext()
    {
        _browser.DocumentCompleted += _browser_DocumentCompleted;
        _browser.Navigate(Constants.TARGET_URL);
    }

    void _browser_DocumentCompleted(object sender,
        WebBrowserDocumentCompletedEventArgs e)
    {
        if (_browser.ReadyState == WebBrowserReadyState.Complete)
        {
            try
            {
                string defaultPrinter = WrappedNativeMethods.GetDefaultPrinter();

                if (NativeMethods.SetDefaultPrinter(Constants.PDF_PRINTER_NAME))
                {
                    object pvaIn = PRINT_ARGS;
                    object pvaOut = Type.Missing;
                    ((SHDocVw.IWebBrowser2)_browser.ActiveXInstance).ExecWB(
                        SHDocVw.OLECMDID.OLECMDID_PRINT,
                        SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
                        ref pvaIn, ref pvaOut);
                        NativeMethods.SetDefaultPrinter(defaultPrinter);
                }
            }
            finally
            {
                Application.ExitThread();
            }
        }
    }
}

Here’s the relevant code in my Constants class:

using System;

class Constants
{
    public const string PDF_PRINTER_NAME = "PDFCreator";
    public const Int16 PRINT_WAITFORCOMPLETION = 0x02;
    public const Int16 PRINT_DONTBOTHERUSER = 0x01;
    public const string TARGET_URL = @"http://url_of_web_page_to_print";
}

Lastly, here’s the relevant code in my NativeMethods class:

using System;
using System.Runtime.InteropServices;
using System.Text;

class NativeMethods
{
    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool GetDefaultPrinter(StringBuilder pszBuffer, ref int pcchBuffer);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool SetDefaultPrinter(string name);
}

The above Win32 function declarations were obtained from pinvoke.net.

If you’re wondering why I didn’t post the code for the WrappedNativeMethods class, it’s because there’s only one method in it: a method that wraps the above GetDefaultPrinter method. To get that code, click here and copy the “Sample Code” on the page.

Now I’ll explain the [STAThread] attribute above the Main method. The attribute is there because in order to instantiate an instance of the WebBrowser control, the current thread must be running in a “single-threaded apartment” state. Click here for a short explanation of what that means.

Now to explain a few quirks of printing with the WebBrowser control (i.e. Internet Explorer) and how I worked around them. First, the WebBrowser control has a single Print() method that takes no arguments. As a result, you can’t easily do the following: (a) send the output to any printer other than the system’s default printer, (b) suppress Internet Explorer’s “print” dialog (it’s displayed as though a user clicked “File => Print”), and (c) execute the print call synchronously (i.e. cause it to block until completion rather than firing off a thread and then returning).

To solve the default printer issue, I had to make Win32 API calls to get the current default printer, make my PDF printer the default right before printing, and reset the default printer right after printing.

Next, to work around the print dialog issue, I bypass the web browser’s Print() method and call ExecWB directly (which Print() calls under the hood) with my own arguments. Note that to call ExecWB, the web browser must be cast to a COM interface of type IWebBrowser2. To include that interface (and a few constants) in my code, I added a reference to the “SHDocVw.dll” file (found in the “C:\WINDOWS\system32″ folder). The OLECMDEXEOPT_DONTPROMPTUSER value passed to ExecWB tells the browser to suppress the print dialog. Likewise, the pvaIn parameter contains a reference to PRINT_ARGS, which includes a “don’t bother the user” flag.

By the way, both flags in PRINT_ARGS are documented here on MSDN. Likewise, the IDM_PRINT command (which is what calling ExecWB with the OLECMDID_PRINT argument maps to) is documented here. If you read the docs you’ll see that the IDM_PRINT command takes a VARIANT of type VT_I2 for the pvaIn argument. In .NET, that maps to an object holding a reference to an Int16 structure (see this page on MSDN).

Lastly, you’ll see that PRINT_ARGS includes a “wait for completion” flag. This is what tells the ExecWB call to block until printing is completed. Why is this important? Because we don’t want to reset the default printer and exit the application until we’ve actually printed something. In fact, exiting too soon can cause the print job never to happen.

I hope this post was helpful. Even if you’re not printing to PDF, I hope knowing a little more about how the WebBrowser controls prints in general will help make it easier to use.

Posted in WebBrowser | Tagged: , , , , | 7 Comments »

Troubleshooting the “Failed to enable constraints…” DataSet Error

Posted by sapientcoder on July 30, 2008

At one time or another, many of us who have worked with DataSets in .NET have received this foreboding message:

Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints.

I’ll go out on a limb here and say we probably all agree it’s not an incredibly useful message. So how do you find out what the heck actually happened to cause the error?

Error messages (real error messages, not the exceptionally “helpful” one above) in DataSets are stored at the column and row levels within the tables in the DataSet. Therefore, you can use diagnostic code such as the following to get these messages:

using System.Collections.Specialized; // for NameValueCollection class

...

try
{
    // code that throws DataSet exception
}
catch
{
    // print out a list of the "real" error messages in the DataSet
    NameValueCollection c = BuildDataSetErrorInfo(myDataSet);
    foreach (string name in c)
    {
        System.Diagnostics.Debug.WriteLine(name + c[name]);
    }
}

...

private static NameValueCollection BuildDataSetErrorInfo(DataSet dataSet)
{
    NameValueCollection errorInfo = new NameValueCollection();
    errorInfo.Add("DataSetName: ", dataSet.DataSetName);

    foreach (DataTable table in dataSet.Tables)
    {
        DataRow[] rows = table.GetErrors();
        if ((rows != null) && (rows.Length > 0))
        {
            errorInfo.Add(" - Table: ", table.TableName);
            foreach (DataRow row in rows)
            {
                errorInfo.Add("   - Row Error: ", row.RowError);
                DataColumn[] cols = row.GetColumnsInError();
                if ((cols != null) && (cols.Length > 0))
                {
                    foreach (DataColumn col in cols)
                    {
                        errorInfo.Add("     - Column: ", col.ColumnName);
                        errorInfo.Add("       Column Error: ", row.GetColumnError(col));
                    }
                }
            }
        }
    }

    return errorInfo;
}

Posted in DataSets | Tagged: , | Leave a Comment »