XMLpitstop.com   |  VBnetexpert.com   |  Community Credit  
 
 
Pitstop Search:  
in
 
Sign in | Join | Help
 
 
  Blog
    Home  
 
  Entries By Date
 
<December 2007>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
 
 
  Blog Categories
   
 
  Archives
    December 2009 (2)  
    October 2009 (1)  
    September 2009 (1)  
    August 2009 (2)  
    July 2009 (2)  
    June 2009 (1)  
    May 2009 (1)  
    February 2009 (1)  
    December 2008 (1)  
    November 2008 (3)  
    September 2008 (3)  
    August 2008 (3)  
    June 2008 (4)  
    May 2008 (2)  
    April 2008 (3)  
    March 2008 (3)  
    February 2008 (5)  
    December 2007 (4)  
    November 2007 (1)  
    October 2007 (3)  
 
  Syndication
    RSS  
    Atom  
    Comments RSS  

December 2007 - Posts

  .NET Flea Market  
 

The Simple Sortable List for Databinding in DataGridViews

I had a need for a collection that would automatically use the sorting features of the datagridview in .NET. Everywhere I looked, people had code samples, but something was always missing. So here's the full version. All you have to do is inherit from this class. Like:

Public Class SoldByUnitCollection
    Inherits SortableBindingList(Of SoldByUnit)

End Class

Simple. Now here's the code:

Imports System.ComponentModel
Imports System.Collections.Generic
Imports System.Windows.Forms

<Serializable()> _
Public Class SortableBindingList(Of Tkey)
    Inherits BindingList(Of Tkey)
    Implements ITypedList

    <NonSerialized()> _
    Private properties As PropertyDescriptorCollection
    Private propertyDesc As PropertyDescriptor
    Private listSortDir As ListSortDirection
    Private isSorted As Boolean

    Protected Overrides ReadOnly Property SupportsSortingCore() As Boolean
        Get
            Return True
        End Get
    End Property

    Protected Overrides ReadOnly Property IsSortedCore() As Boolean
        Get
            Return isSorted
        End Get
    End Property

    Protected Overrides ReadOnly Property SortPropertyCore() _
    As System.ComponentModel.PropertyDescriptor
        Get
            Return propertyDesc
        End Get
    End Property

    Protected Overrides ReadOnly Property SortDirectionCore() _
    As System.ComponentModel.ListSortDirection
        Get
            Return listSortDir
        End Get
    End Property

    Public Sub New()
        MyBase.New()

        Dim pdc As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(Tkey), _
        	New Attribute() {New BrowsableAttribute(True)})

        properties = pdc.Sort()

    End Sub

    Public Function GetItemProperties(ByVal listAccessors() As System.ComponentModel.PropertyDescriptor) _
    	As System.ComponentModel.PropertyDescriptorCollection _
	    Implements System.ComponentModel.ITypedList.GetItemProperties

        Dim pdc As PropertyDescriptorCollection = Nothing

        If listAccessors Is Nothing Then
            pdc = properties
        Else
            pdc = ListBindingHelper.GetListItemProperties(listAccessors(0).PropertyType)
        End If

        Return pdc

    End Function

    Public Function GetListName(ByVal listAccessors() As PropertyDescriptor) _
    	As String Implements System.ComponentModel.ITypedList.GetListName

        Return GetType(Tkey).Name

    End Function

    Protected Overrides Sub ApplySortCore(ByVal prop As System.ComponentModel.PropertyDescriptor, _
    	ByVal direction As System.ComponentModel.ListSortDirection)
        
        Dim itemsList As List(Of Tkey)
        Dim c As New System.Comparison(Of Tkey)(AddressOf SimpleSort)

        Me.propertyDesc = prop
        Me.listSortDir = direction
        Me.isSorted = True

        itemsList = CType(Me.Items, List(Of Tkey))
        itemsList.Sort(c)


        Me.OnListChanged(New System.ComponentModel.ListChangedEventArgs(ListChangedType.Reset, -1))

    End Sub

    Protected Function SimpleSort(ByVal t1 As Tkey, ByVal t2 As Tkey) As Integer
        Dim reverse As Integer
        Dim propertyInfo As System.Reflection.PropertyInfo
        Dim value1 As Object
        Dim value2 As Object
        Dim comparable As IComparable
        Dim result As Integer

        reverse = CInt(IIf(listSortDir = ListSortDirection.Ascending, 1, -1))
        propertyInfo = GetType(Tkey).GetProperty(propertyDesc.Name)

        value1 = propertyInfo.GetValue(t1, Nothing)
        value2 = propertyInfo.GetValue(t2, Nothing)

        comparable = CType(value1, IComparable)

        If comparable IsNot Nothing Then
            result = reverse * comparable.CompareTo(value2)
        Else
            comparable = CType(value2, IComparable)
            If comparable IsNot Nothing Then
                result = -1 * reverse * comparable.CompareTo(value1)
            Else
                result = 0
            End If

        End If

        Return result

    End Function

