Quantcast
Channel: ASP.NET – Code Corner
Viewing all 79 articles
Browse latest View live

Totally custom filter for UltraWebGrid using LINQ

$
0
0

Last time I described how LINQ can be used to custom filter ADO.NET DataTable. This time I will demonstrate how this technique can replace built-in server-side filtering in Infragistics UltraWebGrid.

By default if server-side filtering is enabled in UltraWebGrid controls, it displays a small “funnel” icon in the column header, if you click this icon – a list of unique values from this column is displayed and selecting a value filters the grid by displaying only rows that have this value.

This default behavior works, sort of – it has many issues. If your grid has paging enabled – only values from the current page will be displayed. If your columns or values has characters that grid doesn’t like (commas, square brackets etc.) an exception will be thrown (this happens because under the hood grid converts your filters into a string that is passed to DataTable’s RowFilter), there’re other issues as well.

Why leave anything to chance when you can provide filtering yourself?

First, using method described here we can collect filter values for columns across all the pages. This is the only part of the grid we will be using for filtering – just to hold filter values, but not to perform actual filtering – this we will do ourselves.

Second, we need to collect selected filter values (you can have more than 1 filter applied, since every column can be filtered by). The place to do it is grid’s RowFilterApplying event handler and we can store values in a Generic Dictionary (using filter’s Column Index as key and filter’s value as, well, value). This is how it looks in code:

Protected Sub xmyGrid_RowFilterApplying(sender As Object, e As UltraWebGrid.FilterEventArgs) Handles xmyGrid.RowFilterApplying

   'canceling event since we will be doing filtering ourselves
   e.Cancel = True

   'aLinqFilter Dictionary contains custom filters for grid's underlying table
   Dim aLinqFilter As Generic.Dictionary(Of Integer, String) = Session("LinqFilters")
   'if no filters exist - create new holder
   If aLinqFilter Is Nothing Then aLinqFilter = New Generic.Dictionary(Of Integer, String)

   'if custom filter collection already contains currently filtered column - remove old filter
   If aLinqFilter.ContainsKey(e.ActiveColumnFilter.Column.Index) Then aLinqFilter.Remove(e.ActiveColumnFilter.Column.Index)

   'depending on selected filter condition - create custom filter. Format {Column Index, Filter Value }
   Select Case e.AppliedFilterCondition.ComparisonOperator
      Case FilterComparisionOperator.Empty
         'Storing filter Value as "{isnull}" for "Empty" condition
         aLinqFilter.Add(e.ActiveColumnFilter.Column.Index, "{isnull}")
      Case FilterComparisionOperator.NotEmpty
         'Storing filter Value = "{isnotnull}" for "NotEmpty" condition
         aLinqFilter.Add(e.ActiveColumnFilter.Column.Index, "{isnotnull}")
      Case 3 'Equal
         'otherwise storing actual filter value
         aLinqFilter.Add(e.ActiveColumnFilter.Column.Index, e.AppliedFilterCondition.CompareValue.ToString)
   End Select

    'storing resulting filter in session variabe - it will be used on later onevery grid datbind
   Session("LinqFilters") = aLinqFilter

End Sub

When user selects a value in filter drop-down this event handler is called. We’re canceling it immediatly so not to allow grid perform it’s build-in filtering and at this time simple collect filter value and index of the column it belongs to.

Next we need to finish filtering in RowFilterApplied applied event by binding grid to the DataTable:

Protected Sub xmygrid_RowFilterApplied(sender As Object, e As UltraWebGrid.FilterEventArgs) Handles xmygrid.RowFilterApplied

   xmyGrid.Clear() '-- removing old data from grid so new filter can be applied correclty
   BindDataTableToGrid() ' -- calling method to bind data to the grid

End Sub

And now the most interesting part. Using method, described in previous post we will filter our DataTable during databinding in BindDataTableToGrid method:

 Sub BindDataTableToGrid()

   'reading table with data from Session
   Dim dtTable As DataTable = Session("MyData")

   'aLinqFilter holds custom filters for the grid, it gets created/updated in RowFilterPopulating event
   Dim aLinqFilter As Generic.Dictionary(Of Integer, String) = Session("LinqFilters")

   'IF aLinqFilter (custom filters) is populated - applying filters to DataTable via LINQ WHERE clause
   If aLinqFilter IsNot Nothing AndAlso aLinqFilter.Count > 0 Then
      xmyGrid.DataSource = dtTable.AsEnumerable.Where(Function(dr As DataRow) FilterDataRows(dr, aLinqFilter)).AsDataView
   Else 'if there're no filters - binding grid to original DataTable
      xmyGrid.DataSource = dtTable
   End If

   xmyGrid.DataBind()

End Sub

Here first we getting reference to original unfiltered DataTable (in this case from Session variable, but it can come from other sources), then we reading Dictionary with filters created in RowFilterPopulatingEvent. If filters exist – we use them in DataTable’s WHERE extension method using our own custom filtering function FilterDataRows. If, on the other hand there’re no filters – we binding UltraWebGrid to original unfiltered DataTable.

And the final touch – custom filtering function FilterDataRows:

Function FilterDataRows(i_oRow As DataRow, i_aLinqFilter As Generic.Dictionary(Of Integer, String)) As Boolean
   Dim bTest As Boolean
   Dim oColValue As Object

   'Looping thru custom filters
   For Each oFilterPair In i_aLinqFilter

      'reading current row's cell value that corresponds filter columns
      oColValue = i_oRow(oFilterPair.Key)

      Select Case oFilterPair.Value
         Case "{isnull}" 'if filter is checking for "Empty" value
            bTest = (oColValue Is DBNull.Value) 'comparing value to NULL
         Case "{isnotnull}" 'if filter is checking for "NotEmpty" value
            bTest = (oColValue IsNot DBNull.Value) 'comparing value to NOT NULL
         Case Else 'otherwise comparing cell value to actual filter value
            bTest = (oColValue IsNot DBNull.Value AndAlso oColValue = oFilterPair.Value)
      End Select

      'if at least one filter condition failed - return False and current row will be hidden
      If bTest = False Then Return False 

   Next

   Return True 'if all filter condition pass - return True and current row will be visible

End Function

This function is called for every row in the DataTable, it compares row values to filter values in our filter Dictionary and if row passes all conditions, TRUE is returned and row remains visible, otherwise FALSE is returned and row doesn’t make the cut.

So, let’s review the flow:

  1. User clicks “funnel” icon in the column header and selects value to filter by from the dropdown
  2. RowFilterPopulating event handler is called and filter value is collected in Dictionary
  3. RowFilterPopulated event handler is called which calls method to bind the grid to data
  4. This method applies Dictonary with filters collected in RowFilterPopulating to DataTable
  5. During filtering process a custom filtering function is called to determine every row eligibility

Therefore if you use this approach – every step of the filtering process is in your hands. For example if you need to include some additional logic into FilterDataRows comparasing function – you’re free to do so. Also, BindDataTableToGrid method can be called from other grid events (paging, sorting etc.) and current filter will always be applied automatically.

The only drawback of this method – filter icons in column headers do not automatically change image to indicate that “filter was applied”, but this can be corrected by method similar to one described hereX9YY7N3UYA8A


Solution for WebHierarchicalDataGrid “DataKeyField is invalid” error

$
0
0

If you’re using Infragistics WebHierarchicalDataGrid and getting “DataKeyField is invalid” error after assigning DataKeyFields property for the root level:

xMyGrid.DataKeyFields = "[Key 1],[Key 2],[Key 3]"

and trying some data manipulation (like deleting rows, binding data etc.), try using GridView property of the grid instead:

xMyGrid.GridView.DataKeyFields = "[Key 1],[Key 2],[Key 3]"

The first method used to work, but somewhere around 2011 release of NetAdvantage it broke.

WebDataTree returns multiple selected nodes in single-select mode

$
0
0

This could be a bug in Infragistics WebDataTree control or it could be a very specific scenario (maybe it’s even by design, I don’t know) but here’s a weird behavior that I encountered. Using following code in WebDataTree’s event handler (oh and by the way, tree control is in a single-select mode):

Protected Sub xMyTree_SelectionChanged(ByVal sender As Object, ByVal e As NavigationControls.DataTreeSelectionEventArgs) Handles MyTree.SelectionChanged
   if e.NewSelectedNodes(0).Text = "Some Value"
      'do something with newly selected tree node
   end if
End Sub

I intended to use newly selected node to perform some operations. To my surprise e.NewSelectedNodes(0) referred to previously selected node (despite the name of the property) and newly selected value were located in e.NewSelectedNodes(1)! So the quick workaround was to use slightly modified code:

