Steve Wellens

Programming in the .Net environment

Sponsors

Links

January 2009 - Posts

Twin Cities SharePoint Camp Winter 2009 (and jQuery)

I stepped out the front door and watched my breath turn to ice crystals in the moonlight…the sun hadn't risen yet. As I walked to the garage, my shoes crunched loudly on the packed snow and when I opened the car door, the metal-on-metal shrieking of cold-friction gave my skin goose bumps. The temperature was -13F below zero and it was a Saturday morning. What on this earth would compel a man to leave a warm bed and face such bitter cold on a weekend?

Answer: The Twin Cities SharePoint Camp hosted by New Horizons of Minnesota!

The event was on Saturday, January 24, 2009 and almost 200 brave souls gave up their Saturday and defied the brutally cold weather to attend. I had used SharePoint at a client's site and have seen numerous posts about it on Asp.Net forums where I am a moderator. But I wanted to learn more.

The seminars were very good and well received by the audiences. A common thread was how difficult it was to accomplish small things in large organizations…I had no idea what they were talking about.

The presenters used self deprecating humor and invited audience participation and feedback in the seminars. There was lots of interesting and useful give-and-take between the presenters and audience. The SharePoint community is a fun group. Much information was exchanged.

Miscellaneous Observations:

The presenters wore matching snazzy red and black shirts; I thought they looked professional. A few, apparently, do not shave on weekends…maybe they were protesting being required to wear matching outfits like MacDonald's employees.

I had never seen such a high percentage of women at a technical event. Around 30 percent of the attendees were women. It was a welcome change from most technical events. Maybe they attended because they have a thing for men in uniform.

Most of the seminars were not mere Power Point presentations but were live demonstrations on setting up and running SharePoint applications. If you've ever presented/demonstrated software to a group, you know how dangerous this can be. As to be expected, there were a few glitches, but it actually demonstrated the knowledge and expertise of the presenters when they were able to effectively work their way through the problems.

Summary:

I now know enough about SharePoint to talk about it intelligently. But, to be honest, the most useful information I took away was in one of the free magazines given out ('Code' Jan/Feb 2009). There was an excellent article called An Introduction to jQuery, Part 1 by Rick Strahl …I couldn't wait to get home so I could start playing with it (see below).

Of course there was the usual swag:

SWAG

jQuery:

Initially, JQuery syntax looks as cryptic as Regular Expressions, but once some of the fundamentals are understood, it becomes much easier. Being able to select elements by using CSS selectors is great if you already know CSS.

Here is my first jQuery code:

    <script type="text/javascript" src="jquery.js"></script>         
    <script type="text/javascript">
 
        // ---- After document is loaded, run this -----
 
        $(document).ready(
            function()
            {               
                MyJScript();
            }
        )
 
        // ---- MyJScript -------------------------
 
        function MyJScript()
        {
            // get all TextBoxes with class MyInput
            var TextBoxes = $(".MyInput");
 
            // if any TextBoxes are blank, set the
            // background color to yellow
            TextBoxes.each(
                function Yellowize()
                {
                    if (this.value == "")
                        this.style.backgroundColor = "yellow";
                }
            )
        }
 
    </script>

I hope you found this helpful or at least interesting.

Steve Wellens

Posted: Jan 25 2009, 09:36 PM by SGWellens | with 1 comment(s)
Filed under: , ,
Internet Explorer 7 and CSS….DOH!

I wanted a cheap, simple visual indicator similar to a progress bar. Note, not the kind of dynamic progress bar used when updating AJAX pages, more of a single-bar graph. It should have been simple task.

My plan was to use a Div as a container, implemented through a panel because they are more convenient to work with. Inside the Div I would put a Label control (which renders as a Span element) with an opaque background color, 100% height, and the width being the percentage done of the progress bar value.

I dropped the controls on the page, added a function to set the percentage and it looked good. And then I looked closer…what the heck (WTH)?

You can see the label is shifted down one pixel. Not a big deal, but…perfection is in the details.

I spent time playing with CSS settings trying to get the Label inside the Div to line up correctly. I tried using a read-only textbox instead of a label but alas, IE7 and CSS are more stubborn then I am.

I then tried using a Div inside of a Div with a Label in the inner Div to hold the text. It worked but the Asp code was ridiculous looking with its three nested elements. The code was too ugly for me to tolerate.

Finally, I got it back to simple code by putting a Label inside of a Label without using a Div.

Here is the Asp code for both versions of the ProgressBar:

    You have this much gas left in the tank:<br />
    <asp:Panel ID="ProgressBarContainer1" runat="server" 
        Height="18px" Width="324px" 
        Style="border-color: Black; border-width: 1px; border-style: solid;" 
        HorizontalAlign="Left">
        <asp:Label  ID="LabelProgressBar1" runat="server" 
            Text="10%"
            Height="100%" 
            Width="10%"
            BackColor="Yellow">
        </asp:Label>
    </asp:Panel>
    <br />
 
    Your project is this late:<br />
    <asp:Label ID="ProgressBarContainer2" runat="server" 
        Height="18px" Width="324px" 
        Style="border-color: Black; border-width: 1px; border-style: solid;">       
        <asp:Label ID="LabelProgressBar2" runat="server" 
            Text="10%" 
            Height="100%" 
            Width="10%" 
            BackColor="Yellow">
        </asp:Label>
    </asp:Label>

Here is the function that updates the Progress Bar. I left the code in the affects both implementations:

    // ---- UpdateProgressBar -------------------------------
 
    void UpdateProgressBar(int Percent)
    {
        if (Percent < 0)
            Percent = 0;
        if (Percent > 100)
            Percent = 100;
 
        // first implementation
        LabelProgressBar1.Width = new Unit(Percent, UnitType.Percentage);
        LabelProgressBar1.Text = String.Format("{0}%", Percent);
 
        // second implementation
        LabelProgressBar2.Width = new Unit(Percent, UnitType.Percentage);
        LabelProgressBar2.Text = String.Format("{0}%", Percent);       
    }

Here are both versions of the ProgressBar:

Of course, this would be much nicer as a user control…but I didn't want to take away from the two points of this post:

  • Sometimes easy things are hard.
  • Sometimes it's hard to find easy solutions.

I hope someone finds this helpful.

Steve Wellens

Posted: Jan 16 2009, 09:08 AM by SGWellens | with 3 comment(s)
Filed under: , , ,
Create an XSD Schema….without knowing a darn thing about XSD.

Back in the old days, when dinosaurs roamed the earth, developers wanting to exchange data between applications used binary formatted data, hard-coded text field lengths, or delimited text files. Much parsing and error checking was involved. It was tedious.

With XML files a lot of that work can be done automatically… with one major drawback: You have to learn yet another 'language': XSD.

I don't know about you, but I feel the need to limit the amount of knowledge being poured into my brain on a daily basis… lest something gives (there is a fire-hose teacup analogy in there somewhere).

Here is how to create XML Schemas using classes in the .Net framework without knowing anything about XSD:

    protected void Button1_Click(object sender, EventArgs e)
    {
        // The DataSet name becomes the root XML element
        DataSet MyDataSet = new DataSet("Golfers");
 
        // This can be confusing, the 'DataTable' will actually
        // become Elements (Rows) in the XML file.
        DataTable MyDataTable = new DataTable("Golfer");
 
        MyDataSet.Tables.Add(MyDataTable);
 
        // Make columns attributes so we can 
        // link directly to a GridView
        MyDataTable.Columns.Add(new DataColumn("ID",
                                     typeof(System.Int32),
                                     null,
                                     MappingType.Attribute));
 
        MyDataTable.Columns.Add(new DataColumn("Name",
                                     typeof(String),
                                     null,
                                     MappingType.Attribute));
 
        MyDataTable.Columns.Add(new DataColumn("Birthday",
                                     typeof(DateTime),
                                     null,
                                     MappingType.Attribute));
 
        // Write out the XSD
        MyDataSet.WriteXmlSchema(@"C:\GolfersSchema.xsd");
 
        // Put some data in the table
        DataRow TempRow;
        TempRow = MyDataTable.NewRow();
        TempRow["ID"] = 1;
        TempRow["Name"] = "Bobby Jones";
        TempRow["Birthday"] = new DateTime(1902, 3, 17);
        MyDataTable.Rows.Add(TempRow);
 
        TempRow = MyDataTable.NewRow();
        TempRow["ID"] = 2;
        TempRow["Name"] = "Sam Snead";
        TempRow["Birthday"] = new DateTime(1912, 5, 27);
        MyDataTable.Rows.Add(TempRow);
 
        TempRow = MyDataTable.NewRow();
        TempRow["ID"] = 3;
        TempRow["Name"] = "Tiger Woods";
        TempRow["Birthday"] = new DateTime(1975, 12, 30);
        MyDataTable.Rows.Add(TempRow);
 
        // Write out the data
        MyDataSet.WriteXml(@"C:\Golfers.xml");
    }

Here is how the data is saved, not bad eh?

  <?xml version="1.0" standalone="yes"?>
  <Golfers>
    <Golfer ID="1" Name="Bobby Jones" Birthday="1902-03-17T00:00:00-05:00" />
    <Golfer ID="2" Name="Sam Snead" Birthday="1912-05-27T00:00:00-05:00" />
    <Golfer ID="3" Name="Tiger Woods" Birthday="1975-12-30T00:00:00-06:00" />
  </Golfers>

Here is what the Schema looks like. Note, you can always go into the schema and tweak things.

  <?xml version="1.0" standalone="yes"?>
  <xs:schema id="Golfers" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="Golfers" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
      <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
          <xs:element name="Golfer">
            <xs:complexType>
              <xs:attribute name="ID" type="xs:int" />
              <xs:attribute name="Name" type="xs:string" />
              <xs:attribute name="Birthday" type="xs:dateTime" />
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>

Here is one way to validate the XML against the Schema:

    protected void Button2_Click(object sender, EventArgs e)
    {
        // First, read in the XML schema
        DataSet MyDataSet = new DataSet();
        MyDataSet.ReadXmlSchema(@"C:\GolfersSchema.xsd");
 
        // Now, read in the XML file (it is validated 
        // against the schema when it is read in).
        MyDataSet.ReadXml(@"C:\Golfers.xml");
 
        // See how it looks in a GridView
        GridView1.DataSource = MyDataSet;
        GridView1.DataBind();
    }

 

You can test the validation by modifying the XML file and trying to read it:

    <Golfer ID="abc" Name="Tiger Woods" Birthday="1975-12-30...

When the XML file is read, an exception will be generated. Without the XSD validation, the XML file is loaded without error.

I hope you find this useful.

Steve Wellens

By the way, Microsoft had an event for U of M graduates at Brit's Pub in Minneapolis, Minnesota last night. It was a great success. There was good food and great beer. In the drawing for prizes, the big ticket item, an Xbox 360, was won by the only female graduate in attendance…congratulations!

I got this pen :)

Posted: Jan 05 2009, 10:28 AM by SGWellens | with 7 comment(s) |
Filed under: , , , , ,
Anonymous Macros…Awesome Power!

Once upon a time, in the chilly windowless basement of a Fortune 100 company, a young programmer joined our small development team. He brought with him a programmers editor called Brief. Within weeks every developer in the entire company was using this powerful editor. The three compelling features were the ability to select copy and paste columns of text, multiple windows with cut and paste between them and…drum roll please…macros. With macros, enormously tedious tasks can be performed easily in seconds.

Sadly, the Brief editor went the way of the Dodo, but happily, the macro feature lived on. In the Visual Studio Editor this feature is called Anonymous Macros.

It would be clumsy to describe how Anonymous Macros work with written words so I made a video that shows the power. The video is here:  

After seeing the video, you may want to put these keystrokes on a 'sticky' to tape to your monitor:

Ctrl-Shift-R    Start/Stop Recording

Ctrl-Shift-P    Playback

I've always wanted to try doing a video and this was a good excuse. I hope you find it helpful.

Steve Wellens

From Table to TreeView…Displaying Hierarchies

Who remembers Object Databases? At one time they were going to dominate the programming world. But, as with a lot of over-hyped technology (Lisp, AI, ADA, Virtual Reality) they were destined to a small niche segment of the market.

Relational Databases remain king. Why? Because Relational Databases are easy to understand and they work good…real good.

Still, many times hierarchical data is stored in relational databases. A common example is an organizational chart. This is usually stored in a single Employee table. Each table row represents an employee with an ID field and a ParentID (Boss) field.

This code shows how to go from a hierarchical table to a TreeView. There is nothing sophisticated here, but a few tricks are needed to make it work.

The first trick is to sort the records by the ParentID. We cannot insert a node into the tree until its parent node is in the tree.  This implies a special case where the root node of the tree must be inserted first since it has no parent.

Here is sample data:

// Create the DataTable and columns
DataTable ItemTable = new DataTable("MyTable");
ItemTable.Columns.Add("ID"      , typeof(int   ));
ItemTable.Columns.Add("ParentID", typeof(int   ));
ItemTable.Columns.Add("Name"    , typeof(String));
 
// add some test data
ItemTable.Rows.Add(new object[] { 0,-1, "Bill Gates"    });
ItemTable.Rows.Add(new object[] { 1, 0, "Steve Ballmer" });
ItemTable.Rows.Add(new object[] { 3, 1, "Mary Smith"    });
ItemTable.Rows.Add(new object[] { 2, 0, "Paul Allen"    });
ItemTable.Rows.Add(new object[] { 4, 2, "Ahmed Jones"   });
ItemTable.Rows.Add(new object[] { 5, 2, "Wing Lee"      });
 
// Use the Select method to sort the rows by ParentID
DataRow[] SortedRows;
SortedRows = ItemTable.Select("", "ParentID");

The second trick is the use of XmlDocument. Initially, I tried to go directly to a TreeView without the intermediate data object. But the TreeView control does not have functionality to find an arbitrary node given a node ID. Writing a recursive find function seemed like a lot of work so we take advantage of the XPath search capabilities of XmlDocument to find an arbitrary Parent node.

// create an XmlDocument (with an XML declaration)
XmlDocument XDoc = new XmlDocument();
XmlDeclaration XDec = XDoc.CreateXmlDeclaration("1.0", null, null);
XDoc.AppendChild(XDec);
 
// iterate through the sorted data
// and build the XML document
foreach (DataRow Row in SortedRows)
{
    // create an element node to insert
    // note: Element names may not have spaces so use ID
    // note: Element names may not start with a digit so add underscore
    XmlElement NewNode = XDoc.CreateElement("_" + Row["ID"].ToString());
    NewNode.SetAttribute("ID"      , Row["ID"].ToString());
    NewNode.SetAttribute("ParentID", Row["ParentID"].ToString());
    NewNode.SetAttribute("FullName", Row["Name"].ToString());
 
    // special case for top level node
    if ((int)Row["ParentID"] == -1)
        XDoc.AppendChild(NewNode);  // root node
    else
    {
        // use XPath to find the parent node in the tree
        String SearchString;
        SearchString = String.Format("//*[@ID=\"{0}\"] ", Row["ParentID"].ToString());
        XmlNode Parent = XDoc.SelectSingleNode(SearchString);
 
        if (Parent != null)
            Parent.AppendChild(NewNode);
        else
            ; // Handle Error: Employee with no boss
    }
}

The third trick deals with an error in the documentation. A TreeView (or a Menu) cannot be data bound to an XmlDocument. Doing so produces this error: HierarchicalDataBoundControl only accepts data sources that implement IHierarchicalDataSource or IHierarchicalEnumerable. The workaround is to create an XmlDataSource control dynamically and assign the XML string to it.

// we cannot bind the TreeView directly to an XmlDocument
// so we must create an XmlDataSource and assign the XML text
XmlDataSource XDdataSource = new XmlDataSource();
XDdataSource.ID = DateTime.Now.Ticks.ToString();  // unique ID is required
XDdataSource.Data = XDoc.OuterXml;
 
// we want the full name displayed in the tree so 
// do custom databindings
TreeNodeBinding Binding = new TreeNodeBinding();
Binding.TextField = "FullName";
Binding.ValueField = "ID";
TreeView1.DataBindings.Add(Binding);
 
// Finally! Hook that bad boy up!       
TreeView1.DataSource = XDdataSource;
TreeView1.DataBind();

After all that work, the results:

I hope you find this useful,

Steve Wellens.

PS: Happy 2008++;

More Posts