End Class

Now was that so hard to share with everyone?

 
 
 
 

It's like the "Bag of Holding" for arrays

Here's a little bit of code I find amusing. Haven't you ever wanted an array that never ends? Or maybe better described as an array that loops? So if you have 10 items in your array and you ask for number 11, you get the first element again? I'm sure I have, but I don't remember when or why.

Anyway, here's a collection you can use for an (almost) infinite collection. It was actually a bit of trial and error to get the negative values to work properly.

Public Class ForeverCollection(Of T)
    Inherits Generic.List(Of T)

    Default Public Shadows ReadOnly Property Item(ByVal index As Integer) As T
        Get
            If Me.Count = 0 Then Throw New ArgumentOutOfRangeException("index")

            If index >= 0 Then
                Return MyBase.Item(Math.Abs(index Mod Me.Count))
            Else
                Return MyBase.Item(Me.Count - Math.Abs((index + 1) Mod Me.Count) - 1)
            End If

        End Get
    End Property
End Class

This uses the MOD operator which is a pretty fun bit of code. Its purpose is to return the leftover of any integer division. So 10 MOD 10 is 0, 12 MOD 10 is 2, 34 MOD 10 is 4. What good is that? Well, when you see the examples, it will make more sense.

You can use MOD for time calculations. You have 543 seconds on a timer. How many minutes and seconds is that? Which code is cooler?

allSecs = 543
min = 543 \ 60
sec = allSecs - (min * 60)
MsgBox(min & ":" & sec)

or

allSecs = 543
min = allSecs \ 60
sec = allSecs Mod 60
MsgBox(min & ":" & sec)

You can use MOD as a trigger. Lets say you have a counter and you want to annoy the user every X number of iterations.

For x As Integer = 1 To 1000
	If x Mod 15 = 0 Then
		MsgBox(String.Format("I've bugged you {0} time(s) " _
			& "and the counter is at {1}", (x / 15), x))
	End If
Next

You can use MOD for pseudo-random seed data in SQL. MOD in SQL is "%". Take the milliseconds of the current time and mod it by different numbers.

declare @i int
set @i=0
while @i<500
begin
	insert into userinfo(userid,createdate,logincount,
		lastwithdrawal,lastdeposit,currentbalance)
	select top 1 userid,getdate(),datepart(ms,getdate()),
		datepart(ms,getdate()) % 30,
		(datepart(ms,getdate()) % 15)*50,
		(datepart(ms,getdate()) % 25)*50,
		(datepart(ms,getdate()) % 10)*100
	from users
	order by newid()

	set @i=@i+1
end

I think of all the extra code I used to write before I discovered MOD and I think...

 
 
 
 

Delegatorial Rambling

I see in a sister blog a planned post regarding Delegates in .NET. I have used delegates before, and I think I have seen them misused before. But remaining on the positive, I'll describe what the delegate capability did for me.

In an application I was writing, I made the decision to extract the product search form from the main UI application; my thought was that it would be a common element that could be used in many different applications in the long-range-planned application suite. My choice was a good one, as I did use it in multiple applications, but it did get modified along the way.

The first version was simple: you search for products, you highlight the product(s) you want, and click "Select". The search form would raise an event that the calling form would handle and process. The search form also had a button to view product details. You could highlight a product and click "View Details" to bring up another form with the product information.

This worked great until I wanted to use it in another part of the same application. In this new section, there was no "Select" function needed, only "View Details". One choice I pondered was making the Select button Public instead of Friend (remember this code is in a shared DLL now). Then the calling form could enable or disable it. I also considered making a Boolean property like HideSelect. That seemed pretty tacky. But my overall goal with designing the code was that it do what the programmer tells it and do the most obvious thing by default.

Going with that mindset, I decided the Select button will do nothing unless you give it something to do. Goodbye Event, hello Delegate. I dropped the public event and created a delegate sub that matched the signature of my old event. Then I created a private variable to hold the delegate and created a public property to Get/Set the delegate.

What benefits did this gain? I was then able to see if the calling form needed the Select button. If the private variable was Nothing, I disabled the Select button. If the private variable was a valid instance, I invoked it when the Select button was clicked. When I was using events, I couldn't tell if anyone was subscribed to the event so I couldn't take any action on the Select button.

Back in the calling form, the code change was minimal. Instead of doing an AddHandler statement, I created a new instance of the delegate in the search form using AddressOf in the constructor and set it to the public property on the search form. The target method that handled the event never changed.

That's one way I used a delegate. The other way I used it was with the View Details button. For another application, the View Details was slightly different. In brief, the application needed to handle the display of the product instead of letting the search form do it. Again, I created a delegate sub, private variable and public property. In the click event of the View Details button, I checked to see if the delegate was Nothing. If it was Nothing, I performed the default action and displayed the product in the typical manner. If it was not nothing, I invoked the delegate instead of doing the default display. In this way, I was able to provide an override for default behavior.