Protected Sub xMyTree_SelectionChanged(ByVal sender As Object, ByVal e As NavigationControls.DataTreeSelectionEventArgs) Handles MyTree.SelectionChanged
   if e.NewSelectedNodes(e.NewSelectedNodes.Count-1).Text = "Some Value"
      'do something with newly selected tree node
   end if
End Sub

This way it will always get latest selected node and the solution is universal. If the bug described above occurs – code will grab e.NewSelectedNodes(1) (because Count = 2). If the tree behaves as expected – code will get grab e.NewSelectedNodes(0) (because Count = 1)

WHDG: Correctly detect Async Callback

$
0
0

When using Infragistics WebHierarchicalDataGrid often there’s a need to detect grid’s async callback (for example we need to rebind the grid if it performs AJAX paging or expanding children). For flat WebDataGrid it was suggested to use grid’s RunBot.IsCallback property:

If xMyGrid.RunBot.IsCallback Then
   'Perform some operations for grid's async callback
End If

Now, WHDG also has RunBot.IsCallback property, but the above code always returns False for it. I know I’ve seen this before – a situation where a property doesn’t work on WHDG control itself, but works on the WHDG’s GridView object. Sensing a pattern I modified the code above for WHDG:

If xMyGrid.GridView.RunBot.IsCallback Then
   'Perform some operations for grid's async callback
End If

And bingo! Now Async callback is detected correctly.


UPDATE 4/27/2012: Turned out the above method doesn’t always work either. (To Infragistics stuff, if anybody happened to read this: The scenario above happens with WHDG in manual load on demand mode when grid’s RowIslandPopulating event is handled and ContainerGrid is created on demand and bound to data retrieved from the Database). The above method works when rows of the top level expanded (xMyGrid.GridView.RunBot.IsCallback is True) but fails if child rows are expanded to show grandchildren (in that case xMyGrid.GridView.RunBot.IsCallback is False).

We need another more reliable way to detect callback, no matter what caused it. And we can do that by checking HTTP Request. If it’s an Infragistics callback it will have a Form parameter with key like "__IGCallback************". So the code to always detect the callback correctly is:

If Request.Form.AllKeys.Any(Function(S As String) S IsNot Nothing AndAlso S.StartsWith("__IGCallback")) Then
   'Perform some operations for grid's async callback
End If

SSRS viewer doesn’t work on IIS7 64bit

$
0
0

If you’re developing an ASP.NET application that utilizes Microsoft’s SSRS ReportViewer control, you may experience a weird behavior while trying to use it on 64 bit version of IIS7 (Windows Server 2008 or Windows 7) – nothing is displayed in the main report area and even icons, such as Print, Export aren’t rendering.

One possible cause for this – your application is using Application Pool with 32bit mode enabled. If so – disable it or switch to a different application pool. Chances are this will fix the above issue.

Speed up UltraWebGrid rendering on rebind

$
0
0

If you’re using Infragistics UltraWebGrid with it’s property Browser="Xml", you may find yourself living in a shotgun shack in a strange situation: When grid is rebinding – it takes (comparatively) short time to do server-side processing and then a very long time to render grid in the browser.

In my case it was most felt when grid (which had ViewType="OutlookGroupBy") was initially grouped with a lot of data in each group and then ungrouped back into flat view when user dragged last group column out. Using IE9 built-in developer tools I ran JavaScript profiler I found that culprit was Infragistics JS function “disposeNode” which was called numerous times and had worst both inclusive and exclusive execution time.

Apparently what was happening – before bringing in fresh data grid was destroying all XML nodes of existing data, which – when grid is grouped with a lot of data in each group – is a lot of nodes. I decided to test what would happen if I destroy all top-level nodes in advance, so grid’s code won’t have to recursively loop through each one of them. I did this with just these 2 lines of JavaScript code:

var oGrid = igtbl_getGridById('xMyGrid');
if (oGrid) oGrid.Rows.SelectedNodes.removeAll();

Basically here we get a reference to grid’s client-side object and clear all nodes associated with grid’s rows. My grid didn’t use it’s own AJAX “LoadOnDemand” features and was located inside of Infragistics WARP panel, so I put that code inside of WARP panel’s RefreshRequest event handler which is called before grid’s server-side rebind. Your setup may be different, but you just have to run these lines before grid gets new data.

Result: Rendering in the client almost 90% faster.

‘this.Column.Band’ is null or not an object error in UltraWebGrid filtering

$
0
0

When you filter Infragistics UltraWebGrid by clicking Filter icon in the column header – dropdown with filter values appears. Normally if you click elsewhere on the page – dropdown disappears. But sometimes it doesn’t, for example if you click an element that invokes a JavaScript function. Filter dropdown stays open and this could cause problems – when grid refreshes (and possibly other actions are performed) error is thrown:

‘this.Column.Band’ is null or not an object

The solution to this is to close filter dropdown ourselves. Put these lines into your client-side code whenever clicking outside the filter doesn’t close it:

var aGridCols = igtbl_getGridById('xMyGrid').Bands[0].Columns
   for (var I = 0; I < aGridCols.length; I++)
      aGridCols[I].showFilterDropDown(false);

Here we’re looping thru grids column collection (This example assumes grid is in Flat or OutlookGroupBy mode, if your grid is hierarchical, you will have to loop thru band collection as well). For every column we pass False to showFilterDropDown method which (probably showing Infragistics cute sense of humor) hides filter dropdown if it is open. If dropdown is hidden for the column already – nothing happens.

As a result opened filter dropdown is now always closed prior to previously offending action and the error doesn’t happen.

(Possible) solution for MSChart ArgumentException The image is not found error

$
0
0

If you’re using Microsoft’s .NET charting control on your ASP.NET pages you may receive an annoying error

[ArgumentException]: The image is not found. at
System.Web.UI.DataVisualization.Charting.ChartHttpHandler.ProcessSavedChartImage(HttpContext context) at
System.Web.UI.DataVisualization.Charting.ChartHttpHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext context) at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

I could be intermittent, sometimes happen, sometimes not. In your web.config you already set your temp images to be stored as files and not deleted after serving:

<add key="ChartImageHandler" value="storage=file;timeout=20;url=~/temp/;deleteAfterServicing=false" />

as well as trying other remedies and nothing helps.

One possible solution is, while still using file temp storage, instead of indicating relative path via url property set it to absolute path via dir property:

<add key="ChartImageHandler" value="storage=file;timeout=20;dir=C:\TEMP;deleteAfterServicing=false" />

In the example above C:\TEMP is an absolute path to the temp folder on the Web Server. In your case it could be different; in multi-server (WebFarm) environment it should be a network share, accessible by all servers.


UltraWebGridExcelExporter: Export more than 65536 rows

$
0
0

When exporting data from Infragistics UltraWebGrid into Excel using UltraWebGridExcelExporter ordinary a very basic code similar to this is used:

'
' define Exporter "xMyExporter"
' define UltraWebGrid "xMyGrid" and load grid with data, then:
'
xMyExporter.ExportMode = UltraWebGrid.ExcelExport.ExportMode.Download
xMyExporter.Export(xMyGrid)

That’s it. But, if your data exceed 65536 rows you will get an error:

System.InvalidOperationException: The maximum number of rows in an excel worksheet with the current format is: 65536

This is not Excel Exporter limitation. In fact this is a limitation of Excel 2003 XLS format, which is a default output format of the Excel Exporter. The good news – this can be easily changed. Consider the following code:

'
' define Exporter "xMyExporter"
' define UltraWebGrid "xMyGrid" and load grid with data, then:
'
Dim oBook As New Excel.Workbook(Excel.WorkbookFormat.Excel2007)

xMyExporter.ExportMode = UltraWebGrid.ExcelExport.ExportMode.Download
xMyExporter.Export(xMyGrid, oBook)

In this version we first defined an Excel Workbook (Line 5) and gave it Excel 2007 format. And then used that workbook in actual export (Line 8). Which produces an Excel 2007 XLSX format capable of storing over 1 million rows.

Regenerate JavaScript function code in ASP.NET partial postback after initial load

$
0
0

ASP.NET has a handy way of generating client side JavaScript code, but using it can be sometimes unpredictable. For example your client-side script needs to call function MyAlert, but the function itself is generated server-side on page load:

ClientScript.RegisterStartupScript(Me.GetType, "JSCode", _
                  "function MyAlert() {alert('This is the FIRST call')}", True)

Function is generated, and in due time, when needed is called by the client and the message “This is the FIRST call” is displayed. All is well.

Now, your page also has an UpdatePanel, and during a partial postback you need to modify that client-side function:

ScriptManager.RegisterStartupScript(Me, Me.GetType, "JSCode", _
                  "function MyAlert() {alert('This is the SECOND call')}", True)

In due time, when again needed by client code, function MyAlert is called and the message (wait for it, you’re in for a surprise) “This is the FIRST call” is displayed again. Originally generated function is called and second generation is ignored.

To rectify the situation you need to declare function as a variable (empty initially) in your client code:

var MyAlert;

And then modify server-side generation code to use assignment notation (instead of function MyAlert use MyAlert = function):

Initial load:

ClientScript.RegisterStartupScript(Me.GetType, "JSCode", _
                  "MyAlert = function() {alert('This is the FIRST call')}", True)

and partial postback

ScriptManager.RegisterStartupScript(Me, Me.GetType, "JSCode", _
                  "MyAlert = function() {alert('This is the SECOND call')}", True)

after these changes client-side code behaves as expected and freshly generated function is called every time.

Solution for “Operation is not valid due to the current state of the object” error

$
0
0

Typically this happens when AJAX controls are involved (Microsoft’s UpdatePanel or others that utilize AJAX POST requests.) The error may be displayed outright or, if nothing is shown on the UI – logged in the Event Viewer. In any case you will get a message similar to:

0x800a139e – Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerServerErrorException: Operation is not valid due to the current state of the object

Issue happens because Microsoft Security Update MS11-100 limits number of keys in Forms collection during HTTP POST request. To alleviate this problem you need to increase that number.

This can be done in your application Web.Config in <appSettings> section (create the section directly under <configuration> if it doesn’t exist. Add 2 lines similar to the lines below to the <appSettings> section:

<add key="aspnet:MaxHttpCollectionKeys" value="2000" />
<add key="aspnet:MaxJsonDeserializerMembers" value="2000" />

The above example set the limit to 2000 keys. This will lift the limitation and the error should go away.

UltraWebGrid: “Input string was not in a correct format” error while setting width of a column

$
0
0

Infragistics UltraWebGrid allows you to set width of a column in client-side JavaScript, you can do it using code similar to this:

igtbl_getGridById('xmyGrid').Bands[0].Columns[3].setWidth(someValue)

But, while executing this code on the client, you may get a nasty surprise by getting following error from the server:

Input string was not in a correct format.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.FormatException: Input string was not in a correct format.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[FormatException: Input string was not in a correct format.]
System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) +10163939
System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) +207
Infragistics.WebUI.UltraWebGrid.StateChangeProcessor.ProcessChanges() +6230
Infragistics.WebUI.UltraWebGrid.UltraWebGrid.ProcessChanges(StateChanges stateChanges, Boolean fireEvents) +701
Infragistics.WebUI.UltraWebGrid.UltraWebGrid.LoadPostData(String postDataKey, NameValueCollection values) +7580
System.Web.UI.Page.ProcessPostData(NameValueCollection postData, Boolean fBeforeLoad) +945
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2071

Now, you do know that your someValue is definitely a number, why this error then? Let alone – why client-size resize causes a server call? But I will leave this to Infragistics. Actual cause of the error – number not being Integer. If you change the code from above to

igtbl_getGridById('xmyGrid').Bands[0].Columns[3].setWidth(Math.ceil(someValue))

it will execute without any errors. Go figure.

UltraWebGrid strange behavior in IE8 when IE=EmulateIE7

$
0
0

If you’re using Infragistics UltraWebGrid in Internet Explorer 8 you may experience strange visual (and other) issues if you have page compatibility set to emulate IE7, e.g.

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

Issues can range from column moving not working to weird distortion in grouped mode. According to Infragistics this combination is not supported as “unstable”, but you can still make it work.

Infragistics keeps internal flags of what browser is currently in use: ig_shared.IsIE7, ig_shared.IsIE8, etc. The trick is to detect IE8 and make it fall back to IE7:

if (ig_shared.IsIE8) {
    ig_shared.IsIE8 = false;
    ig_shared.IsIE7Compat = true;
}

Place this code on top of your page JavaScript code and when it sees IE8 it will tell grid to use IE7 rendering. This is most definitely a hack, but it works.

Solution: WebDataGrid loses styles after postback

$
0
0

Infragistics WebDataGrid control offers very flexible styling option – you can set font, color, size of almost any element from column to individual cell. Here is an example of basic markup that sets CSS classes of overall grid control, grid header, grid odd row and grid even row:

<ig:WebDataGrid ID="xMyGrid" runat="server"
   CssClass = "GridStyle"
   HeaderCaptionCssClass = "GridHeaderCellStyle"
   ItemCssClass="GridCellStyle"
   AltItemCssClass="GridAltCellStyle" 
>

And corresponding example of CSS class for even row:

tbody > tr.GridAltCellStyle > td
{
   font-size:11px;
   font-weight:normal;
   font-family:Verdana, Geneva, sans-serif; 
   height:20px;
   padding: 2px 2px 2px 2px;
   border-bottom: none;
   border-right: 1px solid rgb(190,198,178);
   background-color:rgb(240,240,240);
}

(For a complete grid styling reference take a look at this styling guide)

And it all works fine, your grid renders so beautifully, Picasso would cry. But only on initial page load. And here comes dreadful postback. (Or partial postback if your grid is inside of UpdatePanel). Suddenly all your wonderful styles are lost, the grid resets to Default style and looks like a mess.

This happens because on postback grid’s default styles take over. Oh, reference to your styles is still there, but they get overridden. This doesn’t happen on initial page load, only on postback. The solution to this isn’t pretty, but it works. Just add !important modifier to your style elements:

tbody > tr.GridAltCellStyle > td
{
   font-size:11px !important;
   font-weight:normal !important;
   font-family:Verdana, Geneva, sans-serif !important; 
   height:20px !important;
   padding: 2px 2px 2px 2px !important;
   border-bottom: none !important;
   border-right: 1px solid rgb(190,198,178) !important;
   background-color:rgb(240,240,240) !important;
}

This will prevent your styles from being overridden and they will persist across the postbacks.

Infragistics: Detect type of AJAX callback

$
0
0

This post continues topic started in “WHDG: Correctly detect Async Callback“. Back then I described a way to correctly detect server-side when an infragistics control (WebHierarchicalDataGrid) issues an async callback.

But what if you need to detect actual type of the call and distinguish paging from sorting from filtering from row expanding? Well, where there is will there is way. Infragistics says there’s no build-in flag indicating AJAX action, but we can determine the action by intercepting and interpreting HTTP Request object.

First let’s define a return type in form of an Enum describing all possible outcomes (it’s always good to work strongly typed values):

Public Enum GRID_AJAX_CALL_TYPE
   NON_AJAX = 0
   PAGING = 1
   SORTING = 2
   FILTERING = 3
   CHILD_POPULATING = 4
End Enum

And now actual function. In the previous post I described that presence of AJAX call can be determined if key starting with "__IGCallback" is present in Request.Form collection. By interpreting actual value of Request item with that key we can determine type of AJAX call. For example, when grid’s row is expanding during load-on-demand, it contains JSON data "eventName":"Populating","type":"loadOnDemand" so our function simple has to catch it:

Function GetGridAjaxCallType() As GRID_AJAX_CALL_TYPE
   Dim sIgCallBackKey As String = HttpContext.Current.Request.Form.AllKeys.SingleOrDefault(Function(S As String) S IsNot Nothing AndAlso S.StartsWith("__IGCallback"))
   Dim sIgCallBackRequest As String

   If sIgCallBackKey <> "" Then
      sIgCallBackRequest = HttpContext.Current.Request.Form(sIgCallBackKey)

      'Detecting Child Populating.
      If sIgCallBackRequest.Contains("""eventName"":""Populating"",""type"":""loadOnDemand""") Then Return GRID_AJAX_CALL_TYPE.CHILD_POPULATING

      Return GRID_AJAX_CALL_TYPE.NON_AJAX
   Else
      Return GRID_AJAX_CALL_TYPE.NON_AJAX
   End If

End Function

The function tries to locate "__IGCallback" key in HTTP request (Line 2), if the key present – function reads actual Request value (Line 6) and if Load-on-demand populating is detected (Line 9) – returns result saying so, otherwise returned result indicated non-ajax call.

This example implements only one detection – Load-on-demand populating, I will leave it to you to add sorting/filtering/paging detection. Once implemented – usage is pretty straightforward:

If Not IsPostBack Then
   '...
Else
   If GetGridAjaxCallType() = GRID_AJAX_CALL_TYPE.CHILD_POPULATING Then
      RebindTheGrid()
   End If
End if

WHDG: Give Grouped columns correct captions

$
0
0

If you work with Infragistics WebHierarchicalDataGrid and try to use its grouping features, you may notice that it uses grid column keys instead of column header’s captions to name items in “Group By” area.

To work around this limitation, let’s take a look at how Grouped By area is rendered:

Group By Area in WebHierarchicalDataGrid

Basically a container DIV holds a bunch of SPANs representing grouped columns. Both DIV and SPAN can be located by their CSS class (your own, if it’s assigned or Infragistics StyleSet class name, used by the grid control). Knowing the location of the SPANs we can loop thru them altering their text:

function renameColumnsInGroupByArea() {

   //locating GroupBy area DIV 
   var oGroupArea = document.getElementsByClassName('ighg_IGGroupArea')[0]; 

   //locating GroupedColumn SPANs
   var aGroupedColumns = oGroupArea.getElementsByClassName('ighg_IGGroupedColumn');

   // looping thru SPANS with grouped columns, replacing their text
   for (var I = 0; I < aGroupedColumns.length; I++) {
      aGroupedColumns[I].firstChild.nodeValue = // put your new value here;
   }
}

One way of using this function is generate a JavaScript associative array (ColumnData['ColumnKey'] = 'Column Caption') in ASP.NET server side code. Armed with such array Line 11 in the previous code could be simple

aGroupedColumns[I].firstChild.nodeValue = ColumnData[aGroupedColumns[I].firstChild.nodeValue]

WHDG: Position “Group by” area anywhere on the page

$
0
0

WebHierarchicalDataGrid grouping feature offers handy “Group By” area – a place where columns can be dragged to for grouping. It has one limitation though: by default it can be positioned only on top or the bottom of the grid. But what if you want to place it elsewhere? For example you have a dedicated navigation part of your page where you want to display grouped columns.

DOM to the rescue. As I mentioned in my previous post Group By area is a DIV that can be located by its CSS class name (either assigned by you or, barring that, class of a StyleSet used by the grid control). After the area is located, it can be moved to a location of your choice, for example another DIV by standard appendChild DOM method:

function positionGroupByArea() {
   var aGroupAreas = document.getElementsByClassName('ighg_Office2007BlueGroupArea'); 
   var oDivActionControls = $get('xdivNavigation'); 

   if (aGroupAreas.length > 1) {
      oDivActionControls.removeChild(aGroupAreas[0]);
      oDivActionControls.appendChild(aGroupAreas[1])
   } else {
      oDivActionControls.appendChild(aGroupAreas[0])
   }
}

Lines 2-3 Locate Group By area and target DIV respectfully. Note Line 5: like nature abhors a vacuum – WHDG abhors Group By area missing from its default place, if it is missing – grid will try to recreate it. If this happens you will have 2 Group By areas. If code on Line 5 detects this situation it removes old area and re-adds new one. Otherwise it simple moves original area.

You will have to call this code on initial grid load and after every grid operation (sorting, paging etc.) since grid will try to recreate Group By at the old place. Also, if the grid feels jumpy during this move – hide Group By area initially via it’s CSS class by setting display:none and make it visible again after the move.

Overall effect is quite seamless

WebDataTree: Use custom images for Expand/Collapse

$
0
0

Infragistics WebDataTree control offers variety of styles via supplied StyleSets and each StyleSet has its own Expand/Collapse images for tree branches. Unfortunately the control doesn’t offer built-in way to use your own expand/collapse images, to achieve that you need to replace respective images in the StyleSet currently used by the tree.

But if you’re reluctant to go this way for whatever reason (you’re using your own style or don’t want to change canonical Infragistics style) there’s another way – purely client-side JavaScript.

Note: The method below assumes that all the levels the tree are fully rendered on the client. If you’re employing load-on-demand, the method will require some adjustments.

Place the code below in Tree client-side Init event:

function xwdTree_Init(sender, eventArgs) {
   /// <summary>
   /// Fires when tree is initialized
   /// </summary>

   //{******* Replacing Expand/Collapse images in the tree with custom ones

   // Looping thru images in the tree itself
   var aTreeImages = sender.get_element().getElementsByTagName('img');
   
   for (I = 0; I < aTreeImages.length; I++) {
      if (aTreeImages[I].src.indexOf('Plus') != -1) aTreeImages[I].src = 'images/my_expand.png'
      else if (aTreeImages[I].src.indexOf('Minus') != -1) aTreeImages[I].src = 'images/my_collapse.png'
   }

   // Looping thru images in hidden div used for replacement after click
   var aTreeActionImages = $get(sender.get_id() + '_Images').getElementsByTagName('img');
   
   for (I = 0; I < aTreeActionImages.length; I++) {
      if (aTreeActionImages[I].src.indexOf('Plus') != -1) aTreeActionImages[I].src = 'images/my_expand.png'
      else if (aTreeActionImages[I].src.indexOf('Minus') != -1) aTreeActionImages[I].src = 'images/my_collapse.png'
   }

   //******* Replacing Expand/Collapse images in the tree with custom ones }
        
}

Take a look at Lines 8-14. This code locates Tree control and its respective DOM element. Then it loops thru all the images inside. If it’s an “expand” image (has the word “plus” in its name) – we replace it with our custom expand image. If a tree node already rendered expanded – the code will locate “collapse” image (which has the word “minus” in its name).

This is all good and well for currently rendered nodes. But when you start expand and collapse nodes – images are replaced dynamically (“expand” becomes “collapse” when node is expanded and vice-versa) by Infragistics code. If you only execute code in Lines 8-14 and then expand or collapse tree nodes – your images will get replaced with Infragistics original ones from their secret repository.

Fortunately that secret repository is a plain hidden DIV with the ID “yourTreeID_Images”. Take a look at Lines 16-22. This code locates the DIV that holds replacement images and replaces them with our own similarly to Lines 8-14. After it ran, when Infragistics code needs to grab an image to replace expand/collapse one – it will get yours.

As a result Tree is rendered (both statically and dynamically) with your own beautiful images.

Solution for: Value of type ‘System.Web.UI.HtmlControls.HtmlGenericControl’ cannot be converted to ‘System.Web.UI.HtmlControls.HtmlTableRow’ error

$
0
0

If you’re trying to compile an ASP.NET project/website in Visual studio 2012 or 2010 and getting error:

Value of type ‘System.Web.UI.HtmlControls.HtmlGenericControl’ cannot be converted to ‘System.Web.UI.HtmlControls.HtmlTableRow’

chances are you have an HTML table in your ASPX markup with runat="server" attribute set and <tbody> or <thead> tags present. Remove <tbody> and <thead> tags and the error should go away.

Infragistics WebDataMenu: Enable client-side resize with scrolling

$
0
0

Infragistics WebDataMenu control for ASP.NET has a neat feature: if items for horizontal menu don’t fit in allocated width – it automatically adds scroll buttons:

Scrolling in WebDataMenu

Unfortunately this works only if you set menu width in ASPX markup or server-side code. But what if menu is built dynamically and you don’t know in advance how many items there will be. Or if user resizes browser window and available width changes?

We need to be able to set menu width in client-side JavaScript code. Also we need to check whether all items fit into menu width and if not – enable scroll buttons – on demand. The code below does just that.

function adjustMenuWidth() {
	/// <summary>
	/// Sets horizontal menu width with scrolling if needed
	/// </summary>

    //getting reference to menu DOM elements
    var oMenuOuterDiv = $get('xmyMenu');
    var oMenuInnerDiv = oMenuOuterDiv.children[1];
    var oMenuRightScroll = oMenuInnerDiv.children[2];

    //calculating width available to menu
    var iMenuAvailWidth = document.documentElement.offsetWidth;
    
    // if real menu width is wider than available screen width - resize the menu
    if (oMenuInnerDiv.offsetWidth > iMenuAvailWidth) {
        
        //Resizing outer DIV
        oMenuOuterDiv.style.width = iMenuAvailWidth + 'px';

        //Resizing inner DIV ans showing Right Scroll button
        //Need to do this on setTimeout because Infragistics code
        //runs after this and overwrites the values otherwise
        setTimeout(function () {
            oMenuInnerDiv.style.width = iMenuAvailWidth + 'px';
            oMenuRightScroll.style.display = '';
        }, 500);
        
    }
}

The idea here is to emulate what happens when you set menu width server side or in ASPX markup. When menu is generated in that mode – resulting HTML gives the inner and outer DIVs of the menu the set width – and this is what we’re doing here in JavaScript code. The code checks width available to menu (full window width in the example above, but it can also be calculated) and then compares it to real menu width width all elements rendered. If the menu is wider, it gets shortened to available width and scrol button is made visible.

A call to this function can be placed in Menu Init and/or Window resize event.

Viewing all 79 articles
Browse latest View live