<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Cogniza &#187; Programming</title>
	<atom:link href="http://cogniza.com/wordpress/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://cogniza.com/wordpress</link>
	<description>Business-Intelligence Specialists</description>
	<lastBuildDate>Sat, 30 Apr 2011 13:22:21 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>BusinessObjects SDK: Change a Universe&#039;s Data Connection</title>
		<link>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-change-a-universes-data-connection/</link>
		<comments>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-change-a-universes-data-connection/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 15:47:11 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Business Objects Enterprise]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[VB.Net]]></category>
		<category><![CDATA[BusinessObjects-SDK]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=128</guid>
		<description><![CDATA[Change a Universe&#8217;s Data Connection without using the Universe COM SDK.

'get the Universe infoObjects
Dim universeInfoObject As InfoObject = infoStore.Query("SELECT SI_ID, SI_NAME, SI_DATACONNECTION FROM CI_APPOBJECTS WHERE SI_ID=" &#038; )(1)
'cast InfoObject into a Universe
Dim universe As BusinessObjects.Enterprise.Desktop.Universe = CType(universeInfoObject, Universe)
With universe
    'clear all data connections
    .DataConnections.Clear()
    'add new [...]]]></description>
			<content:encoded><![CDATA[<p>Change a Universe&#8217;s Data Connection without using the Universe COM SDK.<span id="more-128"></span></p>
<p><code><br />
'get the Universe infoObjects<br />
Dim universeInfoObject As InfoObject = infoStore.Query("SELECT SI_ID, SI_NAME, SI_DATACONNECTION FROM CI_APPOBJECTS WHERE SI_ID=" &#038; <info Object Id>)(1)</p>
<p>'cast InfoObject into a Universe<br />
Dim universe As BusinessObjects.Enterprise.Desktop.Universe = CType(universeInfoObject, Universe)</p>
<p>With universe<br />
    'clear all data connections<br />
    .DataConnections.Clear()</p>
<p>    'add new data connection<br />
    .DataConnections.Add(<data Connection Id>)</p>
<p>    'save to repository<br />
    .Save()<br />
End With</p>
<p></data></info></code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-change-a-universes-data-connection/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>BusinessObjects SDK: Copy one InfoObject&#039;s File to Another InfoObject</title>
		<link>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-copy-one-infoobjects-file-to-another-infoobject/</link>
		<comments>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-copy-one-infoobjects-file-to-another-infoobject/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 15:32:55 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Business Objects Enterprise]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[VB.Net]]></category>
		<category><![CDATA[BusinessObjects-SDK]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=123</guid>
		<description><![CDATA[Use the BusinessObjects SDK to copy files between two InfoObjects.  This is useful when the source is a 'template' object (e.g. Crystal Report) that will be deployed to one or more targets.]]></description>
			<content:encoded><![CDATA[<p>Use the BusinessObjects SDK to copy files between two InfoObjects.  This is useful when the source is a &#8216;template&#8217; object (e.g. Crystal Report) that will be deployed to one or more targets.<span id="more-123"></span></p>
<p><code><br />
'get the source and target infoObjects<br />
Dim sourceInfoObject As InfoObject = infoStore.Query("SELECT SI_ID, SI_NAME, SI_FILES FROM CI_INFOOBJECTS WHERE SI_ID=" &#038; sourceId)(1)</p>
<p>Dim targetInfoObject As InfoObject =  infoStore.Query("SELECT SI_ID, SI_NAME, SI_FILES FROM CI_INFOOBJECTS WHERE SI_ID=" &#038; targetId)(1)</p>
<p>'get the desired file from FRS<br />
Dim sourceFile As File = sourceInfoObject.Files(1)</p>
<p>'create buffer and size appropriately<br />
Dim buffer As Byte() = new Byte(sourceFile.Size) {}</p>
<p>'copy file to buffer<br />
sourceFile.CopyTo(buffer)</p>
<p>'get the target file from the FRS, using the same index as the source<br />
Dim targetFile As File = targetInfoObject.Files(sourceFile.NthFile)</p>
<p>'copy buffer to target file<br />
targetFile.Overwrite(buffer)</p>
<p>'save to repository<br />
targetInfoObject.Save<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2009/11/12/businessobjects-sdk-copy-one-infoobjects-file-to-another-infoobject/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server: Date Arithmetic</title>
		<link>http://cogniza.com/wordpress/2009/11/05/sql-server-date-arithmetic/</link>
		<comments>http://cogniza.com/wordpress/2009/11/05/sql-server-date-arithmetic/#comments</comments>
		<pubDate>Fri, 06 Nov 2009 02:32:24 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Technique]]></category>
		<category><![CDATA[Date]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=106</guid>
		<description><![CDATA[Useful, dynamically-generated dates for SQL Server.

--[1st day  of current month]: use the GetDate(), Month(), and Year() functions to determine the current month and year.   Build a string using these values in MM/01/YYYY format.  Convert the String to DateTime.
 CAST( CAST( Month(GetDate()) AS Char(2) ) + '/01/' + CAST( Year(GetDate()) AS Char(4)) [...]]]></description>
			<content:encoded><![CDATA[<p>Useful, dynamically-generated dates for SQL Server.<span id="more-106"></span><br />
<code><br />
--[1st day  of current month]: use the GetDate(), Month(), and Year() functions to determine the current month and year.   Build a string using these values in MM/01/YYYY format.  Convert the String to DateTime.<br />
 CAST( CAST( Month(GetDate()) AS Char(2) ) + '/01/' + CAST( Year(GetDate()) AS Char(4)) AS DateTime)<br />
</code><code><br />
--[Last day of current month]: add a month to [1st day of current month], then subtract a day.<br />
DateAdd(m, 1, CAST( CAST( Month(GetDate()) AS Char(2) ) + '/01/' + CAST( Year(GetDate()) AS Char(4)) AS DateTime)) -1<br />
</code><code><br />
--[1st day of prior month]: subtract a month from [1st day of current month].<br />
DateAdd(m, -1, CAST( CAST( Month(GetDate()) AS Char(2) ) + '/01/' + CAST( Year(GetDate()) AS Char(4)) AS DateTime))<br />
</code><code><br />
--[Last day of prior month]:  subtract a day from [1st day of current month].<br />
CAST( CAST( Month(GetDate()) AS Char(2) ) + '/01/' + CAST( Year(GetDate()) AS Char(4)) AS DateTime) -1<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2009/11/05/sql-server-date-arithmetic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crystal Reports: FractionToDecimal() Function</title>
		<link>http://cogniza.com/wordpress/2009/09/08/crystal-reports-fractiontodecimal-function/</link>
		<comments>http://cogniza.com/wordpress/2009/09/08/crystal-reports-fractiontodecimal-function/#comments</comments>
		<pubDate>Tue, 08 Sep 2009 18:26:11 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Crystal Reports]]></category>
		<category><![CDATA[Functions]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=109</guid>
		<description><![CDATA[Converts a fractional value in the format of Numerator/Denominator or Whole + Numerator/Denominator to a decimal value.


//Assumes values in the following formats:
//99 - integer value
//99.9 - decimal value
//N/D - numerator/denominator
//M N/D - whole + numerator/denominator
Function (Stringvar value)
Local Numbervar whole := 0;
Local Stringvar fraction;
//
value := trim(value);
//integer or decimal format
If IsNumeric(value) Then
    ToNumber(value)
Else (
 [...]]]></description>
			<content:encoded><![CDATA[<p>Converts a fractional value in the format of Numerator/Denominator or Whole + Numerator/Denominator to a decimal value.<br />
<span id="more-109"></span><br />
<code><br />
//Assumes values in the following formats:<br />
//99 - integer value<br />
//99.9 - decimal value<br />
//N/D - numerator/denominator<br />
//M N/D - whole + numerator/denominator<br />
Function (Stringvar value)</p>
<p>Local Numbervar whole := 0;<br />
Local Stringvar fraction;</p>
<p>//<br />
value := trim(value);</p>
<p>//integer or decimal format<br />
If IsNumeric(value) Then<br />
    ToNumber(value)</p>
<p>Else (</p>
<p>    //whole + numerator/denominator<br />
    If InStr(value, " ")>0 Then (</p>
<p>        whole := ToNumber(Split(value, " ")[1]);<br />
        fraction := Split(value, " ")[2];</p>
<p>    )</p>
<p>    //numerator/denominator<br />
    Else (</p>
<p>        fraction := value;</p>
<p>    );</p>
<p>    Local Numbervar numerator := ToNumber(Split(fraction, "/")[1]);<br />
    Local Numbervar denominator := ToNumber(Split(fraction, "/")[2]);</p>
<p>    whole + (numerator / denominator);<br />
)<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2009/09/08/crystal-reports-fractiontodecimal-function/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crystal Reports: Strategy to Localize a Report (labels)</title>
		<link>http://cogniza.com/wordpress/2009/07/09/crystal-reports-strategy-to-localize-a-report-labels/</link>
		<comments>http://cogniza.com/wordpress/2009/07/09/crystal-reports-strategy-to-localize-a-report-labels/#comments</comments>
		<pubDate>Thu, 09 Jul 2009 15:05:06 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Business Objects Enterprise]]></category>
		<category><![CDATA[Crystal Reports]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Technique]]></category>
		<category><![CDATA[Localization]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=55</guid>
		<description><![CDATA[Create a Crystal Report that dynamically recalculate its labels based on the Workstation&#8217;s localization settings.

ContentLocale
Crystal Reports XI introduced a keyword named &#8216;ContentLocale&#8217;. This keyword returns the machine&#8217;s locale setting (e.g. &#8216;en_US&#8217; for English (US)).  This value is set in Windows&#8217; Regional Settings Control Panel.
However, this keyword can only be used in formulae that are executed [...]]]></description>
			<content:encoded><![CDATA[<p>Create a Crystal Report that dynamically recalculate its labels based on the Workstation&#8217;s localization settings.</p>
<p><span id="more-55"></span></p>
<h3>ContentLocale</h3>
<p>Crystal Reports XI introduced a keyword named &#8216;ContentLocale&#8217;. This keyword returns the machine&#8217;s locale setting (e.g. &#8216;en_US&#8217; for English (US)).  This value is set in Windows&#8217; Regional Settings Control Panel.</p>
<p>However, this keyword can only be used in formulae that are executed during the report&#8217;s <a href="http://www.cogniza.com/blog/?p=12">Second Pass</a>. The following Record Selection Formula, for example, will generate an error:</p>
<p><code>{my_table.localeName}=ContentLocale </code></p>
<h3>Custom Functions</h3>
<p>This keyword, however, can be used in combination with a Report Custom Function to dynamically and transparently apply localization settings when a report is viewed, even reports that have been saved with data or have been scheduled using Business Objects Enterprise.</p>
<p>//&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
// Return a localized string for a given Key and Language.<br />
//&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Function (Stringvar Key, Stringvar Language)</p>
<p>//define array of keys that are used by Formula fields<br />
Stringvar Array Keys:=["COUNTRY","REGION","CITY"];</p>
<p>//define an array of localized strings for each required language; ensure localized strings are same position as the Key array (correlated arrays).</p>
<p>//determine which array to use<br />
Stringvar Array Values;<br />
Select Language<br />
Case &#8220;en_US&#8221;: Values:=["Country","State","City"]<br />
Case &#8220;it_IT&#8221;: Values:=["Paese","Dichiari","Citta"]<br />
Case &#8220;fr_FR&#8221;: Values:=["Pays","S","C"]<br />
Default: Values:=["Country","State","City"]<br />
;</p>
<p>//set value&#8217;s default value to the Key; useful for debugging purposes.<br />
Stringvar value:=Key;</p>
<p>//iterate through Key array&#8230;<br />
Numbervar i;<br />
for i := 1 To Ubound(Keys) do (<br />
//if value in array matches the specified value, return the value from the localized array<br />
if Keys[i] = Key then (<br />
value:=Values[i];<br />
exit for<br />
)<br />
);</p>
<p>//return value<br />
value;</p>
<h3>Formula Fields</h3>
<p>Create a Formula Field that will act as a label for each desired Key.  Add it to the report&#8217;s canvas.</p>
<p>//{@Country}<br />
getLocalizedString (&#8220;COUNTRY&#8221;, ContentLocale)</p>
<p>//{@Region}<br />
getLocalizedString (&#8220;REGION&#8221;, ContentLocale)</p>
<p>//{@City}<br />
getLocalizedString (&#8220;CITY&#8221;, ContentLocale)</p>
<h4>Advantages</h4>
<ul>
<li>Rapid development and deployment.</li>
<li>Executes during &#8216;view&#8217; time.</li>
<li>Can be saved to Repository and shared with other reports.</li>
</ul>
<h4>Limitations</h4>
<ul>
<li>Crystal Reports is required to maintain the logic and publish changes to the Repository.</li>
<li>Array of Key/Values limited to 1000 items.</li>
</ul>
<h3>Summary</h3>
<p>This approach should help ease the burden of localizing the labels contained in a Crystal Report.</p>
<h3>Download</h3>
<p><a href="/wordpress/wp-content/Report Localization [custom function].12.000.rpt" target="_new">Sample report</a><br />
<a href="/wordpress/wp-content/xtreme.zip" target="_new">Sample database</a></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2009/07/09/crystal-reports-strategy-to-localize-a-report-labels/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Crystal Reports: Array_PushEx() Function</title>
		<link>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_pushex-function/</link>
		<comments>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_pushex-function/#comments</comments>
		<pubDate>Wed, 09 Jan 2008 20:26:20 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Crystal Reports]]></category>
		<category><![CDATA[Functions]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=105</guid>
		<description><![CDATA[Add a unique string value to an array of string values. Uses the delimiter to convert the value into an array.

Function (Stringvar Array Items, Stringvar Values, Stringvar Delimiter)
    Stringvar Array Temp:=Split(Values,Delimiter);
    Numbervar i;
    For i := 1 To Ubound(Temp) Do (
        If Array_Contains (Items, Temp[i]) = False Then (
            Redim Preserve Items[Ubound(Items)+1];
            Items[Ubound(Items)]:=Temp[i];
        );
    );
    [...]]]></description>
			<content:encoded><![CDATA[<p>Add a unique string value to an array of string values. Uses the delimiter to convert the value into an array.<span id="more-105"></span><br />
<code><br />
Function (Stringvar Array Items, Stringvar Values, Stringvar Delimiter)<br />
    Stringvar Array Temp:=Split(Values,Delimiter);<br />
    Numbervar i;<br />
    For i := 1 To Ubound(Temp) Do (<br />
        If Array_Contains (Items, Temp[i]) = False Then (<br />
            Redim Preserve Items[Ubound(Items)+1];<br />
            Items[Ubound(Items)]:=Temp[i];<br />
        );<br />
    );<br />
    Items;<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_pushex-function/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Crystal Reports: Array_Push() Function</title>
		<link>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_push-function/</link>
		<comments>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_push-function/#comments</comments>
		<pubDate>Wed, 09 Jan 2008 20:21:23 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Crystal Reports]]></category>
		<category><![CDATA[Functions]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Function]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=104</guid>
		<description><![CDATA[Add a string value to an array of strings, only if string value hasn&#8217;t already been added to the array.
Function (Stringvar Array Items, Stringvar Value)
    If Array_Contains (Items, Value) = False Then (
        Redim Preserve Items[Ubound(Items)+1];
        Items[Ubound(Items)]:=Value;
    );
    Items;

]]></description>
			<content:encoded><![CDATA[<p>Add a string value to an array of strings, only if string value hasn&#8217;t already been added to the array.<span id="more-104"></span><code><br />
Function (Stringvar Array Items, Stringvar Value)<br />
    If Array_Contains (Items, Value) = False Then (<br />
        Redim Preserve Items[Ubound(Items)+1];<br />
        Items[Ubound(Items)]:=Value;<br />
    );<br />
    Items;<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2008/01/09/crystal-reports-array_push-function/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Visual Basic: Create a Sorted Collection</title>
		<link>http://cogniza.com/wordpress/2007/01/30/visual-basic-create-a-sorted-collection/</link>
		<comments>http://cogniza.com/wordpress/2007/01/30/visual-basic-create-a-sorted-collection/#comments</comments>
		<pubDate>Tue, 30 Jan 2007 21:51:00 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[Visual Basic]]></category>
		<category><![CDATA[Collections]]></category>
		<category><![CDATA[COM]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=92</guid>
		<description><![CDATA[Create a sorted Visual Basic 6 collection that is similar to sorting an ArrayList in Visual Basic .Net.]]></description>
			<content:encoded><![CDATA[<p>Create a sorted Visual Basic 6 collection that is similar to sorting an ArrayList in Visual Basic .Net.</p>
<p><span id="more-92"></span></p>
<p>This example will create a sorted collection of Person objects; the class will be named PersonCollection.</p>
<p>The first step is to create two classes to be used as interfaces: IComparable and IComparer.  This classes resemble their Visual Basic.Net interface equivalents.</p>
<h2>IComparable Class</h2>
<pre>'compare the current object to the referenced object</pre>
<pre>Public Function CompareTo(obj As Object)
End Function</pre>
<p>The Person&#8217;s IComparable interface will be used by the PersonCollection&#8217;s Sort() method to provide the default sorting.</p>
<h2>IComparer Class</h2>
<pre>'compare to referenced objects</pre>
<pre>Public Function Compare(x As Object, y As Object)
End Function</pre>
<p>The PersonSorter&#8217;s IComparable interface will be used by the PersonCollection&#8217;s Sort() method to provide additional sorting options.</p>
<h2>Person Class</h2>
<p>The Person class has two relevant properties:</p>
<pre>'--------------------------------------------------------------------------------
'Public Properties
'--------------------------------------------------------------------------------
'FileAs will be used to provide a default-sorting order.
Public Property Get FileAs() As String
FileAs = Me.LastName &amp; ", " &amp; Me.FirstName
End Property

'The Key property is used to provide the class' unique key.
Public Property Get Key As String
Key = "#" &amp; Me.Id
End Property
'--------------------------------------------------------------------------------
'IComparable Methods
'--------------------------------------------------------------------------------
Private Function IComparable_CompareTo(obj As Object)

If Not TypeOf obj Is Person Then _
Err.Raise vbObjectError + 1, Err.Source, "The object is not a Person."

'convert the object to a Person
Dim Item As Person: Set Item = obj

'compare the FileAs field between the two instances.
If Me.FileAs &gt; Item.FileAs Then
IComparable_CompareTo = 1
ElseIf Me.FileAs &lt; Item.FileAs Then
IComparable_CompareTo = -1
Else
IComparable_CompareTo = 0
End If

End Function</pre>
<p>The Person class exposes a CompareTo function to complete the interface inheritance:</p>
<pre>'--------------------------------------------------------------------------------
'Public Methods
'--------------------------------------------------------------------------------
Public Function CompareTo(obj As Object)
CompareTo = IComparable_CompareTo(obj)
End Function</pre>
<h2>PersonSorter Class</h2>
<p>The PersonSorter class implements the IComparer interface to provide a more-flexible set of sorting option. It uses the CallByName() function in combination with the ProcedureName property to do the actual work of comparison.  In addition, the PersonSorter provides a means to change the direction of the sort using the Direction property.</p>
<pre>'--------------------------------------------------------------------------------
'Public Properties
'--------------------------------------------------------------------------------
Public Direction As DirectionEnum
Public ProcedureName As String</pre>
<pre>'--------------------------------------------------------------------------------
'Public Methods
'--------------------------------------------------------------------------------
Public Function Compare(x As Object, y As Object) As Integer
Compare = IComparer_Compare(x, y)
End Function

Public Function ToString() As String
ToString = IObject_ToString
End Function
'--------------------------------------------------------------------------------
'IComparer Methods
'--------------------------------------------------------------------------------
Private Function IComparer_Compare(x As Object, y As Object) As Integer

If CallByName(x, ProcedureName, VbGet) &gt; CallByName(y, ProcedureName, VbGet) Then
IComparer_Compare = 1 * Direction
ElseIf CallByName(x, ProcedureName, VbGet) &lt; CallByName(y, ProcedureName, VbGet) Then
IComparer_Compare = -1 * Direction
Else
IComparer_Compare = 0
End If

End Function</pre>
<p>The next challenge is to provide a means to swap two items in a collection.  This is done via the Swap method:</p>
<h2>Swap() Function</h2>
<pre>Private Sub Swap(ByRef Items As Collection, x As Long, y As Long)

'quality control
If x = y Then Exit Sub
If x &lt; 0 Or y &lt; 0 Then Exit Sub
If x &gt; Items.Count Or y &gt; Items.Count Then Exit Sub

'normalize positions
If x &gt; y Then
Dim t As Long
t = x
x = y
y = t
End If

'store items temporarily
Dim ItemX As Object: Set ItemX = Items(x)
Dim ItemY As Object: Set ItemY = Items(y)

'remove items
Items.Remove y
Items.Remove x

'For the swap to work, the Key property must be created.  While this could be implemented as an interface, I chose to simplify matters and simply add it to the Person class.
'add y to x's position
If x &gt; Items.Count Then
Items.Add ItemY, ItemY.Key
Else
Items.Add ItemY, ItemY.Key, x
End If

'add x to y's position
If y &gt; Items.Count Then
Items.Add ItemX, ItemX.Key
Else
Items.Add ItemX, ItemX.Key, y
End If

'finalize
Set ItemX = Nothing
Set ItemY = Nothing

End Sub</pre>
<p>Next, a mechanism is needed to sort the collection&#8211;the <a href="http://en.wikipedia.org/wiki/Bubblesort" target="_blank" title="BubbleSort">BubbleSort</a> algorithm was adapted for for this purpose.</p>
<h2>BubbleSort() Function</h2>
<pre>'Items - collection to be sorted 'Comparer - alternate sorting method Public Sub BubbleSort(ByRef Items As Collection, Optional Comparer As IComparer)

Dim i As Long, Sorted As Boolean

If Comparer Is Nothing Then

Do While Not Sorted
Sorted = True
For i = 1 To Items.Count - 1
If Items(i).CompareTo(Items(i + 1)) &gt; 0 Then
Swap Items, i, i + 1
Sorted = False
End If
Next
Loop

Else

Do While Not Sorted
Sorted = True
For i = 1 To Items.Count - 1
If Comparer.Compare(Items(i), Items(i + 1)) &gt; 0 Then
Swap Items, i, i + 1
Sorted = False
End If
Next
Loop

End If

End Sub</pre>
<h2>Usage</h2>
<p>For basic usage, add items to the PersonCollection, the call the Sort() method:</p>
<pre>Dim PC As New PersonCollection
With PC
.Add 1, "chris", "sellers", "6/13/1937"
.Add 5, "john", "gage", "10/10/1974"
.Add 3, "craig", "buchanan", "3/29/1966"
.Add 4, "jane", "buchanan", "12/26/1968"
.Add 2, "bill", "richards", "01/01/1900"
.Sort
End With

Enumerate PC</pre>
<p>For more control, add a PersonSorter class:</p>
<pre>Dim PS As New PersonSorter
With PS
.ProcedureName = "Age"
.Direction = DirectionEnum.Desc
End With

Dim PC As New PersonCollection
With PC
.Add 1, "chris", "sellers", "6/13/1937"
.Add 5, "john", "gage", "10/10/1974"
.Add 3, "craig", "buchanan", "3/29/1966"
.Add 4, "jane", "buchanan", "12/26/1968"
.Add 2, "bill", "richards", "01/01/1900"
.Sort PS
End With

Sub Enumerate(obj As Object)

Dim Item As Person
Debug.Print "--------------------------------------------------"
Dim o As IObject: Set o = obj.Comparer
Debug.Print o.tostring
Debug.Print "--------------------------------------------------"
For Each Item In obj
Debug.Print Item.Id &amp; "; " &amp; Item.FileAs &amp; "; " &amp; Item.Age
Next

End Sub</pre>
<h2>SortableCollection Project</h2>
<p><a href="/wordpress/wp-content/uploads/2006/12/sortablecollection.zip" id="p91">Sortable Collection Project</a></p>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2007/01/30/visual-basic-create-a-sorted-collection/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server: MaximumDate() Function</title>
		<link>http://cogniza.com/wordpress/2007/01/26/sql-server/</link>
		<comments>http://cogniza.com/wordpress/2007/01/26/sql-server/#comments</comments>
		<pubDate>Fri, 26 Jan 2007 15:04:05 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Transact-SQL]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=96</guid>
		<description><![CDATA[A SQL Server, scalar-value function that returns the greater of two dates.]]></description>
			<content:encoded><![CDATA[<p>A SQL Server, scalar-value function that returns the greater of two dates.<span id="more-96"></span></p>
<pre>set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO

/*----------------------------------------------------------------------------------------------------
Author:        Craig Buchanan
Date:        2/6/2006
Description:    Returns the greater of two dates
Parameters:    @Date1 - datetime to be compared
@Date2 - datetime to be compared
----------------------------------------------------------------------------------------------------*/
CREATE FUNCTION [dbo].[MaximumDate] (
@Date1    datetime,
@Date2    datetime
)

RETURNS datetime AS
BEGIN

RETURN CASE
WHEN @Date1 IS NULL AND @Date2 IS NULL THEN NULL
WHEN @Date1 IS NULL THEN @Date2
WHEN @Date2 IS NULL THEN @Date1
WHEN @Date1 &gt; @Date2 THEN @Date1
WHEN @Date2 &gt; @Date1 THEN @Date2
WHEN @Date2 = @Date1 THEN @Date2
ELSE NULL
END

END</pre>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2007/01/26/sql-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server: MinimumDate() Function</title>
		<link>http://cogniza.com/wordpress/2007/01/26/sql-server-minimumdate-function/</link>
		<comments>http://cogniza.com/wordpress/2007/01/26/sql-server-minimumdate-function/#comments</comments>
		<pubDate>Fri, 26 Jan 2007 15:02:30 +0000</pubDate>
		<dc:creator>Craig Buchanan</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Transact-SQL]]></category>

		<guid isPermaLink="false">http://www.cogniza.com/blog/?p=95</guid>
		<description><![CDATA[A SQL Server, scalar-value function that returns the lessor of two dates.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
/*----------------------------------------------------------------------------------------------------
Author:        Craig Buchanan
Date:        2/6/2006
Description:    Returns the lessor of two dates
Parameters:    @Date1 - datetime to be compared
@Date2 - datetime to be [...]]]></description>
			<content:encoded><![CDATA[<p>A SQL Server, scalar-value function that returns the lessor of two dates.<span id="more-95"></span></p>
<pre>set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
/*----------------------------------------------------------------------------------------------------
Author:        Craig Buchanan
Date:        2/6/2006
Description:    Returns the lessor of two dates
Parameters:    @Date1 - datetime to be compared
@Date2 - datetime to be compared
----------------------------------------------------------------------------------------------------*/
CREATE FUNCTION [dbo].[MinimumDate] (
@Date1    datetime,
@Date2    datetime
)
RETURNS datetime AS
BEGIN

RETURN CASE
WHEN @Date1 IS NULL AND @Date2 IS NULL THEN NULL
WHEN @Date1 IS NULL THEN @Date2
WHEN @Date2 IS NULL THEN @Date1
WHEN @Date1 &lt; @Date2 THEN @Date1
WHEN @Date2 &lt; @Date1 THEN @Date2
WHEN @Date2 = @Date1 THEN @Date2
ELSE NULL
END

END</pre>
]]></content:encoded>
			<wfw:commentRss>http://cogniza.com/wordpress/2007/01/26/sql-server-minimumdate-function/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