Without using delegates, how could that have been done as easily? As I was determining how do implement the override feature, I considered using events, but that means that every calling form would have to handle that event in order to implement the default display action. That went against my design philosophy. I could have used a Boolean property, but...bleh. I insisted to myself that I would not have any shared library that required performing steps in a rigid fashion. Like, oh, the code bombed out. I forgot to call InitControls2() after setting the base properties. Yuck.

So that's my delegate story. In summary, I used Delegates instead of Events because I was able to test to see if they were needed using IsNothing() and they provided me with a way to optionally inject a call to a remote codebase (that codebase being the calling form). Sorry for no code samples. I don't have the code with me right now.

 
 
 
 

Page crushed under ASCX, then released using server controls.

Preface: When I originally did this, it was using ASP.NET 1.1 and it was really significant. But with ASP.NET 2.0, the viewstate handling is SO much better. That kind of makes this less relevant, but still could be a performance gain in special scenarios or especially if you are still using ASP.NET 1.1.

Let's say you want to show a grid of textboxes and you want the textboxes to have custom functionality. That sounds like a reusable control, right? Write once and reuse it many times. Well, it's great that ASP.NET allows you to do such a thing. They're ASCX files and they're very easy to create. So in your ASP.NET project you create a new ASCX control and add a textbox to it. This textbox should only accept numbers, so you go into the code and add some code to enforce keypresses:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
 Handles Me.Load
        If Not Me.Page.ClientScript.IsClientScriptBlockRegistered("NumberTextBox") Then
            Dim js As String
            js = "<script language=jscript>" & vbCrLf
            js &= "function ChkNumber(){" & vbCrLf
            js &= vbTab & "if ((event.keyCode ==47) " _
            	& "||(event.keyCode < 46 || event.keyCode > 57)) event.returnValue = false;" & vbCrLf
            js &= "}</script>"
            Me.Page.ClientScript.RegisterClientScriptBlock(GetType(String), "NumberTextBox", js)
        End If

        txtNumber.Attributes.Add("onkeypress", "ChkNumber();")
    End Sub

Then you use this control in your grid. Let's say it's a 10x8 grid.

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="UserControls.aspx.vb"
	Inherits="TestControls.UserControls" %>
<%@ Register TagPrefix="cb" TagName="ASCTextBox" Src="~/ASCXTextBox.ascx" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server"><title>ASCX Controls</title></head>
<body>
    <form id="form1" runat="server">
    <div>
        <cb:ASCTextBox runat="server" id="ASCTextBox0" />
        <cb:ASCTextBox runat="server" id="ASCTextBox1" />
        <cb:ASCTextBox runat="server" id="ASCTextBox4" />
        <cb:ASCTextBox runat="server" id="ASCTextBox5" />
        <cb:ASCTextBox runat="server" id="ASCTextBox8" />
        <cb:ASCTextBox runat="server" id="ASCTextBox9" />
        <cb:ASCTextBox runat="server" id="ASCTextBox12" />
        <cb:ASCTextBox runat="server" id="ASCTextBox13" />
...
    </div>
    </form>
</body>
</html>

You view the page and it looks good. Then you happen to look at the source and you see this:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 
value="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" />

You pause for a moment and say "Eh. Whatever. We've got fast servers" Your co-worker looks over, sees your screen and starts laughing at you.

"Your viewstate sucks, man!"

So you're now on the defensive. "That was just a layout test. I have a different control I'm using for performance." It sounded convincing, but you need to produce this control like, now.

Fortunately, there is a super fast way to get custom functionality into a web control and avoid the viewstate glut of ASCX controls. They're server controls and they are super easy to do. Here's the code to do what the above control did:

Public Class NumberTextBox
    Inherits TextBox

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        If Not Me.Page.ClientScript.IsClientScriptBlockRegistered("NumberTextBox") Then
            Dim js As String
            js = "<script language=jscript>" & vbCrLf
            js &= "function ChkNumber(){" & vbCrLf
            js &= vbTab & "if ((event.keyCode ==47) || " _
            	& "(event.keyCode < 46 || event.keyCode > 57)) event.returnValue = false;" & vbCrLf
            js &= "}</script>"
            Me.Page.ClientScript.RegisterClientScriptBlock(GetType(String), _ 
            	"NumberTextBox", js)
        End If

    End Sub

    Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
        Me.Attributes.Item("onkeypress") = "ChkNumber();" & Me.Attributes.Item("onkeypress")
        MyBase.AddAttributesToRender(writer)
    End Sub

End Class

It's actually just about the same amount of code, but it is a real textbox, with all the events, properties and methods. With an ASCX control, you'd have to jump thorugh a lot of hoops to get at the textbox inside the ASCX control. So what is the viewstate like after this change?

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUIMTAxNDAxNzVkZDG8dSjZI4sEFKBynBmvLjF/Ytxk" />

Yeah.

 
 
 

 
Copyright © . All Rights Reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems