Monday, September 12, 2011

Google-like Filtering with RadGrid and Separate Data Class


I needed to create a Google Suggest style search interface for an ASP.NET page.

Our site recently obtained licenses to the Telerik control suite, and the powerful RadGrid control looked like it was just what I needed. I found this example on the Telerik site.

Key to this setup is having a textbox for the user to input filter criteria, which is then from the user's perspective transformed into a RadComboBox object to house matching results; then once the user selects an item from the dropdown, the RadGrid is bound and displays any matching results.

I encountered a problem because our site uses a centralized class library for CRUD outside of this particular page’s code class, meaning I could not access my application’s data context from within the GridBoundColumn class definition. The Telerik example (specifically in the MyCustomFilteringColumnCS.cs file) performs its own queries as needed using a SqlDataAdapter with a ConnectionString obtained from the application configuration. A simple SELECT statement is executed which returns matching results.

When I tried to reference my centralized data class from within the GridBoundColumn class definition, I got the following error:

Cannot access non-static property … in static context.


Below is the problem code, revealing that the property CurrentUser is inaccessible to the class.

       protected void list_ItemsRequested(object o, RadComboBoxItemsRequestedEventArgs e)
     {
        // Cannot access non-static property 'CurrentUser' in static context.
        using (MyCRUD mc = new MyCRUD(CurrentUser) )
     
  {
              ((RadComboBox)o).DataTextField = DataField;
              ((RadComboBox)o).DataValueField = DataField;
              ((RadComboBox)o).DataSource = mc.GetMatchingAddresses(e.Text);
              ((RadComboBox)o).DataBind();
        }
     }


My GridBoundColumn class does not exist until my application instantiates it with its parent RadGrid object, so I cannot directly assign a property to it. However, I stumbled upon this post which made me realize I could, in the GridBoundColumn class definition, make several changes.

  1. Define a constructor for the class which takes an existing instance of the MyCRUD class as input.
  1. Create a public property in the class definition which can be assigned the MyCRUD object.
  1. Create a private field in the class definition to contain the instance of the MyCRUD object to be utilized by the GridBoundColumn class.
 
Below is the modified class, with additions (*) indicated below.

       public class rgcFilterColumn : GridBoundColumn
      {   
            // * I added a constructor with an input parameter of the type 
            // * corresponding to my app’s CRUD object.       
            public rgcFilterColumn(MyCRUD mycrud)
            {
                TheDataContext = mycrud;
            }

            // * This field provides an instance of the rgcFilterColumn class 
            // * with the corresponding value set for the data context object.
            private readonly MyCRUD TheDataContext;

            // * This property enables the process which instantiates this 
            // * class to assign the MyCRUD object to TheDataContext.
            public MyCRUD thedatacontext
            {
                get { return TheDataContext; }
            }


     // RadGrid will call this method when it initializes
     // the controls inside the filtering item cells
            protected override void SetupFilterControls(TableCell cell)
            {
                base.SetupFilterControls(cell);
                cell.Controls.RemoveAt(0);
                RadComboBox combo = new RadComboBox
                {
                    ID = ("RadComboBox1" + UniqueName),
                    ShowToggleImage = false,
                    Skin = "Office2007",
                    EnableLoadOnDemand = true,
                    AutoPostBack = true,
                    MarkFirstMatch = true,
                    Height = Unit.Pixel(100)
                };
                combo.ItemsRequested += list_ItemsRequested;
                combo.SelectedIndexChanged += list_SelectedIndexChanged;
                cell.Controls.AddAt(0, combo);
                cell.Controls.RemoveAt(1);
            }

   // RadGrid will call this method when the value should
   // be set to the filtering input control(s)
           protected override void SetCurrentFilterValueToControl(TableCell cell)
           {
                base.SetCurrentFilterValueToControl(cell);
                RadComboBox combo = (RadComboBox)cell.Controls[0];
                if ((CurrentFilterValue != string.Empty))
                {
      combo.Text = CurrentFilterValue;
        }
     }

   //
RadGrid will call this method when the filtering value
   // should be extracted from the filtering input control(s)
           protected override string GetCurrentFilterValueFromControl(TableCell cell)
           {
                RadComboBox combo = (RadComboBox)cell.Controls[0];
                return combo.Text;
           }


           protected void list_ItemsRequested(object o, RadComboBoxItemsRequestedEventArgs e)
           {
                // * Below we use the value of field TheDataContext to execute 
                // * a method accesible via the MyCRUD data context for the application.
                using (MyCRUD mc = TheDataContext)         
                {
                     ((RadComboBox)o).DataTextField = DataField;
                     ((RadComboBox)o).DataValueField = DataField;
                     ((RadComboBox)o).DataSource = mc.GetMatchingAddresses(e.Text);
                     ((RadComboBox)o).DataBind();
                }
           }

           private void list_SelectedIndexChanged(object o, RadComboBoxSelectedIndexChangedEventArgs e)
           {
                GridFilteringItem filterItem = (GridFilteringItem)((RadComboBox)o).NamingContainer;
                if ((UniqueName == "Index"))
                {
                    // this is filtering for integer column type
                    filterItem.FireCommandEvent("Filter", new Pair("EqualTo", UniqueName));
                }
                // filtering for string column type
                filterItem.FireCommandEvent("Filter", new Pair("Contains", UniqueName));
           }

      }



Now an instance of the GridBoundColumn class can happily utilize my application's central CRUD class for all its data retrieval operations.


Thursday, August 18, 2011

There, Their...


Thursday, July 28, 2011

Google+: Why... So... Serious??

I just tried to use Google's +1 feature, but inexplicably saw red, an exclamation point icon appeared instead of the usual cheerful blue +1. Upon clicking this, I was greeted with the following:
Your profile is currently suspended
Until your profile is unsuspended, you will be unable to create +1's.

Wat??

Initially this smacked of the kind of shit I've experienced on Yahoo Answers, where a profile you might invest a lot of time and effort in gets suspended because you post a controversial answer that a bunch of people dislike and report to indulge their passive aggressive tendencies. A closer look at my profile revealed this additional clarification.
Your profile is suspended

After reviewing your profile, we determined that the name you provided violates our Community Standards.

If you believe that your profile has been suspended in error, please provide us with additional information via this form, and we will review your profile again.

Google's rules provided this additional tidbit:
Display Name
To help fight spam and prevent fake profiles, use the name your friends, family, or co-workers usually call you. For example, if your full legal name is Charles Jones Jr. but you normally use Chuck Jones or Junior Jones, either of these would be acceptable.

Google+ is still being tested so to me this is no biggie, but it is an annoyance. Some people might not want to use their real name, some might want to create a profile with a fake name for their cat, dog, business, narwhal, whatever.

They have provided the option to provide optional verification in the form of a photo ID...

Additional Verification Information (Optional Section)

Attach a copy of your ID with your name and photo clearly visible. You can block out other personal information. Your ID will only be used to verify your name and will be deleted after review.

I chose not to pursue this option, since I know who I am and I feel Google needn't know more than whom I've presented through my online persona. Facebook, certainly, doesn't mind...

Hopefully Google will unclench their social networking buttcheeks and let people have some latitude in terms of naming their profiles.




Tuesday, July 26, 2011

Explorer.EXE Crashes and Restarts Constantly

After a reboot, Windows Explorer began to crash and restart constantly with an Event ID 1000 error. Windows Help was useless, so I opened Event Viewer and saw the following:



It's curious that a file which is part of the third-party Windows 7 Codec pack seems to be responsible:
Faulting module path: C:\Program Files\Win7codecs\filters\DivXMFSource.DLL

Fortunately, I got around this issue by simply uninstalling the codec pack as described here, by executing the file Win7codecs.msi in a subfolder contained immediately under C:\ProgramData\Win7codecs\, and then choosing Remove (the only option available via Control Panel \ Programs \ Programs and Features is to perform a "Repair", which didn't help in my case).