<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://aspadvice.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>.Net Discoveries</title><link>http://aspadvice.com/blogs/net_discoveries/default.aspx</link><description>An attempt to pass along some answers I have discovered in my .Net coding.</description><dc:language>en</dc:language><generator>CommunityServer 2.1 (Build: 60809.935)</generator><item><title>Working with Dual Monitors in .Net</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx</link><pubDate>Thu, 08 May 2008 00:06:55 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:42004</guid><dc:creator>Yougotiger</dc:creator><slash:comments>0</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/42004.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=42004</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=42004</wfw:comment><description>&lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;It's been a while since I posted becuase until recently I haven't had the opportunity to do much coding lately. All the website updates I was working on got suspended and then outsourced so I haven't been doing much coding, mostly other projects. Recently though, I had reason to do some work with creating a web service and some other interesting things that I may do some articles on. One of those is today's article.&lt;/p&gt; &lt;p&gt;We recently purchased access to an XML data feed that gives us live data that we'd like to redisplay in a number of ways and a number of locations here. So I was tasked with the coding. I'll probably talk more about our re-purposing of the feed later in a subsequent article, but today I want to talk about something different, supporting dual monitors. The data we retrieve from the feed, we will be displaying in a number of locations. This is done quite often with a dual monitor computer. One display shows at the computer, and the other at a remote monitor, usually a flat panel TV.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Our problem, we found was that the application I wrote to display the information would maximize to the primary monitor, usually we want it to be displayed on the secondary monitor instead (the flat panel TV). Our challenge in this excercise&amp;nbsp;is to get the application to run maximized&amp;nbsp;on the secondary monitor. Additionally, not all locations that the application will be used at should necessarily go directly to the secondary monitor, so we want to let the user who launches the application select which monitor to display on.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;Ok, let's begin by creating an application that exhibits the old, undesirable primary monitor oonly&amp;nbsp;behavior first and then begin modifying the application to do what we need it to do. We don't need much for an application, so it'll be pretty simple. Begin by creating a new application.&amp;nbsp;Modify&amp;nbsp;the form so it maximizes by default, by changing the &lt;strong&gt;WindowState&lt;/strong&gt; property of the form to &lt;strong&gt;Maximized&lt;/strong&gt;. Also, we wanted to&amp;nbsp;NOT have any&amp;nbsp;window controls showing so&amp;nbsp;also set the &lt;strong&gt;FormBorderStyle&lt;/strong&gt; property to &lt;strong&gt;none&lt;/strong&gt;. One last thing, we want to add one line of code to the form's click event handler as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Application.Exit()&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This will allow us to exit the program, otherwise since we don't have a title bar at the top, it would be rather difficult. Run the application and your form should fill the primary monitor. But how do we get it to go to the secondary monitor? We can't drag it there (no title bar remember?), moving the .exe file to the second monitor doesn't help either. We'll have to do it through code. So, add to your form a button and a checkbox, name the button btnDoIt, and the checkbox chkSecondMonitor. Change the checkbox's&amp;nbsp;&lt;strong&gt;visible&lt;/strong&gt; property to &lt;strong&gt;false&lt;/strong&gt;. Now we're ready to start adding some code to move to the 2nd monitor. I found a post on Microsoft forums that walked me through some of the code to do what we want, you can read their information &lt;a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=798811&amp;amp;SiteID=1"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;First we want to make the checkbox show only&amp;nbsp;if there are dual monitors. If not, then we don't want to show it. So we'll add some code to detect if there are multiple monitors. Let's create a new function that returns if there are dual monitors as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Function DualMonitors() As Boolean&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim myScreens() As Screen = Screen.AllScreens&lt;br&gt;&amp;nbsp;&amp;nbsp; If myScreens.Length &amp;gt; 1 Then Return True Else Return False&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, this retrieves an array of all the screens (monitors) available, checks if the array has more than one element (or more than one screen) and returns true if there are multiple. Now we need to add some code to the form_load event to make visible the checkbox if there are dual monitors. Add the following to form_load:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;chkSecondMonitor.Visible = DualMonitors()&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now if you run your application, you'll see that our checkbox will display depending on availability of a 2nd monitor. However, we still&amp;nbsp;need to make the form move when the button is clicked (if the checkbox is&amp;nbsp;checked). To our btnDoIt_clicked event, add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;If chkSecondMonitor.Checked = True Then&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim theScreen As Screen = Screen.AllScreens(1)&lt;br&gt;&amp;nbsp;&amp;nbsp; Me.Location = theScreen.Bounds.Location&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, we're&amp;nbsp;retrieving&amp;nbsp;the specifics of the second screen (element 1 in the AllScreens array) and using its location to set our form's location. This in effect should move our form to the starting point of the 2nd monitor, the upper-left location will become our form's upper-left location.&lt;/p&gt; &lt;p&gt;Run your application, check the&amp;nbsp;checkbox, click the button&amp;nbsp;and see what happens. That's right, big fat nothing. Why? You can't move a form that is maximized. We need to either launch the application NOT maximized, or restore (un-maximize) the form, move it and then re-maximize it. So we'll modify our code to restore, move and maximize. Add the following BEFORE the Me.Location = line in our btnDoIt_Clicked event handler:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Me.WindowState = FormWindowState.Normal&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and add it's corresponding maximize call after the Me.Location line:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Me.WindowState = FormWindowState.Maximized&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now run your application and observe it functioning. Check the box, click the button, the form restores, moves and the maximizes. Just as it should.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;Realistically speaking, moving the application to the second monitor is very easy. There are other things that we could do using the screen object that we haven't touched on (centering the form on the&amp;nbsp;second monitor&amp;nbsp;etc). A point of distinction that may help if you want to do further development, the Bounds encompasss the entire screen (including the task bar), if you want working area (not including the task bar) you'll need to get the WorkingArea property instead of Bounds property.&lt;/p&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;;subject=Working+with+Dual+Monitors+in+.Net" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;;title=Working+with+Dual+Monitors+in+.Net" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;title=Working+with+Dual+Monitors+in+.Net" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;;title=Working+with+Dual+Monitors+in+.Net" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx&amp;amp;;title=Working+with+Dual+Monitors+in+.Net&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2008/05/07/Working-with-Dual-Monitors-in-.Net.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=42004" width="1" height="1"&gt;</description><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/Windows+Forms/default.aspx">Windows Forms</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/Dual+Monitors/default.aspx">Dual Monitors</category></item><item><title>VB.Net and GroupWise Soap Pt. 4 - HTML Messages</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx</link><pubDate>Thu, 04 Oct 2007 23:54:52 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:35426</guid><dc:creator>Yougotiger</dc:creator><slash:comments>0</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/35426.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=35426</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=35426</wfw:comment><description>&lt;p&gt;&lt;/p&gt; &lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;In my last few&amp;nbsp;posts, I talked about accessing GroupWise via the SOAP services that they provide. In the last post, specifically we looked at sending and creating messages. I left with the promise that if I could get draft HTML messages to work that I'd post an article about it. Here it is.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Our problem, continued from the last post, was to figure out how to login and create a draft message in the user's Work in Progress folder that is a template of something the company uses currently in hard copy. In this article, we'll leverage all the stuff we've done up to this point and add the functionality to create an HTML draft message.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;We will be building on the application that we started last time. If you haven't created the login procedures from &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;Part 1&lt;/a&gt;,&amp;nbsp;the folder listing subroutines from&amp;nbsp;&lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;Part 2&lt;/a&gt;, and the&amp;nbsp;creating/sending messages&amp;nbsp;subroutines&amp;nbsp;from&amp;nbsp;&lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;Part 3&lt;/a&gt;,&amp;nbsp;I suggest you do it now, we will need each of those pieces and will just be adding additional functionality.&lt;/p&gt; &lt;p&gt;When I started this post, this first line actually read, "We actually have very little to do do make this work." That's not&amp;nbsp;correct, we have a bunch of stuff to do to get it to work. In addition to adding HTML messages to our functionality,&amp;nbsp;we'll add functionality so that we can embed images into our HTML and they would display in our message. This added a decent amount of code to the project.&lt;/p&gt; &lt;p&gt;I&amp;nbsp;ran into a lot of trouble with getting images to display and had a lot of discourse via the forum with Novell people about how to get it done. Finally&amp;nbsp;my troubles&amp;nbsp;came down to 2 things: 1) The image wasn't attaching as it should, it was only 10% of what it should be, and 2) you have to open the draft message in the mode it was "created" or intended to be in. #1 I will address a little later in this post when it makes more sense to do so. #2 however took me quite a long time to figure out, (actually I got a decent amount of help from an outside consultant who replied on the post). Basically, if you're default compose view is set to plain text, when you open a draft HTML item, it WON'T be opened as HTML. If you want to save an HTML message (or create one&amp;nbsp;in our instance) as a draft and then reopen it as an HTML message, you&amp;nbsp;need to set your default compose view to HTML. You can do this under Tools-&amp;gt;Options-&amp;gt;Environment-&amp;gt;Views(tab), and select HTML as the 'Default Compose View &amp;amp; Font'. I learned this the hard way. One would think that the message itself would have enough identifying information in it that the program can figure out which to do... Nope. Or one would figure that if it opened plain text, that switching to HTML view would restore your original HTML message... Nope. Neither way displays your images and the other&amp;nbsp;expanded features of HTML. Ok, enough complaining about that for now, lets get started.&lt;/p&gt; &lt;p&gt;First, lets modify our form. We only need to add one control, a checkbox named chkHtml with text of 'HTML Message'. Using this, we can create an HTML message (using HTML code) or a plain text message using the same textbox and createMessage code.&lt;/p&gt; &lt;p&gt;We have a number of things to create in the code so that this will work, and then we'll finally go back and modify our CreateMessage subroutine to include our HTML stuff.&lt;/p&gt; &lt;p&gt;If we look in the Novell&amp;nbsp;documentation for creating an HTML message, we find &lt;em&gt;"HTML messages are returned as the first attachment and are named text.htm. Any embedded images or associated files are also downloaded as attachments. These HTML associated files are all marked hidden."&lt;/em&gt; So, to create an HTML message we need to add an attachment with our HTML content (rather than create a message body... ok, that's not strictly true, it's recommended to enter a text version of your&amp;nbsp;HTML as the message body in addition to your attachment). That means, we'll just add an attachment to our message body IF we check the chkHTML checkbox.&lt;/p&gt; &lt;p&gt;Further reading in the Novell documentation reveals: &lt;em&gt;"Any embedded images or associated files are also downloaded as attachments. The HTML message body and accompanying files are grouped together. The text.htm file is first, followed by the other associated files. Each of the accompanying attachments has a contendId element."&lt;/em&gt; What this means is that we need to create an attachment for our HTML code, this should be the first attachment and it should be named text.htm. Also,&amp;nbsp;we should create an attachment for each image that should be embedded in the message, and they should follow in order. Now, this only applies for images that are &lt;u&gt;embedded INTO&lt;/u&gt; the email. This doesn't apply to images that are linked from outside sources (their src is a web address instead of a file path). In our example we'll only use images that are embedded into the email, we won't be processing image files that are linked from outside sources&amp;nbsp;(in fact our code will probably break a linked image if we do it that way, more on that later).&lt;/p&gt; &lt;p&gt;Ok, now with that all said, we should have a basic understanding of what we need to create, so let's get started.&amp;nbsp;Right off the bat, we want to create a new function that will return our HTML message and&amp;nbsp;embedded images&amp;nbsp;as an attachment array (HTML message first followed by the images). Let's create a function to do this and define it as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Function ConvertHtmlMessageToAttachmentArray _ &lt;br&gt;&amp;nbsp;&amp;nbsp; (ByVal sMessageToConvert As String) As GWWS.AttachmentItemInfo()&lt;br&gt;&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We are going break up all our pieces and return them as a GroupWise message AttachmentItemInfo array. Since we don't know right off the bat how many images we are going to add to our attachment array, we'll take the easy way out and rather than define an array, we'll create a generic list(of) our AttachmentItemInfo and just keep adding them. Then we can have our list output an array. So, we need to add some imports statements at the top of our code,&amp;nbsp;so let's add them all now and we'll get to each in time. Add the following to the top of your code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Imports System.Collections.Generic&lt;br&gt;Imports System.Text.RegularExpressions&lt;br&gt;Imports System.IO&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;The important one for our list(of) operations is the Collections.Generic. If you haven't played with list(of) yet, try it out it is VERY cool stuff. Anyhow, we're ready to begin defining some variables for our code. Let's start with our list(of) definition, add the following just inside your function declaration:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim theAttachments As New List(Of GWWS.AttachmentItemInfo)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This defines a list of AttachmentItemInfo objects for us, we'll use it later. If you remember we need to add our HTML&amp;nbsp;message attachment&amp;nbsp;first,&amp;nbsp;and &lt;u&gt;then&lt;/u&gt; our images as attachments. Now we COULD create our HTML attachment first, but we need to rename our image attachments and update the references to them in our HTML code. If we add our HTML attachment&amp;nbsp;first, then the HTML attachment will have incorrect code and won't display our images as we would like. So what we'll do is add our images first, and THEN create our HTML attachment and insert it at the beginning of the list (LOVE GENERIC LISTS!).&lt;/p&gt; &lt;p&gt;To find all the instances of our images, we'll use the REGEX object and create a regular expression to find our image tags, extract the SRC and replace it with our updated src. (This is where the&amp;nbsp;RegularExpressions import statement comes in). Now, not being an expert on regular expressions, it gets to be a little bit confusing getting everything just right, but I managed to get a regular expression that can find &amp;lt;img &amp;gt; and return everything from &amp;lt; to &amp;gt;.&amp;nbsp;So&amp;nbsp;we'll do is&amp;nbsp;create two regex objects, and instantiate them with the correct regular expression to return what we need. Add the following just below our last variable declaration:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim regImage As New Regex("&amp;lt;img[^&amp;gt;]*&amp;gt;")&lt;br&gt;Dim regSrc As New Regex("src=" &amp;amp; Chr(34) &amp;amp; "[^&amp;gt;]*" &amp;amp; Chr(34))&lt;br&gt;Dim matchesImages As MatchCollection = regImage.Matches _&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (sMessageToConvert)&lt;br&gt;Dim iCount as Integer = 0 'Counter variable&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we define a regex object to find all our image tags, and then we define one to find the src within the tag, the regex for the image tag is defined like this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&amp;lt;img - Match the characters "&amp;lt;img" literally&lt;br&gt;[^&amp;gt;] - Match a single character NOT in the list (our list consists&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; of only the &amp;gt;&amp;nbsp;character)&lt;br&gt;* - Match the proceeding between zero and unlimited times (meaning&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; match our [^&amp;gt;] block 0 to unlimited times)&lt;br&gt;&amp;gt; - Match "&amp;gt;" literally&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So basically, we find "&amp;lt;img" followed by anything except "&amp;gt;" followed by "&amp;gt;". In the next to last line, we request our Regex object to find matches of this sequence and store them in a matches collection. We'll iterate through the collection later and use our other Regex object, it finds the src.&amp;nbsp;Basically, the regSrc object is defined like this:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;src= - Match "src=" literally&lt;br&gt;Chr(34) -&amp;nbsp;adds the double quote (") to the src= statement, so we're actually finding src=" literally&lt;br&gt;[^&amp;gt;] - Match a single character NOT in the list (our list consists&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; of only the &amp;gt;&amp;nbsp;character)&lt;br&gt;Chr(34) -&amp;nbsp;adds the double quote to the END of our statement&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So basically, we're looking for src=" literally (including the double quotes, we had to use the Chr(34) code because the way strings are handled we couldn't just type the double quotes), followed by anything EXCEPT a &amp;gt; followed by " (double quotes). This returns the entire src="xyz" string. (Confused&amp;nbsp; yet?) (As an FYI, we will be replacing the content of the src attribute. If you have an src that links to an external website, it will be replaces. You'll need to add a check to differentiate between embedded images and linked images. I haven't done so.)&lt;/p&gt; &lt;p&gt;And finally we setup a counter variable, iCount. Ok, so we've setup our regex objects, and we've populated a collection with the strings that match our &amp;lt;img &amp;gt; tags. Now we want to cycle through the collection and do two things 1) create an attachment with our image, and 2) replace the src attribute with an updated src that reflects that the image is embedded in the email. So first thing, let's setup a For Each loop to loop through our image tags collection. Add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;For Each m As Match In matchesImages&lt;br&gt;&lt;br&gt;Next&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now we want to&amp;nbsp;extract the src portion, and use it to&amp;nbsp;make an attachment out of the image. So what we'll do is create a couple of variables here inside the for each loop to do that. Add to your For Each loop, the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim matchSrc As Match = regSrc.Match(m.Value)&lt;br&gt;Dim sSrc As String = matchSrc.Value.Replace _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ("src=", "").Replace(Chr(34), "")&lt;br&gt;Dim theAttachment As GWWS.AttachmentItemInfo = _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; GetAttachment(sSrc, True)&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;What we're doing is creating a regex match object and matching the src phrase in our first img object. If you remember, this will return the entire src="xyz" phrase. We want just the file name for now, so the second variable, sSrc removes the src= and the double quotes from both ends, so we should just have a file path to an image and nothing more. Finally, we need to retrieve the image using the filename and make it an attachment. So we pass its filepath to the GetAttachment function we are about to create. This returns an AttachmentItemInfo object&amp;nbsp;that we will use in a minute.&amp;nbsp;There are some other things we need to do before our attachment is finished, but first we need the file to be an attachment. So let's create a new function GetAttachment and define it as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Function GetAttachment(ByVal sFilePathAndName As String, _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Optional ByVal bHideAttachment As Boolean = False) _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;As GWWS.AttachmentItemInfo&lt;br&gt;&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Our function will take a file path, retrieve the file located there, create an attachment and return the attachment. We also have a parameter to make the attachment hidden (as we should for html messages). (&lt;strong&gt;Bonus&lt;/strong&gt;, you can use this function to create an attachment for ANYTHING, not just our images). Now we need to create a FileInfo object to hold our file (thus the System.IO import above). We also need to create an empty AttachmentItemInfo that we can build up. So add the following variables to your function:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim thefile As FileInfo = New FileInfo(sFilePathAndName)&lt;br&gt;Dim gwAttachItemInfo(0) As GWWS.AttachmentItemInfo&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now that we've got our file and our AttachmentItemInfo, we can get started with building up our attachment.&amp;nbsp;Let's start with the easy stuff first:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;gwAttachItemInfo(0) = New GWWS.AttachmentItemInfo&lt;br&gt;&lt;br&gt;' Name&lt;br&gt;gwAttachItemInfo(0).name = thefile.Name &lt;br&gt;&lt;br&gt;'Date&lt;br&gt;gwAttachItemInfo(0).date = _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Format(thefile.CreationTimeUtc, "yyyy-MM-ddTHH:mm:ddZ")&lt;br&gt;gwAttachItemInfo(0).dateSpecified = True&lt;br&gt;&lt;br&gt;'Hide attachment?&lt;br&gt;If bHideAttachment = True Then&lt;br&gt;&amp;nbsp;&amp;nbsp; gwAttachItemInfo(0).hidden = True&lt;br&gt;&amp;nbsp;&amp;nbsp; gwAttachItemInfo(0).hiddenSpecified = True&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Ok, that's the end of the EASY stuff, we instantiated the&amp;nbsp;AttachmentItemInfo object, assigned a name, a date and marked it hidden, if requested.&amp;nbsp;The rest of it gets a little more complicated. Now we need to import the&amp;nbsp;binary data&amp;nbsp;for the image file and add it to the data of the attachment. If you remember back at the very start of the post, you'll remember that I mentioned 2 things didn't work. Well this is #1 that didn't work. I originally started with the code from the example application included in the SDK for GroupWise Soap. Unfortunately the GetAttachment code in their example used a string array to receive the filestream. This resulted in only the information up to the first space being retrieved (in the case of my gif file, "GIF89Aw"). Nothing else. For the longest time I couldn't figure out what was going on. Once I finally realized that the code pretty much only works for text attachments, I had to find something different. Gotta love the internet and copy and paste inheritance though. I found some code, an made some heavy modifications that resulted in some&amp;nbsp;code that DOES work to import the image's data to the AttachmentItemInfo's data property. Add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;'Data&lt;br&gt;Dim fsData As FileStream = thefile.OpenRead()&lt;br&gt;Dim gwByte(fsData.Length) As Byte&lt;br&gt;fsData.Read(gwByte, 0, fsData.Length)&lt;br&gt;fsData.Close()&lt;br&gt;gwAttachItemInfo(0).data = gwByte&lt;br&gt;&lt;br&gt;Return gwAttachItemInfo(0)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we create a FileStream object and assigned it to our image's file stream. Next, we create an array of bytes that is as long as our FileStream, and then read the FileStream into the byte array (starting at 0 and going to the end). Next, we close the FileStream object, add our byte array as the data property of the attachment. And finally, we return our AttachmentItemInfo object so we can continue to manipulate it.&lt;/p&gt; &lt;p&gt;Ok, so now we've created our attachment and returned it to our ConvertHtmlMessageToAttachmentArray (CHMTAA, I'll use this from now on, it's shorter...)&amp;nbsp;function. Now we want to do some processing to the attachment so that it conforms to the standards we need it to. If you remember from the beginning, the Novell documentation states, "&lt;em&gt;Each of the accompanying attachments has a contendId element..&lt;/em&gt;" This means that we need to assign an contentID to each attachment. This contentID needs to be unique (or it will pickup the first attachment with the correct contentID and use that for all pictures). We also need to replace the filepath in our HTML code with our new contentID so that it can be located. And we'll also set the contentType so that it can be treated as it should. Add the following inside your For Each loop, after the 'theAttachment' declaration:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;theAttachment.contentId = iCount &amp;amp; "." &amp;amp; Path.GetFileName(sSrc)&lt;br&gt;theAttachment.contentType = "image/" &amp;amp; sSrc.Substring(sSrc.Length - 3)&lt;br&gt;sMessageToConvert = sMessageToConvert.Replace(matchSrc.Value,&amp;nbsp;_&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "src=" &amp;amp; Chr(34) &amp;amp; "cid:" &amp;amp; theAttachment.contentId &amp;amp; Chr(34))&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we&amp;nbsp;assign a contentID. We can't just use the filename because we may have it in our message more than once. So we use a counter variable to make a differentiation. Next, we&amp;nbsp;tell it that our content is an image of type X (whatever the extension is i.e. gif, jpg). Finally, we take our full HTML message and replace the src="xyz" statement with a custom one. This will specify that our src should use the contentID instead of a file path. We do that by adding 'cid:' before our contentId so our final src should look like src="cid:1.xyz.gif". Remember our previous src had the full filepath, now we shorten it to just the filename and the cid directive. Now we can add our attachment to our list of attachments and increment our counter. Add the following to finish out our For Each loop:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;theAttachments.Add(theAttachment)&lt;br&gt;iCount += 1&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically our For Each loop will cycle through each of the embedded images, create it as an attachment, pack it up as we like, modify our HTML to reflect the new src&amp;nbsp;and then add the attachment to&amp;nbsp;our attachment&amp;nbsp;list. The last thing we need to do is use our modified HTML to create an attachment and create it as the first attachment in our message's attachments array. We're going to create another helper function, one that will take our HTML text, create an attachment with the correct name and return that. So jump out of our CHMTAA function and create a new function as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Function ConvertHtmlMessageToAttachment _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (ByVal sMessageToConvert As String) As GWWS.AttachmentItemInfo&lt;br&gt;&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Our function will take the text of our HTML message, and return an AttachmentItemInfo object that we can add to our attachments list in our CHMTAA function. We need to create two objects to start with, an AttachmentItemInfo that we can pack up and send back, and an encoding object so we can easily convert our string to a byte array. Add the following variable declarations to your code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwAttachmentItemInfo As New GWWS.AttachmentItemInfo&lt;br&gt;Dim encoding As System.Text.Encoding = System.Text.Encoding.UTF8&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;If you want to know more about the encoding object, you should check out &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;Part 3&lt;/a&gt; of this series. Now we just need to&amp;nbsp;pack up our attachment object and send it back. Add the following code to your subroutine to finish it out:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;gwAttachmentItemInfo.name = "text.htm" &lt;br&gt;gwAttachmentItemInfo.date = Format _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (Date.Now(), "yyyy-MM-ddTHH:mm:ddZ")&lt;br&gt;gwAttachmentItemInfo.dateSpecified = True&lt;br&gt;gwAttachmentItemInfo.data = encoding.GetBytes(sMessageToConvert)&lt;br&gt;gwAttachmentItemInfo.hidden = True&lt;br&gt;gwAttachmentItemInfo.contentType = "text/html"&lt;br&gt;&lt;br&gt;Return gwAttachmentItemInfo&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;According to our Novell documentation: "&lt;em&gt;HTML messages are returned as the first attachment and are named text.htm&lt;/em&gt;." So we name it accordingly. Next we assign a date and let it know that a date has been specified. Then we use our encoding object to convert our HTML text to a byte array and assign that as the attachment's data. We should also mark our attachment as hidden, and finally we should mark the attachment's contentType as being html (why it doesn't recognize that and open in the correct compose mode I don't know but...). And finally we return our object to the calling procedure.&lt;/p&gt; &lt;p&gt;Ok, let's go back to our CHMTAA function one last time and finish it out. We need to get our HTML attachment and add it as the first item in our list, and then return our list as an array to the CreateMessage subroutine. Easily done with the very cool generic list. Add the following two lines to your CHMTAA function AFTER the For Each loop:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;theAttachments.Insert _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; (0, ConvertHtmlMessageToAttachment(sMessageToConvert))&lt;br&gt;Return theAttachments.ToArray()&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Using the built-in features or our list of attachments, we request an HTML attachment and insert it in the first position of the list. Next we&amp;nbsp;call the ToArray() function of the list and return the resulting array.&lt;/p&gt; &lt;p&gt;OK, we've covered a lot of ground, but we're still not quite done. We still need to modify our CreateMessage&amp;nbsp;subroutine so that it can handle the HTML. We will leave all the existing code (since it is recommended that we assign a text version of our HTML to the message body). All we need to do is add the attachment array. In your CreateMessage subroutine that we created in &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;Part 3&lt;/a&gt;, add the following right after the gwMail.message = gwMessageParts statement:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;If chkHtmlMessage.Checked = True Then&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; gwMail.attachments = _ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ConvertHtmlMessageToAttachmentArray(txtMessage.Text)&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;If our HTML checkbox is checked, then we simply pass the HTML text to our CHMTAA function and assign the attachment array that is returned to the attachments property of our message.&lt;/p&gt; &lt;p&gt;Go ahead and run your application and try to send and HTML message with and embedded image. Here's the HTML code that I used to create my HTML message for testing purposes:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;html&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;head&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;title&amp;gt;&amp;lt;/title&amp;gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/head&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;body&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;table border=1&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;lt;p align=center&amp;gt;Cell1&amp;lt;/p&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;tr&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;td&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;p align=center&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;img src="C:\My Pictures\xyz.jpg" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;/p&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;/td&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;/tr&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;/table&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;img src="C:\My Pictures\abc.gif" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;br&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;lt;img src="C:\My Pictures\subfolder\abc.gif" /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;/body&amp;gt;&lt;br&gt;&amp;lt;/html&amp;gt; &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;You'll notice that it has 3 images, xyz.jpg and two labeled abc.gif. In my test, the two abc.gif files had the same name but were different images and were in different folders. This is how I determined I needed to use the counter variable in the contentID creation above.&lt;/p&gt; &lt;p&gt;Hopefully your program will work as you want it to. If you're having difficulty with it open as a text file, then check out the beginning paragraph's lament about item #2.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;&lt;/p&gt; &lt;p&gt;Phew. We covered a bunch of stuff in this post. Now I can create my templates to my hearts content and have them added automatically to people's work in progress folders and they can have full formatting because I can use HTML code to do it (I wonder if I can embed JavaScript this way?) (NOTE: you can embed JavaScript this way, but GWclient doesn't process it).&lt;/p&gt; &lt;p&gt;One of the diagnostic tools that you can use to find problems (and trust me I had lots of problems I had to find) is a trace file. This will give you the xml that the soap functionality sent to the server and the response that it sent back. I had a heck of time getting tracing to work, so I may write a part 5 that goes over what I did to get it to work, however, this concludes what we set out to do. We created a draft item, put it in the users work in progress folder and even added HTML functionality. Go take a well earned break.&lt;/p&gt; &lt;p&gt;As a side note, I'm trying beef up on my bloggins skills, so...&lt;/p&gt; &lt;p&gt; &lt;div id="simpleology_blog_04aa51825866cc82c528968419529c9d"&gt; &lt;p&gt;I'm evaluating a &lt;a href="http://www.simpleology.com/training/blogging/index.php"&gt;multi-media course on blogging&lt;/a&gt; from the folks at Simpleology. For a while, they're letting you &lt;b&gt;&lt;a href="http://www.simpleology.com/training/blogging/index.php"&gt;snag it for free&lt;/a&gt;&lt;/b&gt; if you post about it on your blog.&lt;/p&gt; &lt;p&gt;It covers:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The best blogging techniques.  &lt;li&gt;How to get traffic to your blog.  &lt;li&gt;How to turn your blog into money.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;I'll let you know what I think once I've had a chance to check it out. Meanwhile, go grab yours while it's still free.&lt;/p&gt;&lt;/div&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;;subject=VB.Net+and+GroupWise+Soap+Pt.+4+-+HTML+Messages" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+4+-+HTML+Messages" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;title=VB.Net+and+GroupWise+Soap+Pt.+4+-+HTML+Messages" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+4+-+HTML+Messages" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+4+-+HTML+Messages&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/10/04/VB.Net-and-GroupWise-Soap-Pt.-4-_2D00_-HTML-Messages.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=35426" width="1" height="1"&gt;</description><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/Asp.net/default.aspx">Asp.net</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Soap/default.aspx">GroupWise Soap</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Web+Services/default.aspx">GroupWise Web Services</category></item><item><title>VB.Net and GroupWise Soap Pt. 3 - Creating a Message</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx</link><pubDate>Tue, 04 Sep 2007 23:53:37 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:34459</guid><dc:creator>Yougotiger</dc:creator><slash:comments>1</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/34459.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=34459</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=34459</wfw:comment><description>&lt;p&gt;&lt;/p&gt; &lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;In my last post, I talked about accessing GroupWise via the SOAP services that they provide. Specifically, we looked at getting a folder list. In this post&amp;nbsp;I want to expand upon the application that we started and add some more functionality.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Our problem, continued from the last post, was to figure out how to login and create a draft message in the user's Work in Progress folder that is a template of something the company uses currently in hard copy. In this article, I'm going to look specifically at how to create a message as&amp;nbsp;a draft, and place it in a specific folder. Technically this will meet the requirements I set forth in the first post, but (and there's always a but isn't there?), I want to create an&amp;nbsp;HTML draft message. The HTML template will not be covered in this post, and for that matter I haven't gotten it to work yet, so if anyone has any suggestions, please let me know.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;We will be building on the application that we started last time. If you haven't created the login procedures from &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;Part 1&lt;/a&gt;&amp;nbsp;and the folder listing subroutines from&amp;nbsp;&lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;Part 2&lt;/a&gt;, I suggest you do it now, we will need to login and we need to be able to get a folder's ID.&lt;/p&gt; &lt;p&gt;We actually have very little in the way of preparation. We simply need to add some controls so that we can create a message. We won't create any new helper functions yet.&amp;nbsp;Let's&amp;nbsp;add&amp;nbsp;two more&amp;nbsp;buttons, name one btnSendMessage, set the text to 'Send Message' and name the other btnCreateMessage and set it's text to 'Create Message'. For both buttons, set their enabled property to false.&amp;nbsp;Next create&amp;nbsp;two text boxes, txtSubject and txtTo and create one multiline text box and name it txtMessage (you may want to add labels to go with them).&lt;/p&gt; &lt;p&gt;One last thing to do is to add our new buttons to the ToggleButtons() function. Just below btnListFolders.Enabled = True, add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;btnSendMessage.Enabled = True&lt;br&gt;btnCreateMessage.Enabled = True&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and add the opposite just below btnListFolders.Enabled = False, add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;btnSendMessage.Enabled = False&lt;br&gt;btnCreateMessage.Enabled = False&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, this just ensures that we can't attempt to send or create a message on the server without being logged in first.&lt;/p&gt; &lt;p&gt;As you can guess, there are two methods in GroupWise SOAP that we can use to create messages. I am choosing to show both, because they create things a little bit different. Let's start with SENDING a message first, then we'll take a short look at creating one since it is very similar. Start by creating a new subroutine for sending a message:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Sub SendMessage()&lt;br&gt;&lt;br&gt;End Sub &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Hook up your btnSendMessage button's click event to this subroutine. Now we want to get started with our message. To begin with, we'll need some specific objects such as a sendItemRequest and a sendItemResponse. We'll also need to create a Mail item to send as a parameter in our request. Define them as follows in your subroutine:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwMail As New GWWS.Mail&lt;br&gt;Dim gwSendReq As New GWWS.sendItemRequest&lt;br&gt;Dim gwSendResp As New GWWS.sendItemResponse&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This creates and instantiates all three of our necessary pieces. The sendItemRequest expects us to pass in an item (an appointment, or a mail item for example) and then it will process the item and return a response.&lt;/p&gt; &lt;p&gt;Now we're ready to begin loading our mail item with our settings. Let's start with the easy stuff first.:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;gwMail.subject = txtSubject.Text&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Ok, that's the end of the EASY stuff, the rest of it gets a little more complicated. It can't be as easy as just packing up the information and sending it, NOOO, we need to convert things to arrays and add the arrays. Some are easier than others. The first one we'll tackle is one of the more difficult ones to figure out but once you figure it out, it isn't really all that bad.&amp;nbsp;Lets start by creating a new helper function, define it as follows:&amp;nbsp;&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Function ConvertStringToMessagePart(ByVal sStringToConvert _&lt;br&gt;&amp;nbsp; &amp;nbsp;As String) As GWWS.MessagePart&lt;br&gt;&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We cannot just assign the text of our message box to the message, we actually need to encode it as an array of bytes, add it to a MessagePart object, and then add&amp;nbsp;the MessagePart object&amp;nbsp;to the MessageParts array. This function will be used to bring in our message text,&amp;nbsp;convert it into a byte array, add it to a MessagePart and then return the finished MessagePart. First we need to create an encoding object and then create the MessagePart to return. Add the following to your subroutine:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim encoding As System.Text.Encoding = System.Text.Encoding.UTF8&lt;br&gt;Dim gwMessagePart As New GWWS.MessagePart&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we create an encoding object. Then we assign it UTF8 encoding. This will encode our message into the ASCII encoding it's expected to be in. Second we create&amp;nbsp;a new MessagePart object. Now we need to load it up with the byte array containing our message text and return it. Add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;gwMessagePart.Value = encoding.GetBytes(sStringToConvert)&lt;br&gt;gwMessagePart.length = 0&lt;br&gt;Return gwMessagePart&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We use our encoding object to return an array of bytes by using the GetBytes function. Basically, we return an array of ASCII values, one element for each character of the string we're converting. This array is then assigned to the MessagePart we created. Now it might seem a little strange to set the length of the MessagePart to 0, and you'd be right, but apparently there is a bug in the server code, so if you set it manually, your message get's cut short. I believe the documentation online instructs you to set it manually, but it doesn't work, you'll get your message, but about 1/3 of your message will be missing. So we set it to 0 and the server will do the calculation itself (easier anyhow...). Finally we return the MessagePart we just created.&lt;/p&gt; &lt;p&gt;Now we're ready to use helper function in our main function. Add the following to your SendMessage subroutine code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwMessageParts(0) As GWWS.MessagePart&lt;br&gt;gwMessageParts(0) = ConvertStringToMessagePart(txtMessage.Text)&lt;br&gt;gwMail.message = gwMessageParts&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We start by creating an array of MessageParts with one element (Honestly, I'm not sure the benefit of having multiple message parts, anyone out there know?). Next we assign the encoded message text from the form, returned as a MessagePart to the first element of the array. Finally, we assign our MessageParts array to the message property of our mail item. Next we'll want to address our message&amp;nbsp;so it can go into somebody's folder. We need to create 2 objects for addressing, one for the distribution list and one an array for the recipients. Add the following to your code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwDistribution As New GWWS.Distribution&lt;br&gt;Dim gwRecipients(0) As GWWS.Recipient&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;In this example, we've only allowed for one recipient, thus we created a recipients array with a single element. Now we want to create a single recipient, load it up and add all the pieces together. Add the following lines to your code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwRecipient As New GWWS.Recipient&lt;br&gt;gwRecipient.email = txtTo.Text&lt;br&gt;gwRecipient.distType = GWWS.DistributionType.TO&lt;br&gt;gwRecipients(0) = gwRecipient&lt;br&gt;gwDistribution.recipients = gwRecipients&lt;br&gt;gwMail.distribution = gwDistribution&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we create a new recipient object, then we load its properties with the email address and the distribution type. Next, we add our recipient to the recipients array (ok, this array makes sense to me, you may easily have more than one recipient...). Finally we add the recipient array&amp;nbsp;to the distribution and assign our distribution to the mail item. (Phew..)&lt;/p&gt; &lt;p&gt;Technically we are now ready to&amp;nbsp;send the item, we just need to add some code to do the actual sending but, our item is ready for delivery as it is. So let's add that code and then go back and add in some options. Add the following code:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;gwSendReq.item = gwMail&lt;br&gt;gwSendResp = wsGWBinding.sendItemRequest(gwSendReq)&lt;br&gt;If gwSendResp.status.code = 0 Then&lt;br&gt;&amp;nbsp;&amp;nbsp;ReportStuff("Send Mail Status: Success.")&lt;br&gt;Else&lt;br&gt;&amp;nbsp;&amp;nbsp;ReportStuff("Send Mail Status: Failure, Code: " &amp;amp; gwSendResp.status.code) End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;So we assign our mail item to the item property of the sendItemRquest object we created, and then use the sendItemReqeust to send our item. We capture the returned sendItemResponse and parse it to see if we succeeded in sending our message and report. Run your program and test it. Send a message twice, once to yourself and once to a co-worker. It should successfully send a message to both yourself and the co-worker. Where do the messages end up?&amp;nbsp;Both messages are successfully sent to the recipient's inbox.&lt;/p&gt; &lt;p&gt;Ok, now we want to add some options. We wanted to do two things that we haven't done yet. We wanted to create the item as a draft, and we wanted to create it in the 'Work In Progress' folder. So let's start by making it draft, perhaps that will automatically put it in the Work In Progress folder? (no such luck, but you can think that for a moment if you want to...). Add the following code to specify that we have a draft message and put it after our recipient section but before our sending section:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwItemSource As GWWS.ItemSource&lt;br&gt;gwItemSource = GWWS.ItemSource.draft&lt;br&gt;gwMail.source = gwItemSource&lt;br&gt;gwMail.sourceSpecified = True&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We&amp;nbsp;create an ItemSource object and then assign it to be a draft message. Next we add it to the source property of our mail item and then let the item know that ItemSource is specified.&lt;/p&gt; &lt;p&gt;Go ahead and run your application again try to send it the same two ways (once with your email address as the recipient, and then try it with a co-worker's as the recipient). What happens? Where does the message end up? In both instances, the message will show up as a draft in &lt;u&gt;YOUR&lt;/u&gt; mailbox. There are two things to note here. 1) Even though you specified a co-worker as the recipient, the message was &lt;strong&gt;sent&lt;/strong&gt; to &lt;u&gt;YOUR&lt;/u&gt; inbox, and 2) labeling the item as draft did NOT put it into the 'Work In Progress' folder automatically. Ok so why does the co-worker's email send correctly before but end up in your inbox now? Well, that's because it is a draft, you can't SEND a draft to someone else, you have to complete it (thus your co-worker's email in your inbox). So it shows up in your inbox. So why the inbox? Well, that's related to #2 and we'll talk about that a little more in a second. First, let's try to specify a folder to create the new item in. Add the directly&amp;nbsp;after the ItemSource section:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim gwContainerRef(0) As GWWS.ContainerRef&lt;br&gt;gwContainerRef(0) = New GWWS.ContainerRef()&lt;br&gt;gwContainerRef(0).Value = GetFolderIDByName("Work In Progress")&lt;br&gt;gwMail.container = gwContainerRef&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;By now you should be getting used to the array concept. We create a container reference object, as a single element array of ContainerRef. Then we initialize the element with a concrete object. Then we access the value property and assign in the ID of the folder we want to create the message in (Work in Progress) using the helper function we created in the last exercise (Part 2). Finally, we assign our containerRef array to the mail item's container property. Run it again and just send one to yourself this time. What happens? The message still ends up in the inbox. Why? The simple answer is this, you SENT it. If you send an email, you cannot specify where the recipient will receive it. Even though you sent it to yourself, you SENT it and&amp;nbsp;because it was SENT to you&amp;nbsp;it bypasses the container information and DELIVERS it to the inbox (as opposed to creating it).&lt;/p&gt; &lt;p&gt;So how do we create a message in a folder? Well there is another method for that. I alluded to it before. We CREATE an item rather than sending, and then we can specify where it goes. Realistically, all the code is pretty much the same with the exception that we need to change all our sentItem stuff (req, response, etc.) to CREATE (i.e. copy and paste, then change). Rather than try to specify all the lines that need changed, I'll just post the final CreateMessage subroutine code. It looks like this:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Sub CreateMessage()&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwMail As New GWWS.Mail&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwDistribution As New GWWS.Distribution&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwMessageParts(0) As GWWS.MessagePart&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwCreateReq As New GWWS.createItemRequest&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwCreateResp As New GWWS.createItemResponse&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.subject = txtSubject.Text&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMessageParts(0) = ConvertStringToMessagePart(txtMessage.Text)&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.message = gwMessageParts&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwRecipients(0) As GWWS.Recipient&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwRecipient As New GWWS.Recipient&lt;br&gt;&amp;nbsp;&amp;nbsp; gwRecipient.email = txtTo.Text&lt;br&gt;&amp;nbsp;&amp;nbsp; gwRecipient.distType = GWWS.DistributionType.TO&lt;br&gt;&amp;nbsp;&amp;nbsp; gwRecipients(0) = gwRecipient&lt;br&gt;&amp;nbsp;&amp;nbsp; gwDistribution.recipients = gwRecipients&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.distribution = gwDistribution &lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwItemSource As GWWS.ItemSource&lt;br&gt;&amp;nbsp;&amp;nbsp; gwItemSource = GWWS.ItemSource.draft&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.source = gwItemSource&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.sourceSpecified = True&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim gwContainerRef(0) As GWWS.ContainerRef&lt;br&gt;&amp;nbsp;&amp;nbsp; gwContainerRef(0) = New GWWS.ContainerRef()&lt;br&gt;&amp;nbsp;&amp;nbsp; gwContainerRef(0).Value = GetFolderIDByName("Work In Progress")&lt;br&gt;&amp;nbsp;&amp;nbsp; gwMail.container = gwContainerRef&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; gwCreateReq.item = gwMail&lt;br&gt;&amp;nbsp;&amp;nbsp; gwCreateResp = wsGWBinding.createItemRequest(gwCreateReq)&lt;br&gt;&amp;nbsp;&amp;nbsp; If gwCreateResp.status.code = 0 Then&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ReportStuff("Create Mail Status: Success.")&lt;br&gt;&amp;nbsp;&amp;nbsp; Else&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; ReportStuff("Create Mail Status: Failure, Code: " &amp;amp; _ &lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; gwCreateResp.status.code)&lt;br&gt;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;End Sub&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Assign the click event of your btnCreateMessage to this subroutine. Run your program and rather than send a message, create a message. Create one to yourself and one to your co-worker. What happens? Where does the message end up? Yes, both are created as draft message and both end up in YOUR 'Work In Progress' folder (remember that draft thing). Even if you take the draft&amp;nbsp;and folder section out of the subroutine, we cannot CREATE a message in somebody else's account. So if you comment out the Draft and Folder portion above and send to a co-worker, guess what?&amp;nbsp;A new message appears in YOUR inbox.&lt;/p&gt; &lt;p&gt;As an FYI, creating a message actually bypasses the Rules&amp;nbsp;that you have setup (under Tools-&amp;gt;Rules). This is because the item is CREATED in the folder, rather than sent. There isn't a rule event to act on CREATED items, just RECEIVED Items.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;&lt;/p&gt; &lt;p&gt;We've come a long way towards our goal, we've created a message, created it as a draft and finally, we've got it to go into the folder that we specify. Like I said earlier, technically we've satisfied the requirements set forth in Part 1, but I'd like to make the draft&amp;nbsp;mail item an HTML message.&amp;nbsp;I'd like to say that I'll show you next time how to create an HTML draft message, but unfortunately, it hasn't been working for me. When I figure it out, there'll be a Part 4, I just don't know when that'll be.&lt;/p&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;;subject=VB.Net+and+GroupWise+Soap+Pt.+3+-+Creating+a+Message" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+3+-+Creating+a+Message" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;title=VB.Net+and+GroupWise+Soap+Pt.+3+-+Creating+a+Message" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+3+-+Creating+a+Message" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+3+-+Creating+a+Message&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/09/04/VB.Net-and-GroupWise-Soap-Pt.-3-_2D00_-Creating-a-Message.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=34459" width="1" height="1"&gt;</description><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/Asp.net/default.aspx">Asp.net</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Soap/default.aspx">GroupWise Soap</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Web+Services/default.aspx">GroupWise Web Services</category></item><item><title>VB.Net and GroupWise Soap Pt. 2 - Getting a Folder List</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx</link><pubDate>Wed, 22 Aug 2007 21:10:23 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:34068</guid><dc:creator>Yougotiger</dc:creator><slash:comments>2</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/34068.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=34068</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=34068</wfw:comment><description>&lt;p&gt;&lt;/p&gt; &lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;In my last post, I talked about accessing GroupWise via the SOAP services that they provide. Specifically, we looked at logging in. In this post we want to expand upon the application that we started and add some more functionality.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Our problem, continued from the last post was to figure out how to login and create a draft message in the user's Work in Progress folder that is a template of something the company uses currently in hard copy. In this article, I'm going to look specifically at how to do some work with the folders. This is necessary so that we can place our message in the correct folder once we create the draft message. I plan to do future articles on some of the other information that I find and that I figure out.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;We will be building on the application that we started last time. If you haven't created the login procedures from &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;Part 1&lt;/a&gt;, I suggest you do it now, we will need to login.&lt;/p&gt; &lt;p&gt;To start, let's add the controls to the form that we will use in this exercise. We need to add another button, name it btnListFolders, set the text to 'List Folders' and finally, set enabled to false (yes, false). Next create a multiline text box and name it txtResults (you may want to add a label to go with it.&lt;/p&gt; &lt;p&gt;Now let's also add a couple of helper functions that we can reuse them over and over. We want to add two&amp;nbsp;functions, one called ReportStuff() and one called ToggleButtons() the are defined as:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Sub ReportStuff(ByVal sCurrentStatus As String)&lt;br&gt;&amp;nbsp;&amp;nbsp; txtResults.Text &amp;amp;= sCurrentStatus &amp;amp; vbCrLf&lt;br&gt;End Sub&lt;br&gt;&lt;br&gt;Private Sub ToggleButtons(ByVal bLoggedIn As Boolean)&lt;br&gt;&amp;nbsp;&amp;nbsp; If bLoggedIn = True Then&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; btnListFolders.Enabled = True&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; tslblCurrentStatus.Text = "Logged In..."&lt;br&gt;&amp;nbsp;&amp;nbsp; Else&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; btnListFolders.Enabled = False&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; tslblCurrentStatus.Text = "Logged Out"&lt;br&gt;&amp;nbsp;&amp;nbsp; End If&lt;br&gt;End Sub &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, we just pass stuff to our report stuff so that we can track what's happening. It will print what we pass it to the text box. The ToggleButtons subroutine we'll call so that we can enable and disable buttons and give feedback regarding our login status.&lt;/p&gt; &lt;p&gt;We'll make some minor changes to our login and logout functions to incorporate our new helper functions. In LoginUser() add the following line just before the 'If 0 = resp.status.code then' line:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ReportStuff("Login status: " &amp;amp; resp.status.code.ToString())&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and as the last line in the if then block we were just referencing, add the following line:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ToggleButtons(True)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Finally, add the following line inside our if then block in LogoutUser():&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ToggleButtons(False)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Ok, now we're ready to add our folder functionality. We're going to look at two different pieces of functionality regarding folders, first, listing them all, and second, getting the ID of a particular folder. We'll create our listing function, then reuse it's code to create the other.&lt;/p&gt; &lt;p&gt;To get started, create a new subroutine, and name it GetFolders(). As is the pattern with GroupWise Soap objects, we'll create a request object, then pass it to a function and get back a response object. So let's start with our variables. Add the following to the top of your subroutine:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim flReq As New GWWS.getFolderListRequest()&lt;br&gt;Dim flResp As GWWS.getFolderListResponse&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now that we've created our objects, let's start populating them. It doesn't really take much to get a folder list back. Add the following to our subroutine:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;flReq.recurse = True&lt;br&gt;flReq.parent = "folders"&lt;br&gt;&lt;br&gt;flResp = wsGWBinding.getFolderListRequest(flReq)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Our settings simply tell the server we want to recurse and return&amp;nbsp;all the folders, not just the direct children folders, and then we have to specify where to start by setting the parent folder. We want to start at the "folders" folder. That will give us ALL the folders, &lt;u&gt;including&lt;/u&gt; the home folder. We then make the request to the server and get back a response.&lt;/p&gt; &lt;p&gt;Now we need to go through the folder list and list the folders. We'll just have the list pumped out to the txtResults box using our new ReportStuff function. First we need to check that our response wasn't somehow messed up and that we have a valid folder response object. Add a bit of reporting and&amp;nbsp;an If Then as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ReportStuff("Folders:")&lt;br&gt;If IsNothing(flResp.folders) = False Then&lt;br&gt;&lt;br&gt;end If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This just checks that we aren't working with an empty object. Next, we'll add some&amp;nbsp;additional reporting and set up a loop to go through our&amp;nbsp;response object.&amp;nbsp;Add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ReportStuff("Listing the " &amp;amp; flResp.folders.Length &amp;amp; " folders found:")&lt;br&gt;For Each theFolder As GWWS.Folder In flResp.folders&lt;br&gt;&lt;br&gt;Next&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Finally, we'll&amp;nbsp;create a basic For Each loop and loop through all the folder objects in the folders collection. All that is left is for us to do our reporting for each folder we parse. Add the following reporting line:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ReportStuff(theFolder.name &amp;amp; " " &amp;amp; theFolder.description &amp;amp; _&lt;br&gt;&amp;nbsp;&amp;nbsp; " " &amp;amp; theFolder.id)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Call your GetFolders subroutine from your btnListFolders click event. We need to make sure that we don't check our folder list unless we're logged in, and that's where the toggle buttons comes in. We've already set the application to not allow you to click the List Folders button if we are not logged in. Run your application, login and then click the List Folders button. You should get a&amp;nbsp;list of the folder names, and ID's. "But we asked for the folder description," you say. Sure, but most, if not all, of the folders don't have a description so it shows blank, and then we have a horribly cryptic folder ID at the end. This ID&amp;nbsp;is what we need if we want to create an email in a specific folder. Passing the name of the folder doesn't work, we need to pass the folder ID.&lt;/p&gt; &lt;p&gt;With our GetFolders() sub working correctly, let's look at getting the ID for a specific folder. The Function to return the ID of a specified folder is basically the same as the one we created above, but without most of the reporting tied in. We'll create a new Function as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private Function GetFolderIDByName(ByVal folderName As String) As String&lt;br&gt;&lt;br&gt;End Function&lt;br&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now&amp;nbsp;copy and paste our GetFolders() subroutine, and remove our reporting elements. We will also remove the existing code inside our For Each block. We'll replace it with an If Then that compares the folder's name with what we pass in. If they match, we return the folder's ID (not the name). Add the following lines in our For Each block:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;If theFolder.name.ToLower() = folderName.ToLower() Then&lt;br&gt;&amp;nbsp;&amp;nbsp; Return theFolder.id&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now we just need to hook it up. Add a line to your btnListFolder's click event (or replace your line that calls GetFolders()) as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ReportStuff(GetFolderIDByName("Work In Progress"))&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Run your app, Login and press your List Folders button. We get the ID of the Work in Progress folder.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;&lt;/p&gt; &lt;p&gt;Using the web services for GroupWise&amp;nbsp;isn't exactly intuitive. Once you get an understanding of the Request/Response model it gets much easier to use. Documentation could still be a little better letting us know what each of the properties and methods are and&amp;nbsp;do specifically, but we work with what we have. In the next part I think we'll look at actually creating a message now that we can reference the folders by ID.&lt;/p&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;;subject=VB.Net+and+GroupWise+Soap+Pt.+2+-+Getting+a+Folder+List" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+2+-+Getting+a+Folder+List" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;title=VB.Net+and+GroupWise+Soap+Pt.+2+-+Getting+a+Folder+List" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+2+-+Getting+a+Folder+List" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+2+-+Getting+a+Folder+List&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/22/VB.Net-and-GroupWise-Soap-Pt.-2-_2D00_-Getting-a-Folder-List.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=34068" width="1" height="1"&gt;</description></item><item><title>VB.Net and GroupWise Soap Pt. 1 - Getting Logged In</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx</link><pubDate>Thu, 09 Aug 2007 17:22:01 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:33721</guid><dc:creator>Yougotiger</dc:creator><slash:comments>3</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/33721.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=33721</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=33721</wfw:comment><description>&lt;p&gt;&lt;/p&gt; &lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;One of the things that Microsoft does really well is cater to their developers. If you don't know how to do something, Microsoft is pretty decent about giving you ample&amp;nbsp;sample code to&amp;nbsp;learn it. And if that's not comprehensible (sometimes it's kind of cryptic), then somebody has probably written about it somewhere and it's just a matter of finding that someone's article. I can't say the same about Novell. Now don't get me wrong, I'm not anti-Novell, all our file servers are Novell and our email system is Novell GroupWise, which I really like, but they don't make it easy for their developers like Microsoft does. GroupWise's latest version introduced some web services that you can tap into and use to do all the things that their GW client can do (ok, not quite all&amp;nbsp;yet). But documentation leaves much to be desired and there is VERY little sample code out there to help you figure it out. I had a request from one of our users&amp;nbsp;for creating templates in GroupWise, and figured that I could use the web services to create some templates in the user's draft (work in progress) folder.&lt;/p&gt; &lt;p&gt;I know that I'm possibly limiting the readership of this particular article series, but I figured since the information isn't out there, I'd share what I've found and hopefully it will help someone else like me.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Figure out how to login and create a draft message in the user's Work in Progress folder that is a template of something the company uses currently in hard copy. In this article, I'm going to look specifically at how to login to the GroupWise system. I plan to do future articles on some of the other information that I find and that I figure out.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;Ok, to get started, create a VB.Net application. I would guess that we could do this almost as easily with ASP.Net, but for the sake of ease (not having to preserve state), let's just use a windows app. On the form, add 4 Labels, 4 TextBoxes, 2 Buttons and a StatusStrip. On the StatusStrip, add a label and name it lblCurrentStatus. The rest of the controls, configure as follows:&lt;/p&gt; &lt;p&gt; &lt;table cellspacing="1" cellpadding="1"&gt;  &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;&lt;strong&gt;&lt;u&gt;Control Type&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;&lt;strong&gt;&lt;u&gt;Name The&amp;nbsp;Control...&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;&lt;strong&gt;&lt;u&gt;and Set Text To...&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Label&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;lblPOAAddress&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Post Office Agent Address:&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Label&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;lblPOAPort&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Post Office Agent Port:&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Label&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;lblUsername&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Username:&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Label&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;lblPassword&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Password:&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;TextBox&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;txtPOAAddress&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;TextBox&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;txtPOAPort&lt;/p&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;TextBox&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;txtUsername&lt;/p&gt;&lt;/td&gt; &lt;td&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;TextBox&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;txtPassword&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Button&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;btnLogin&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Login&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p align="center"&gt;Button&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;btnLogout&lt;/p&gt;&lt;/td&gt; &lt;td&gt; &lt;p align="center"&gt;Logout&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/p&gt; &lt;p&gt;Ok, now we also need to have the Novell GroupWise Services configured on the server, and we'll need to download the Novell Development Kit for GW Soap. We also need to find out the IP address or DNS address for the Post Office Agent (POA), and the port number it listens on (typically 7191). Once you download and install the SDK, you can add a web reference and point it to the&amp;nbsp;WSDL&amp;nbsp;file on your hard drive. Name this Web Reference GWWS.&lt;/p&gt; &lt;p&gt;Ok, now we're ready to get&amp;nbsp;into some code. Open up the code view and let's add two global variables:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private bLoggedIn As Boolean = False&lt;br&gt;Private wsGWBinding As GWWS.GroupWiseBinding&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We want to track if our application is logged in currently. If it isn't, then we'll have difficulty in future creating messages and other stuff (I tried). We also need to have a GroupWiseBinding object, this inherits the Soap protocols and handles all the back and forth with the server. These will need to remain populated until we release them so we'll create them as globals.&lt;/p&gt; &lt;p&gt;Now lets create a subroutine to log the user in. Create a sub as follow:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Sub LoginUser()&lt;br&gt;&lt;br&gt;End Sub&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We need to create some objects to work with as we create our login request. So add the following variables:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim req As New GWWS.loginRequest()&lt;br&gt;Dim resp As GWWS.loginResponse&lt;br&gt;Dim pText As GWWS.PlainText = New GWWS.PlainText()&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we create a request object, we create it using new so that we can&amp;nbsp;assign values to&amp;nbsp;it. This we pack up with our settings&amp;nbsp;and send to the server using our wsGWBinding object later. Then we create a loginResponse object to receive the response from the server. We also need a plainText authentication object. This allows us to pass username and password to the server in plain text for login. (NOTE: perhaps in a later article I'll do a not plain text one... when I figure it out...)&lt;/p&gt; &lt;p&gt;Now we need to start configuring our objects so that they can be used. So add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;wsGWBinding = New GWWS.GroupWiseBinding()&lt;br&gt;wsGWBinding.Url = "http://" &amp;amp; txtPOAAddress.Text &amp;amp; ":" &amp;amp; txtPOAPort.Text &amp;amp; "/soap"&lt;br&gt;pText.username = txtUserName.Text&lt;br&gt;pText.password = txtPassword.Text&lt;br&gt;req.auth = pText&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we initialize the GroupWiseBinding object so that we can assign stuff to it. Then we start by compiling the url to the web service and adding that to our GroupWiseBinding object. Next, we get the credentials and add them to the plainText object and then assign the plainText object to the request's auth property. Now we've got what we need to try to login, so we'll make the request. Add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;resp = wsGWBinding.loginRequest(req)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Here we make the loginRequest and pass in the loginRequest object that contains all our settings. We get back a login response object with the details of what happened. Now we need to parse the response and find out what happened. If login was successful, the response's status.code should be 0, so we'll take some action based on successful login, add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;If 0 = resp.status.code Then&lt;br&gt;&amp;nbsp;&amp;nbsp; wsGWBinding.session = New GWWS.string() 'initialize session object&lt;br&gt;&amp;nbsp;&amp;nbsp; wsGWBinding.session.Text = New String() {resp.session} 'load session&amp;nbsp;obj&lt;br&gt;&amp;nbsp;&amp;nbsp; bLoggedIn = True&lt;br&gt;&amp;nbsp;&amp;nbsp; lblCurrentStatus.Text = "Logged In..."&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;First we check to see if current status is logged in, and if so, then we'll store some of the information we go back, and do so me "housekeeping". First we'll store the session's&amp;nbsp;ID, that way we can use it in the future when we want to create a message. We store that in our GroupWiseBinding object and then it'll be passed when we use our GroupWiseBinding object again. This is where stuff get's kind of tricky.&amp;nbsp;We need to create and assign a new string object to the session, then set the session's&amp;nbsp;text property using a string array. The second line creates a new string array and assigns our response object's session to this (since session is a string).&amp;nbsp;Then finally, we&amp;nbsp;note that we are currently logged in,&amp;nbsp;and alert the user that we are logged in. Viola, logged in.&lt;/p&gt; &lt;p&gt;Now for logging out.&amp;nbsp;This is the easier part, we'll create another subroutine for logging out as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Sub LogoutUser()&lt;br&gt;&lt;br&gt;end sub&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;We also need a variable to receive the logout response:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Dim resp as GWWS.logoutResponse&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now we need to make our actual logout request, so add:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;resp = wsGWBinding.logoutRequest(New GWWS.logoutRequest)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This will send our request to the server and we will get a logout response in return.&amp;nbsp;From here, based on success or failure, we'll take some action. Again 0 indicates success so add:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;If 0 = resp.status.code Then&lt;br&gt;&amp;nbsp;&amp;nbsp; bLoggedIn = False&lt;br&gt;&amp;nbsp;&amp;nbsp; wsGWBinding = Nothing&lt;br&gt;&amp;nbsp;&amp;nbsp; lblCurrentStatus.Text = "Logged Out"&lt;br&gt;End If&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;If logout was successful, we record that we're logged out, alert the user, and then nuke our GroupWiseBinding object so that it holds no current session information. That's it.&lt;/p&gt; &lt;p&gt;Run your app, enter your POA address and port, then put in your username and password. Click login and see it logs in. It should. Then click the Logout and see if it logs out correctly.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;&lt;/p&gt; &lt;p&gt;Using the web services for GroupWise&amp;nbsp;isn't exactly intuitive, perhaps it is&amp;nbsp;under a different programming model than I'm used to with .Net, but once you figure it out it can be done. Next time&amp;nbsp;we'll look at&amp;nbsp;adding some code to return a folder list from the server after we login.&lt;/p&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;;subject=VB.Net+and+GroupWise+Soap+Pt.+1+-+Getting+Logged+In" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+1+-+Getting+Logged+In" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;title=VB.Net+and+GroupWise+Soap+Pt.+1+-+Getting+Logged+In" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+1+-+Getting+Logged+In" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx&amp;amp;;title=VB.Net+and+GroupWise+Soap+Pt.+1+-+Getting+Logged+In&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/08/09/VB.Net-and-GroupWise-Soap-Pt.-1-_2D00_-Getting-Logged-In.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=33721" width="1" height="1"&gt;</description><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/Novell+GroupWise/default.aspx">Novell GroupWise</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Soap/default.aspx">GroupWise Soap</category><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/GroupWise+Web+Services/default.aspx">GroupWise Web Services</category></item><item><title>More Page Layout Snafu's or More Stupid CSS Tricks</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx</link><pubDate>Thu, 26 Jul 2007 17:12:11 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:33295</guid><dc:creator>Yougotiger</dc:creator><slash:comments>1</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/33295.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=33295</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=33295</wfw:comment><description>&lt;p class="MyHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;I wanted to document a couple new CSS snafu's that I found in the midst of my work. If you've ever used CSS, you'll realize what a pain it can get to be if you have a lot of nested stuff. The website redesign I was (the company is opting to change their name, logo, color scheme, the works.. so I stopped redesigning) contains a multitude of nested elements. I've found it quite frustrating trying to get the CSS to display the same in both&amp;nbsp;IE and in Firefox (and for that matter, IE6 and IE7 don't always agree). In fact, most of the snafu's here&amp;nbsp;were found as a result of attempting to achieve&amp;nbsp;cross-browser compliance.&amp;nbsp;So with no further ado, here are some snafu's and how to work around them.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;Creating and&amp;nbsp;working with&amp;nbsp;CSS in Visual Studio is getting easier, it tests well in IE, but then if you turn around and test it in Firefox, it doesn't work. Most of what I'm sharing worked in one but not the other, so I had to find a work around for the CSS so it would work in both. (as a side note, I can't wait for VS '08 and their improved CSS features!)&lt;/p&gt; &lt;p class="MyHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;We'll actually build on one file for most of this post so let's get started...&lt;/p&gt; &lt;p class="MySubHeadings"&gt;How Do I Horizontally Center&amp;nbsp;in Firefox?&lt;/p&gt; &lt;p&gt;Ok for this one, create an HTML file and add the following to the header section:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;style type="text/css"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; body&lt;br&gt;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp; .header&lt;br&gt;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; border: solid 1px red;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; height: 100px;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; width: 607px;&lt;br&gt;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;lt;/style&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Ok, now add a DIV section to the body as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;div class="header"&amp;gt;I want this section centered&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now bring it up in both IE and FireFox. Notice that our red DIV section is NOT centered. How do we go about centering it horizontally? Let's try the wrong way first. Add the following to your body section of the CSS:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;text-align: center;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now refresh IE an see what happens. IE works like a charm. Notice that all text has been centered, AND the DIV as well. Now refresh it in FireFox. What happens? All the text centers, but because the DIV isn't text, it doesn't center. So how to do it? It took me a while, but I found &lt;a href="http://www.boutell.com/newfaq/creating/center.html"&gt;an article&lt;/a&gt; that told me how to do it. There is apparently something in the specifications for CSS that says margins should fill up all the remaining space in the &lt;u&gt;enclosing&lt;/u&gt; element (in this case, the body is the enclosing element to our DIV). So in the DIV's style section, add another line as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;margin: auto;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now refresh both IE and FireFox. Works in both. What about the text-align:center in the body? Well, you can leave it if you want because older IE versions will use the text-align. That is if you are ok with the text in your body being centered. This margin: auto method can also center other elements in your code. Let's say you wanted to center a table in a span, how do you horizontally center, again use the margin: auto on your table and it'll center.&lt;/p&gt; &lt;p class="MySubHeadings"&gt;Set a Height, But Let it Grow&lt;/p&gt; &lt;p&gt;Another issue that I ran into was that I created a section, and it needed to be AT LEAST a certain height, but also needed to allow it to automatically grow longer if there were more text in it. Till this project, I'd done nearly all my development in IE and it works by default there, but FireFox&amp;nbsp;isn't&amp;nbsp;quite as straight forward.&amp;nbsp;Let's continue with our current file and keep adding to it. In your DIV section, add a span section and some text as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;span class="leftCol"&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; I want to&amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; put a lot of&amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; text here&amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; so that it &amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; will go &amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; down past&amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; the bottom &amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; of the &amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; enclosing &amp;lt;br /&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; container.&lt;br&gt;&amp;lt;/span&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;(yes, leave the unused class designation for now, we'll use it later) Ok, now refresh both IE and FireFox. What happens? Well, in IE your DIV grows but FireFox, it just stays the same and the text flows right out of it. How to fix it? Well, let's try a couple of things that are a little less effective on our way to the right answer. First,&amp;nbsp;lets specify that we want the height to adjust automatically. So at the end of your .header style definition, add:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;height: auto;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and refresh in both browsers. Now they're both appear to be working, but take the text out of your span, save and refresh. It's not really working as desired, it doesn't retain it's minimum height. Since we have two height: statements in our definition, the last one wins. So how do we maintain a minimum height, but let it grow? There is a min-height: CSS attribute that we can use, so replace your height: auto with it as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;min-height: 100px;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now let's see what happens when we refresh. IE's working fine, but FireFox still isn't growing. Pretty much we've just come back to where we were before we started. Ok,&amp;nbsp;lets think it through just a little from the specifications point of view&amp;nbsp;(I don't know em, I just read about em). We need a minimum height, but min-height doesn't work in IE, it does in FireFox though. If we specify to do auto with the height, then both browsers will shrink too small. If I put a height in, IE will grow but FireFox won't. How can we combine all three to make it work? We start with a minimum height. Then we tell FireFox that we want the height to be auto. We can just tell IE a height and it'll take care of itself. Ok, so take out the stuff below your min-height statement, add the following 2 lines:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;height: auto !important;&lt;br&gt;height: 100px;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now refresh both and see what happens, now take the text out of your span and see what happens. Both are working as we want. How did we do it? It works like this. Since CSS is read from the top down, whatever is last is what's&amp;nbsp;used. So&amp;nbsp;we tell the browser that we need a min-height, Firefox complies, IE will ignore it. Then we tell the browser, that we want the height to be automatic (but with a minimum from the min-height line) AND that we want it to be the most important height statement (using the !important statement). FireFox says, ok, min-height and it's important to be auto height. IE, ignores the !important statement and the min-height statement&amp;nbsp;and just sees the next line and sets its height. FireFox downplays the next line because of the !important statement, and all's well (note: IE7 seems to use the min-height statement, but IE6 and below will disregard it).&lt;/p&gt; &lt;p&gt;Basically, we're getting IE6 to do it's default behavior, the only line that really gets used is the height:auto. With Firefox, we're telling it to set a min-height, then telling it a priority on height statements. Simple and elegent, you just have to know how to do it. There are some pretty complex CSS hacks to get around this out there, but I liked this one for a number of reasons. I read &lt;a href="http://www.dustindiaz.com/min-height-fast-hack"&gt;the article&lt;/a&gt; and it made sense to me&amp;nbsp;to use 3 lines of CSS rather than a lot of other html elements to force what we need. Anyhow, its clean and it works.&lt;/p&gt; &lt;p class="MySubHeadings"&gt;My Float's Sticking Out&lt;/p&gt; &lt;p&gt;I used floats in my layout so that I could get the HTML code&amp;nbsp;to put certain text ahead of others (for search engine optimization purposes), but display the content in&amp;nbsp;a different order&amp;nbsp;(see my &lt;a href="http://aspadvice.com/blogs/net_discoveries/archive/2006/09/29/Page-layout-SNAFU_2700_s_2C00_-or-Stupid-CSS-Tricks.aspx"&gt;previous post's section on using a float to change the html text flow&lt;/a&gt;). Well, then I ran into the issue that my floated section didn't cause the floated element's containing&amp;nbsp;element to automatically grow and encapsulate the entire floated element. In our example, we'll continue with what we have. The header DIV may represent a background to a section, and the SPAN inside the text that may be inside it (in real life I had a DIV as a background (with color) and the SPANs inside with text, that were floated as I wanted them. The Div should grow with the text so that the background extended to the end of the text.). But in FireFox, the background didn't extend down past the end of the floated internal element.&lt;/p&gt; &lt;p&gt;To begin, we need to add some stuff to our page. Add two more spans inside our DIV as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;span class="midCol"&amp;gt;b&amp;lt;/span&amp;gt;&lt;br&gt;&amp;lt;span class="rightCol"&amp;gt;c&amp;lt;/span&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, we also need to add style definitions for our column spans, so add the following to the style section:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;.leftCol&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; width: 200px;&lt;br&gt;&amp;nbsp;&amp;nbsp; border: solid 1px green;&lt;br&gt;&amp;nbsp;&amp;nbsp; float: right;&lt;br&gt;&amp;nbsp;&amp;nbsp; display: inline-block;&lt;br&gt;&amp;nbsp;&amp;nbsp; position: relative;&lt;br&gt;}&lt;br&gt;.rightCol&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; width: 200px;&lt;br&gt;&amp;nbsp;&amp;nbsp; border: solid 1px green;&lt;br&gt;&amp;nbsp;&amp;nbsp; float: left;&lt;br&gt;&amp;nbsp;&amp;nbsp; display: inline-block;&lt;br&gt;&amp;nbsp;&amp;nbsp; position: relative;&lt;br&gt;}&lt;br&gt;.midCol&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; width: 200px;&lt;br&gt;&amp;nbsp;&amp;nbsp; border: solid 1px green;&lt;br&gt;&amp;nbsp;&amp;nbsp; float: right;&lt;br&gt;&amp;nbsp;&amp;nbsp; display: inline-block;&lt;br&gt;&amp;nbsp;&amp;nbsp; position: relative;&lt;br&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, refresh and see what I'm talking about, IE actually has&amp;nbsp;our red DIV section enlarge to fit the floated&amp;nbsp;SPAN, but FireFox does not. What's the deal? Well, actually Firefox is doing it&amp;nbsp;the way it's supposed to work. If it didn't work this way, then things wouldn't wrap around stuff like pictures like they should (again I read it in &lt;a href="http://www.complexspiral.com/publications/containing-floats/"&gt;an article&lt;/a&gt;). The question though, is how to fix it. The simple solution lies in adding something in the flow AFTER the floated element that isn't floating, something that the parent element WILL have to wrap around. So add another DIV after all our SPANS as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;&amp;lt;div class="floatHack"&amp;gt;&amp;lt;/div&amp;gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;and add another section to our style section as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;.floatHack&lt;br&gt;{&lt;br&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Notice that the floatHack DIV is empty, it doesn't need any content, we just need something invisible to force the flow differently. So what do we do to fix the problem. Well, we need an item that ISN'T affected BY the floats to be placed&amp;nbsp;AFTER the floats. To do that, we use the clear: CSS attribute. We can have it clear right, left or both&amp;nbsp;floats. In our example, it's the right float&amp;nbsp;that's causing the problem, so we can choose either right or both, I'll choose both. Add the following to your .floatHack style definition:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;clear: both;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now refresh and see what happens. Viola, our problem fixed.&amp;nbsp;As a side note, you can use any&amp;nbsp;block level element to do this hack.&amp;nbsp;Now, let me give you a little side note of warning, though. Add the following to our leftCol style definition and see what happens:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;top: 40px;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Refresh and see what happens, our container doesn't grow to fit. What happened? Well, we're moving our float out of it's position. Workaround? I don't have one.&lt;/p&gt; &lt;p class="MySubHeadings"&gt;Turn Off Double Spacing in an HTML List&lt;/p&gt; &lt;p&gt;Ok one more then I'll call it quits. Have you ever noticed that when you create a list in HTML using the UL tag, that it puts a space between the header and the first item (and one after the list for that matter)? How do we get rid of that unsightly annoyance?&lt;/p&gt; &lt;p&gt;This exercise doesn't continue where we left off, so you may want to create a new html file for it. Create a style section, but don't put any definitions in it.&amp;nbsp;Then in the body of the HTML,&amp;nbsp;create a UL as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;My unordered list&lt;br&gt;&amp;lt;ul&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;first item&amp;lt;/li&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;second item&amp;lt;/li&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;lt;li&amp;gt;etc.&amp;lt;/li&amp;gt;&lt;br&gt;&amp;lt;/ul&amp;gt;&lt;br&gt;The line after&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Pretty standard, and when we render it in the browser, it puts a space between the title and the first item, and a space between the last item and the next line of text. To get rid of it, I'm using some CSS from &lt;a href="http://www.boutell.com/newfaq/creating/ulwithoutdoublelinebreak.html"&gt;another article&lt;/a&gt; that I read. All we need to do is turn off the top and bottom margins for the UL element. So, add the following to your style section in the header:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;ul&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; margin-top: 0px;&lt;br&gt;&amp;nbsp;&amp;nbsp; margin-bottom: 0px;&lt;br&gt;}&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now refresh both browsers and we've eliminated the nasty spaces. We can now&amp;nbsp;put spaces where WE want them instead of where the HTML wants them.&lt;/p&gt; &lt;p class="MyHeadings"&gt;Epilogue  &lt;p&gt;&lt;/p&gt; &lt;p&gt;Here's what I found. Test your web apps in IE6, IE7 and FireFox. They are all different animals, and will all display differently. Then try not to&amp;nbsp;pull your hair out trying to fix it&amp;nbsp;when they don't exactly work the same.&lt;/p&gt;
&lt;div class = "shareblock"&gt;&lt;strong&gt;Share this post:&lt;/strong&gt; &lt;a href = "mailto:?body=Thought you might like this: http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;;subject=More+Page+Layout+Snafu%27s+or+More+Stupid+CSS+Tricks" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;email it!&lt;/a&gt; |  &lt;a href = "http://del.icio.us/post?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;;title=More+Page+Layout+Snafu%27s+or+More+Stupid+CSS+Tricks" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;bookmark it!&lt;/a&gt; |  &lt;a href = "http://www.digg.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;;phase=2" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;digg it!&lt;/a&gt; |  &lt;a href = "http://reddit.com/submit?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;title=More+Page+Layout+Snafu%27s+or+More+Stupid+CSS+Tricks" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;reddit!&lt;/a&gt; |  &lt;a href = "http://www.dotnetkicks.com/submit/?url=http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;;title=More+Page+Layout+Snafu%27s+or+More+Stupid+CSS+Tricks" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;kick it!&lt;/a&gt; |  &lt;a href = "https://favorites.live.com/quickadd.aspx?marklet=1&amp;amp;;mkt=en-us&amp;amp;;url=http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx&amp;amp;;title=More+Page+Layout+Snafu%27s+or+More+Stupid+CSS+Tricks&amp;amp;;top=1" target="_blank" title = "Post http://aspadvice.com/blogs/net_discoveries/archive/2007/07/26/More-Page-Layout-Snafu_2700_s-or-More-Stupid-CSS-Tricks.aspx"&gt;live it!&lt;/a&gt;&lt;/div&gt;&lt;img src="http://aspadvice.com/aggbug.aspx?PostID=33295" width="1" height="1"&gt;</description><category domain="http://aspadvice.com/blogs/net_discoveries/archive/tags/CSS/default.aspx">CSS</category></item><item><title>Working with an ObjectDataSource</title><link>http://aspadvice.com/blogs/net_discoveries/archive/2007/05/21/Working-with-an-ObjectDataSource.aspx</link><pubDate>Mon, 21 May 2007 22:08:50 GMT</pubDate><guid isPermaLink="false">e709ad4c-0c15-48eb-915e-c462c6e85445:29846</guid><dc:creator>Yougotiger</dc:creator><slash:comments>2</slash:comments><comments>http://aspadvice.com/blogs/net_discoveries/comments/29846.aspx</comments><wfw:commentRss>http://aspadvice.com/blogs/net_discoveries/commentrss.aspx?PostID=29846</wfw:commentRss><wfw:comment>http://aspadvice.com/blogs/net_discoveries/rsscomments.aspx?PostID=29846</wfw:comment><description>&lt;p class="MySubHeadings"&gt;Prologue&lt;/p&gt; &lt;p&gt;Unrelated (for a change... as of yet) to the website redesign that I'm doing of our company website, I've been reading and doing the code from a very good book called "&lt;a href="http://www.wrox.com/WileyCDA/WroxTitle/productCd-0764584642.html"&gt;ASP.Net 2.0 Programming: Problem-Design-Solution&lt;/a&gt;". In this book it details how to create a scalable website using the ObjectDataSource and an appropriate n-layer data solution. The ObjectDataSource is very powerful, but it isn't without it's little hitches. I ran into one and it took me a while to find the &lt;a href="http://www.c-sharpcorner.com/UploadFile/rehamanshaik/ObjectDataSource11162006100303AM/ObjectDataSource.aspx?ArticleID=47284edf-52f2-41aa-9fb0-b59a95602498"&gt;solution&lt;/a&gt;&amp;nbsp;(much of my info is take from there). But I did finally find the answer.&lt;/p&gt; &lt;p class="MySubHeadings"&gt;Problem&lt;/p&gt; &lt;p&gt;ObjectDataSource is a very easy way to create a custom object holding your data and then have something else do the&amp;nbsp;hard work of allowing edits, updates etc to your data&amp;nbsp;from the&amp;nbsp;GridView it's bound to. What I found though is that you have to be careful with your variable and parameter ID's.&lt;/p&gt; &lt;p class="MySubHeadings"&gt;Solution&lt;/p&gt; &lt;p&gt;Ok, we've got a little setup to do so that we can properly use our ObjectDataSource (ODS). Rather than creating a database, I'm going to simulate one, which means that our updates won't work quite properly (to keep the code down), but it will give an opportunity to see the problem that we're discussing here and let us see some basics of setting up an ObjectDataSource. Ok, to begin, we'll create&amp;nbsp;2 class modules:&amp;nbsp;ODSCustomer.vb and ODSDal.vb. ODSCustomer will actually be our Business Logic&amp;nbsp;Layer (BLL) and our Presentation Layer Object, and will be an instance of a single&amp;nbsp;customer. Within this instance, are all the properties of a customer and all the functions related to using the customer (typically CRUD&amp;nbsp;create, retrieve, update and delete functions). The ODSDal is our Data Access Layer (DAL). This will actually "retrieve" our data from the database (create the data in our case) and return an array of the Customer objects. (As a side note, in the book, one more module is created and that holds only the details of the customer (properties). This is the information passed between the DAL and the BLL, the common thread so to speak. It is then reformatted by the BLL into an object with the methods etc. We're cheating and having our data returned in our BLL format. Confused yet?) The design is the most confusing part, see the table below for a better idea:&lt;/p&gt; &lt;p&gt; &lt;table&gt;  &lt;tr&gt; &lt;td align="middle" colspan="3"&gt;&lt;strong&gt;True N-Tier Design&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;p&gt;&lt;strong&gt;DAL&lt;/strong&gt;&lt;br&gt;Contains data access code, returns an array of our BLL object&lt;/p&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;BLL&lt;/strong&gt;&lt;br&gt;Contains the information of 1 object (1 customer's details). This ensures that the information passed between DAL and presentation Layers is pretty generic. (DAL doesn't know what happens in object, and object doesn't know about data retrieval in DAL).&lt;/td&gt; &lt;td&gt;&lt;strong&gt;Object&lt;br&gt;&lt;/strong&gt;Contains all the information found in the BLL, but also has methods relating to the object (our CRUD methods - they call the DAL and pass the BLL object.). These methods call the DAL, receive and return BLL objects.&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td align="middle" colspan="3"&gt;&lt;strong&gt;Our Design&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;DAL&lt;/strong&gt;&lt;br&gt;This generates our data (not retrieves it) and returns an array of our Object&lt;/td&gt; &lt;td colspan="2"&gt;&lt;strong&gt;BLL/Object&lt;br&gt;&lt;/strong&gt;We just combined the two, our DAL returns an array of the object instead of packing up the details to transfer back and forth. There is no abstraction between DAL And BLL.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/p&gt; &lt;p&gt;Ok, so let's start by creating our DAL. Open our ODSDal.vbs. Lets start with adding a namespace and import the Collections.Generic namespace:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Imports System.Collections.Generic&lt;br&gt;Namespace ODS.DAL&lt;br&gt;End Namespace&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Next, let's create an array to hold our list of customers, we'll actually create a generic list of that we can pass around:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Shared theCustomers As List(Of ODS.Model.ODSCustomer) _&lt;br&gt;= New List(Of ODS.Model.ODSCustomer)&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This creates an empty&amp;nbsp;collection of our customer objects this going to be the datasource that our project uses (essentially). We are actually using Shared functions throughout so we don't necessarily need to create a constructor to generate our list, but we do need to generate it when we retrieve our list. So to create our collection, we have the GenerateCustomers() method:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Shared Sub GenerateCustomers()&lt;br&gt;&amp;nbsp;&amp;nbsp; If theCustomers.Count &amp;gt; 0 Then Exit Sub&lt;br&gt;&amp;nbsp;&amp;nbsp; Dim iMax As Integer = CInt(New Random().NextDouble() * 100)&lt;br&gt;&amp;nbsp;&amp;nbsp; theCustomers.Clear()&lt;br&gt;&amp;nbsp;&amp;nbsp; For iCount As Integer = 0 To iMax&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; theCustomers.Add(New ODS.Model.ODSCustomer _&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; (iCount, "FirstName" &amp;amp; iCount.ToString(), _&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; "LastName" &amp;amp; iCount.ToString()))&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;Next&lt;br&gt;End Sub&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, this just picks a random number from 1 to 100 and generates that number of customer records. Names are just FirstName + the current item number, same with the last name. This is all stored in a customer object and added to our collection. Now we need to create functions to Get, Update and Delete Customers:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Shared Function GetCustomers() As List(Of ODS.Model.ODSCustomer)&lt;br&gt;&amp;nbsp;&amp;nbsp; GenerateCustomers()&lt;br&gt;&amp;nbsp;&amp;nbsp; Return theCustomers&lt;br&gt;End Function &lt;br&gt;&lt;br&gt;Public Shared Function UpdateCustomer(ByVal ID As Integer, _ &lt;br&gt;&amp;nbsp;&amp;nbsp; ByVal FName As String, ByVal LName As String) As Boolean&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; theCustomers(ID).FirstName = FName&lt;br&gt;&amp;nbsp;&amp;nbsp; theCustomers(ID).LastName = LName&lt;br&gt;End Function &lt;br&gt;&lt;br&gt;Public Shared Function DeleteCustomer(ByVal ID As Integer) As Boolean&lt;br&gt;&amp;nbsp;&amp;nbsp; Return theCustomers.Remove(theCustomers(ID))&lt;br&gt;End Function &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;You'll see that GetCustomers calls our&amp;nbsp;GenerateCustomers() method and then&amp;nbsp;returns&amp;nbsp;the list of Customers that we just created. UpdateCustomer() takes the ID of the customer and the new Names, and just changes them (FYI, update code doesn't necessarily update the correct name). Delete takes the ID and then removes it from the collection (again, ID doesn't necessarily correspond to placement in the grid so it doesn't always delete the correct entry - that's what our BLL should do...). That's it for our DAL. You should be getting an alert that ODS.Model.ODSCustomer doesn't exist, we'll create that now. Now let's concentrate on our Customer Object. Again, we want to import the collections.generic namespace and create our own namespace:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Imports System.Collections.Generic&lt;br&gt;Namespace ODS.Model&lt;br&gt;End Namespace&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now, we need to create properties and the corresponding internal variables to hold the customer's ID, First and Last names, so add:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Private _CustomerID As Integer = 0&lt;br&gt;Private _FirstName As String = ""&lt;br&gt;Private _LastName As String = ""&lt;br&gt;&lt;br&gt;Public Property CustomerID() As Integer&lt;br&gt;&amp;nbsp;&amp;nbsp; Get&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; Return _CustomerID&lt;br&gt;&amp;nbsp;&amp;nbsp; End Get&lt;br&gt;&amp;nbsp;&amp;nbsp; Set(ByVal value As Integer)&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; _CustomerID = value&lt;br&gt;&amp;nbsp;&amp;nbsp; End Set&lt;br&gt;End Property &lt;br&gt;&lt;br&gt;Public Property FirstName() As String&lt;br&gt;&amp;nbsp;&amp;nbsp; Get&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; Return _FirstName&lt;br&gt;&amp;nbsp;&amp;nbsp; End Get&lt;br&gt;&amp;nbsp;&amp;nbsp; Set(ByVal value As String)&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; _FirstName = value&lt;br&gt;&amp;nbsp;&amp;nbsp; End Set&lt;br&gt;End Property &lt;br&gt;&lt;br&gt;Public Property LastName() As String&lt;br&gt;&amp;nbsp;&amp;nbsp; Get&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; Return _LastName&lt;br&gt;&amp;nbsp;&amp;nbsp; End Get&lt;br&gt;&amp;nbsp;&amp;nbsp; Set(ByVal value As String)&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; _LastName = value&lt;br&gt;&amp;nbsp;&amp;nbsp; End Set&lt;br&gt;End Property &lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;This is really nothing more than exposing the properties of our object. Ok, now we need to add a constructor to the class so that we can instantiate the object. Well add an overloaded constructor, one that takes no parameters, and one that takes them all:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Sub New() &lt;br&gt;&lt;br&gt;End Sub &lt;br&gt;&lt;br&gt;Public Sub New(ByVal CustomerId As Integer, _&lt;br&gt;&amp;nbsp; &amp;nbsp;ByVal FirstName As String,&amp;nbsp;ByVal LastName As String)&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; Me.CustomerID = CustomerId&lt;br&gt;&amp;nbsp;&amp;nbsp; Me.FirstName = FirstName&lt;br&gt;&amp;nbsp;&amp;nbsp; Me.LastName = LastName&lt;br&gt;End Sub&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now there's one last piece we need to do. We need to have the object invoke its own&amp;nbsp;methods so that it can perform its DAL functions. In order for this object to be used as a datasource, it needs to be able to call its own&amp;nbsp;CRUD methods. So we'll add the following: GetCustomers, UpdateCustomer and DeleteCustomer. If we wanted to, we could add others such as InsertCustomer, and GetCustomerByID (for a details view), but we don't want to. So add the following:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="CodeSnippet"&gt;Public Shared Function GetCustomers() As List(Of ODSCustomer)&lt;br&gt;&amp;nbsp;&amp;nbsp; Return DAL.ODSDal.GetCustomers()&lt;br&gt;End Function &lt;br&gt;&lt;br&gt;Public Shared Function UpdateCustomer(ByVal ID As Integer, _&lt;br&gt;&amp;nbsp;&amp;nbsp; ByVal FName As String, ByVal LName As String) As Boolean &lt;br&gt;&amp;nbsp;&amp;nbsp; Return DAL.ODSDal.UpdateCustomer(ID, FName, LName)&lt;br&gt;End Function &lt;br&gt;&lt;br&gt;Public Shared Function DeleteCustomer(ByVal CustomerID As Integer) As Boolean&lt;br&gt;&amp;nbsp;&amp;nbsp; Return DAL.ODSDal.DeleteCustomer(CustomerID)&lt;br&gt;End Function&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Basically, these just pass the information back to the DAL and it does the work.&lt;/p&gt; &lt;p&gt;Ok, now for the final piece, we need to create the page that will display our data. Create an ASP.Net page named ObjDataSource.aspx. Once it's created, we don't even have to type any code, we can just do it visually (awesome!). Drop a GridView onto your page, and name it gvwCustomer. Drop an ObjectDataSource onto the page as well, and name it objCustomers. Now click on objCustomers, and click the SmartTag arrow.&amp;nbsp;Select 'Configure Data Source'.&amp;nbsp;You want to choose your Business Object, in our case we want to select ODS.Model.ODSCustomer&amp;nbsp;- you may need to uncheck the 'Show only data components' checkbox -&amp;nbsp;and click next. Now we need to tell it what functions to use for each of our CRUD statements. For Select, choose our GetCustomers() method, for Update, choose our UpdateCustomer method and for Delete select our DeleteCustomer method, then click Finish.&lt;/p&gt; &lt;p&gt;Next, lets configure the GridView. Click on the SmartTag for the GridView and select ObjCustomers as our datasource. Also, check the boxes for Enable Editing and Enable Deleting. Then run your application. Viola, a datasource built from a collection of objects, or an ObjectDataSource.&lt;/p&gt; &lt;p&gt;Ok, so it looks good, but I ran into an issue that I have already&amp;nbsp;built into our project so I could talk about it. I looked everywhere on the Internet and only found on decent solution, so&amp;nbsp;the entire reason I did this post was so that I could add a second. To see our issue, click Delete next to a row, it works fine, but now click 'edit', make a change and then click 'update'. Notice that we&amp;nbsp;get an error that reads 'objCustomers could not find a non-generic method 'UpdateCustomer' that has parameters: ID, FName, LName, FirstName, LastName, CustomerID' (maybe in slightly different order). What's the problem? Simple actually, but frustrating a lot. We were inconsistent with our parameter and variable names. The property names in our Customer class are CustomerID, FirstName and LastName, but if you look at the&amp;nbsp;UpdateCustomer method our parameters are&amp;nbsp;ID, FName and LName.&lt;/p&gt; &lt;p&gt;Ok, I hear you saying, "so what?", that's what I thought too, but it matters. If we aren't consistent then ASP.Net will look for ANOTHER parameter with the same name. Notice that our error statement has&amp;nbsp;6 parameters,&amp;nbsp;not the 3 we created. It's trying to pass&amp;nbsp;6 parameters to a method that has&amp;nbsp;only&amp;nbsp;3. It sees the different names as DIFFERENT&amp;nbsp;parameters and tries to pass all 6.&amp;nbsp;The solution is that&amp;nbsp;we need to modify our UpdateCustomer method so that the parameters are consistent with our property names. Change your code as follows:&lt;/p&gt; &lt;blockquote&gt; &lt;p class="