Archive

Archive for the ‘Code’ Category

Extending DataControlField – EditableButtonField

January 14th, 2012 No comments

It turns out that once you start extending DataControlField, you never turn back.  I have discovered joy in reducing code duplication and creating reusable components by converting common GridView tasks into new DataControlFields. Here’s another fun one: EditableButtonField.

A “normal” ButtonField is a read-only control which has no built-in behavior when the row is being edited.  With the EditableButtonField control, it will convert to a TextBox and provide immediate access to the DataTextField element.  Nifty!

Note that this control simply extends ButtonField rather than extending the full DataControlField.  Also note that I am assuming a ButtonType of “Link” and this would probably explode into itty bitty bits if you tried a Button.

<code>

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace JoeDonahue.resources {
public class EditableButtonField : ButtonField {

private Boolean inEditMode;

public override void InitializeCell(DataControlFieldCell cell,
DataControlCellType cellType, DataControlRowState rowState, int rowIndex) {
base.InitializeCell(cell, cellType, rowState, rowIndex);

switch (cellType) {
case DataControlCellType.DataCell:
this.InitializeDataCell(cell, rowState);
break;
case DataControlCellType.Footer:
this.InitializeFooterCell(cell, rowState);
break;
case DataControlCellType.Header:
this.InitializeHeaderCell(cell, rowState);
break;
}
}

protected void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState) {

cell.DataBinding += new EventHandler(OnDataBindField);

this.inEditMode = (rowState & (DataControlRowState.Edit | DataControlRowState.Insert)) != 0;

if (inEditMode) {
TextBox t = new TextBox();
cell.Controls.Clear();
cell.Controls.Add(t);
} else {
// Default behavior is OK
}
}

protected void InitializeHeaderCell(DataControlFieldCell cell, DataControlRowState rowState) {
}

protected void InitializeFooterCell(DataControlFieldCell cell, DataControlRowState rowState) {
}

protected virtual void OnDataBindField(object sender, EventArgs e) {
TableCell cell = (TableCell)sender;
IDataItemContainer container = (IDataItemContainer)cell.NamingContainer;
object boundvalue = GetBoundValue(container);
String selectedValue = boundvalue.ToString();

/* Set the corresponding text or selectedValue */
if (inEditMode) {
object dataItem = DataBinder.GetDataItem(container);
boundvalue = DataBinder.GetPropertyValue(dataItem, DataTextField);
selectedValue = boundvalue.ToString();

TextBox t = (TextBox)cell.Controls[0];

if (selectedValue != null && selectedValue.Length > 0)
t.Text = selectedValue;

} else {
LinkButton l = (LinkButton)cell.Controls[0];
l.Text = selectedValue;
}

}

object GetBoundValue(IDataItemContainer controlContainer) {
object dataItem = DataBinder.GetDataItem(controlContainer);
return DataBinder.GetPropertyValue(dataItem, DataTextField);
}

public override void ExtractValuesFromCell(System.Collections.Specialized.IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly) {

base.ExtractValuesFromCell(dictionary, cell, rowState, includeReadOnly);

this.inEditMode = (rowState & (DataControlRowState.Edit | DataControlRowState.Insert)) != 0;

string value = null;

if (cell.Controls.Count > 0) {
Control control = cell.Controls[0];
if (control == null)
throw new InvalidOperationException(“The control cannot be extracted”);

if (inEditMode)
value = ((TextBox)control).Text;
else
value = ((LinkButton)control).Text;

}

if (dictionary.Contains(this.DataTextField))
dictionary[this.DataTextField] = value;
else
dictionary.Add(this.DataTextField, value);
}
}

}

</code>

 

Categories: Code, Daily Grind Tags:

Custom DataControlField containing DropDownList

November 29th, 2011 No comments

While the idea is far from new (and can be seen in various implementations here, here, and here), every example I had found fell short of being exactly what I was looking for. My requirements, briefly, were:

  • Call a custom DataControlField from within a DetailsView
  • Do so from the ASP, not programmatically in the code-behind
  • Display a Label in View mode, but display a DropDownList in Edit Mode
  • The DropDownList must be tied to a SQLDataSource
  • The Update event, clearly, should save the selected value
  • Intellisense Integration. Because it gives me warm fuzzies.

Why?

Among the many variations uncovered above, one plaguing question kept arising – Why? This can easily be done by simply using a TemplateField. My answer is: Frequency.
This is not a once-in-a-lifetime requirement. I use this functionality – a lot. And while I appreciate the flexibility of the TemplateField, it is kind of annoying to me that I have to keep re-writing the same lines of code over-and-over again every single page I want to slap a DropDownList in a DetailsView. The DRY (Don’t Repeat Yourself) principle forced me over the edge.

Introducing… The DropDownField

While I expect to be revising both this page and the code as it is used out in the wild, I’ll be keeping this page updated both with my findings and any feedback I received.

The ASP Code

This run-of-the-mill DetailsView is bound to a SQLDataSource, as is the source for the DropDownList.

<res:DropDownField HeaderText=”Status” DataSourceID=”StatusData” DataTextField=”status” DataValueField=”status” />

The prefix for the control is “res”, which is defined in the web.config.

<add assembly=”JoeDonahue” namespace=”JoeDonahue.resources” tagPrefix=”res” />

DropDownField.cs

revision 2 – Updated 11/30/2011

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace JoeDonahue.resources {
public class DropDownField : DataControlField {

public String DataTextField {
get {
object dataTextField = ViewState["DataTextField"];
if (dataTextField != null) {
return dataTextField.ToString();
}
return string.Empty;
}
set {
this.ViewState["DataTextField"] = value;
}
}

public string DataValueField {
get {
object dataValueField = ViewState["DataValueField"];
if (dataValueField != null) {
return dataValueField.ToString();
}
return string.Empty;
}
set {
this.ViewState["DataValueField"] = value;
}
}

public String DataSourceID {
get {
object dataSourceID = ViewState["DataSourceID"];
if (dataSourceID != null) {
return dataSourceID.ToString();
}
return string.Empty;
}
set {
this.ViewState["DataSourceID"] = value;
}
}

public string SelectedValue {
get {
object selectedValue = ViewState["selectedValue"];
if (selectedValue != null) {
return selectedValue.ToString();
}
return string.Empty;
}
set {
this.ViewState["selectedValue"] = value;
}
}

private Boolean inEditMode;

/** Methods **/

protected override DataControlField CreateField() {
return new DropDownField();
}

public override void InitializeCell(DataControlFieldCell cell,
DataControlCellType cellType, DataControlRowState rowState, int rowIndex) {
base.InitializeCell(cell, cellType, rowState, rowIndex);

switch (cellType) {
case DataControlCellType.DataCell:
this.InitializeDataCell(cell, rowState);
break;
case DataControlCellType.Footer:
this.InitializeFooterCell(cell, rowState);
break;
case DataControlCellType.Header:
this.InitializeHeaderCell(cell, rowState);
break;
}
}

protected void InitializeDataCell(DataControlFieldCell cell, DataControlRowState rowState) {

cell.DataBinding += new EventHandler(OnDataBindField);

this.inEditMode = (rowState & (DataControlRowState.Edit | DataControlRowState.Insert)) != 0;

if (inEditMode) {
DropDownList ddl = new DropDownList();
ddl.ID = this.DataTextField;
ddl.DataSourceID = this.DataSourceID;
ddl.DataTextField = this.DataTextField;
ddl.DataValueField = this.DataValueField;
cell.Controls.Add(ddl);
} else {
Label l = new Label();
cell.Controls.Add(l);
}
}

protected void InitializeHeaderCell(DataControlFieldCell cell, DataControlRowState rowState) {
}

protected void InitializeFooterCell(DataControlFieldCell cell, DataControlRowState rowState) {
}

protected virtual void OnDataBindField(object sender, EventArgs e) {
TableCell cell = (TableCell)sender;
IDataItemContainer container = (IDataItemContainer)cell.NamingContainer;
object boundvalue = GetBoundValue(container);
String selectedValue = boundvalue.ToString();

/* Set the corresponding text or selectedValue */
if (inEditMode) {
object dataItem = DataBinder.GetDataItem(container);
selectedValue = (String)DataBinder.GetPropertyValue(dataItem, DataValueField);
DropDownList ddl = (DropDownList)cell.Controls[0];
ddl.SelectedValue = selectedValue;
} else {
Label l = (Label)cell.Controls[0];
l.Text = selectedValue;
}

}

object GetBoundValue(IDataItemContainer controlContainer) {
object dataItem = DataBinder.GetDataItem(controlContainer);
return DataBinder.GetPropertyValue(dataItem, DataTextField);
}

public override void ExtractValuesFromCell(System.Collections.Specialized.IOrderedDictionary dictionary, DataControlFieldCell cell, DataControlRowState rowState, bool includeReadOnly) {

base.ExtractValuesFromCell(dictionary, cell, rowState, includeReadOnly);

string value = null;

if (cell.Controls.Count > 0) {
Control control = cell.Controls[0];
if (control == null)
throw new InvalidOperationException(“The control cannot be extracted”);
value = ((DropDownList)control).SelectedValue;
}

if (dictionary.Contains(this.DataValueField))
dictionary[this.DataValueField] = value;
else
dictionary.Add(this.DataValueField, value);
}
}
}

Helpful Resources

The following links were really helpful in accomplishing this. Thanks!

Categories: Code Tags:

T-SQL Implicit Join Against A Function

October 21st, 2010 No comments

I am posting this not only for personal reference, but because I conjured it as a result of the T-SQL Explode function I described in my last post. Previously, I had taken the results from the Explode and pushed them into a temp table; But thanks to SQL Server 2005 CROSS APPLY functionality, we can cut the temp table out of the picture and join directly against the function.

Note: I tried an explicit join using the ON keyword, but SQL was not very happy about it. Using the implicit join within the WHERE clause works perfectly fine.

select t.*
from table t
cross apply explode(‘value1, value2, value3′) e
where e.item = t.foreign_key

Categories: Code Tags:

T-SQL Explode

July 6th, 2010 No comments

A feature I have used with much joy in PHP is the explode() function, which creates an array populated by a single parameter containing comma-separated values (CSV).  I recently had a desire to use the same approach in SQL Server programming (T-SQL) and push a set of CSV parameters into a temp table.  A slightly modified version of the function found here now allows me to do so!

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
/* select * from explode(’1,2,3′) */
CREATE FUNCTION [dbo].[explode] (
@CSVString VARCHAR(8000))
RETURNS @temptable TABLE (item VARCHAR(8000))
AS
BEGIN

  • DECLARE @pos INT;
    DECLARE @slice VARCHAR(8000);
    DECLARE @Delimiter CHAR(1);
    SELECT @pos = 1;
    SELECT @Delimiter = ‘,’;
    IF LEN(@CSVString) < 1 OR @CSVString IS NULL RETURN;
    WHILE @pos!= 0
    BEGIN

    • SET @pos = CHARINDEX(@Delimiter,@CSVString);
      IF @pos != 0
      SET @slice = LEFT(@CSVString, @pos – 1);
      ELSE
      SET @slice = @CSVString;

      IF( LEN(@slice) > 0)
      INSERT INTO @temptable(item) VALUES (@slice);
      SET @CSVString = RIGHT(@CSVString, LEN(@CSVString) – @pos);
      IF LEN(@CSVString) = 0 BREAK;

    END

RETURN
END

Categories: Code Tags: