The Self-Taught Programmer

Recipes from real-world experience with asp.net.

Posts Tagged ‘DevExpress

How To Process a Portion of a Form via Callback and Display a Results Message Using DevExpress Controls

leave a comment »

The Situation:

You have an ASP.NET page with multiple form sections, and you want to process only one section when an ASPxButton control is clicked. You also want to display a status message after processing.

A Solution:

This should be basic Ajax-type stuff, but as I am a self-taught programmer, I came to this late. Also, I am using DevExpress controls in my current application, which function a bit differently than standard Ajax. Without Ajax, you would just process the page on postback and set the results label in the code-behind. However, using a callback rather than postback is a great way to process only a portion of the page.

Many of the DevExpress controls have an CallbackComplete client-side event, so you would handle the callback event, return a result to the control, and use the CallbackComplete event to set the results message label’s text value.

However, the ASPxButton does NOT have an CallbackComplete event that you can use (inconvenient!). The way around this is to use an ASPxCallback control.

1) Add an ASPxCallback control to your page. Add text to the control’s client-side CallbackComplete event to update the results label.

<dx:ASPxCallback ID="cbSaveResults" runat="server" ClientInstanceName="cbSaveResults" OnCallback="cbSaveResults_Callback">
	<ClientSideEvents CallbackComplete="function(s, e) {
				lblResults.SetText(e.result);
			}" />
</dx:ASPxCallback>

 
2) Add a method to the code-behind to handle the callback:

protected void cbSaveResults_Callback(object source, DevExpress.Web.ASPxCallback.CallbackEventArgs e)
{
	string returnMessage = "";

	try
	{
		// PUT CODE HERE TO PROCESS THE FORM

		returnMessage = "Your Changes have been saved.";
	}
	catch (Exception ex)
	{
		returnMessage = String.Format("There was a problem saving your changes. The error message was '{0}'", ex.Message);
	}

	e.Result = returnMessage;
}

 
3) Finally, add an ASPxButton to your page and add code to its client-side Click event to call the ASPxCallback control’s server-side OnCallback event (and blank the results label). Don’t forget to set the ASPxButton’s AutoPostBack=”False”!

<dx:ASPxButton ID="btnSaveResults" runat="server" Text="Save" AutoPostBack="False">
	<ClientSideEvents Click="function(s, e) {
				lblStatus.SetText('');
				cbSaveResults.PerformCallback();
			}" />
</dx:ASPxButton>

 
That’s all. When your user clicks the button, the ASPxButton actually throws the processing over to the ASPxCallback control, which then finishes and updates the results label. ALMOST as good as if DevExpress had included the functionality into the ASPxButton itself.

Advertisements

Written by Jim McMullen

July 18, 2013 at 4:20 pm

How To Perform a Callback on an ASPxGridLookup Control

with one comment

The Situation:

You have a DevExpress ASPxGridLookup control on your page and need to update its contents when some event occurs (such as a parent drop-down value changes). The problem is that the ASPxGridLookup control does not implement an OnCallback event.

A Solution:

First of all, this is not my solution. I found it on the DevExpress support site. It took me a while to find it, and to decode it, so I am putting it here so that I will be able to find it easier next time. I am also going to outline what is happening and why it works. The original solution was posted here.

There are two main ways to go about this. The first is to embed the ASPxGridLookup in an ASPxCallbackPanel. This is the first one that comes to mind, but there is an even simpler solution that involves adding a custom callback event to the instance of the ASPxGridLookup control.

Here is an ASPxGridLookup control on an aspx page:

<dx:ASPxGridLookup ID="ASPxGridLookup1" runat="server" ClientInstanceName="ASPxGridLookup1"
	TextFormatString="{0}" KeyFieldName="ProdId"
	AutoGenerateColumns="False" oninit="ASPxGridLookup1_Init">
	<GridViewProperties>
		<SettingsBehavior AllowFocusedRow="True" AllowSelectSingleRowOnly="True" />
		<SettingsPager Mode="ShowAllRecords">
		</SettingsPager>
	</GridViewProperties>
	<Columns>
		<dx:GridViewDataTextColumn VisibleIndex="0" FieldName="ProdId" Width="85px" Caption="ID">
		</dx:GridViewDataTextColumn>
		<dx:GridViewDataTextColumn VisibleIndex="1" FieldName="Description" Width="250px" Caption="Description">
		</dx:GridViewDataTextColumn>
	</Columns>
	<ClientSideEvents Validation="function(s,e) {if(s.GetValue()=='0') { e.isValid=false; e.errorText='Please select a Network ID.';}}" />
	<ValidationSettings SetFocusOnError="True" ErrorDisplayMode="ImageWithTooltip" ValidationGroup="Profile">
		<RequiredField IsRequired="True" ErrorText="Network ID is required" />
	</ValidationSettings>
</dx:ASPxGridLookup>

 

Then attach javascript like this to a calling control, for example when the selected item of an ASPxComboBox is changed:

<script language="javascript" type="text/javascript">
	function OnComboBoxChangedEventHandler() {
		ASPxGridLookup1.GetGridView().PerformCallback();
	}
</script>

 

Now to make this work, you have to do two things in the the code behind.

(1) The ASPxGridLookup control contains an ASPxGridView inside it (that’s what makes it such a valuable control). So you need to attach a ASPxGridViewCustomCallbackEventHandler to the ASPxGridView in the ASPxGridLookup control on init. Do that like this:

using DevExpress.Web.ASPxGridLookup;
using DevExpress.Web.ASPxGridView;

protected void ASPxGridLookup1_Init(object sender, EventArgs e)
{
	ASPxGridLookup lookup = (ASPxGridLookup)sender;
	ASPxGridView gridView = lookup.GridView;
	gridView.CustomCallback += new ASPxGridViewCustomCallbackEventHandler(ASPxGridLookup1_gridView_CustomCallback);
}

 

(2) The final step is to add the code to handle your new custom callback event handler. Do that like this:

void ddlCompProdId_gridView_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e)
{
	ASPxGridView gridView = (ASPxGridView)sender;
	//Put code here to requery/rebind the datasource for the ASPxGridLookup control
	...
	gridView.DataBind();
}

 

That’s all. When your user triggers the callback, it should act just the same as if DevExpress had built callback functionality into the control.

Written by Jim McMullen

May 31, 2013 at 12:50 am

How To Make a Column in an ASPxGridView ReadOnly for Editing but Enabled when Inserting a New Record

leave a comment »

The Situation:

You have a DevExpress ASPxGridView on your page with a column designated as “ReadOnly=true”. This works great when you are editing an existing record — it won’t allow you to change that field, but when you try to insert a NEW record, you can’t enter anything in the field.

Note: I don’t know if this happens under all circumstances, but for me, it happened for an ASPxGridView that is databound via code rather than from a datasource object on an aspx page.

A Solution:

The simple solution for this problem is to handle the “oncelleditorinitialize” event of the ASPxGridView.

Here is (simplified) ASPxGridView in my aspx page:

<dx:ASPxGridView ID="ASPxGridView1" runat="server" AutoGenerateColumns="False"
	KeyFieldName="SettingName" onrowdeleting="ASPxGridView1_RowDeleting"
	onrowinserting="ASPxGridView1_RowInserting"
	onrowupdating="ASPxGridView1_RowUpdating" Theme="DevEx"
	oncelleditorinitialize="ASPxGridView1_CellEditorInitialize">
	<Columns>
		<dx:GridViewCommandColumn VisibleIndex="0">
			<EditButton Visible="True" />
			<NewButton Visible="True" />
			<DeleteButton Visible="True" />
		</dx:GridViewCommandColumn>
		<dx:GridViewDataTextColumn FieldName="SettingName" ReadOnly="true"
			VisibleIndex="1">
		</dx:GridViewDataTextColumn>
		<dx:GridViewDataTextColumn FieldName="SettingValue" VisibleIndex="2">
		</dx:GridViewDataTextColumn>
	</Columns>
</dx:ASPxGridView>

And here is the code called for the oncelleditorinitialize event:

protected void ASPxGridView1_CellEditorInitialize(object sender, ASPxGridViewEditorEventArgs e)
{
	if (ASPxGridView1.IsNewRowEditing)
	{
		if ((e.Column.FieldName) == "SettingName")
		{
			e.Editor.ReadOnly = false;
		}
	}
}

Written by Jim McMullen

February 12, 2013 at 11:16 am

Posted in How-To

Tagged with ,

How To Edit a Record in an ASPxGridView Bound to EntitySpaces’ esDataSource that Uses a Join

leave a comment »

The Situation:

You get an error when you try to save edits to a record in a DevExpress ASPxGridView. The grid view is bound to an EntitySpaces esDataSource, which is populated using EntitySpaces dynamic query functionality. The ES dynamic query syntax allows you to use joins between entities like this (actual code from one of my recent projects):

private SubscriptionScopeCollection GetScopes(int catid)
{
	SubscriptionScopeQuery ssq = new SubscriptionScopeQuery("q1");
	VwClientSubscriptionsQuery csq = new VwClientSubscriptionsQuery("q2");

	ssq.Select(ssq, csq.ClientName );
	ssq.InnerJoin(csq).On(csq.SubscriptionId == ssq.SubscriptionId);
	ssq.OrderBy(csq.ClientName.Ascending, csq.SObjectName.Ascending);

	SubscriptionScopeCollection coll = new SubscriptionScopeCollection();
	coll.Load(ssq);
	
	return coll;
}

What this gives you is an entity collection full of entities that have extra columns (the fields from the joined tables – in this case, the “ClientName” field). EntitySpaces stores these extra columns in a Dictionary object.

Why This Causes an Error…

esDataSource has functionality built in to update/delete an entity. This is one of its biggest advantages. When it tries to save the changes, it creates a new version of the entity to work with. As the EntitySpaces documentation states, you have to put the following code into your code-behind to allow it to do this:

protected void esDataSrc_esCreateEntity(object sender, esDataSourceCreateEntityEventArgs e)
{
    SubscriptionScope entity = new SubscriptionScope();
    if (e.PrimaryKeys != null)
        entity.LoadByPrimaryKey((int)e.PrimaryKeys[0]);
 
    // Assign the Entity
    e.Entity = entity;
}

However, the ASPxGridView sends ALL field values back to be saved, including the extra fields. The entity created using the code above does not contain those extra fields, so it will throw an error when it tries to process them.

The Solution:

To make the entity recognize those extra fields, you have to add them into the entity’s ExtraColumns object. Doing so will NOT allow the entity to save their data, but it will keep it from throwing an error. (If you want to save the data from those extra fields, you have to override the esDataSource’s save functionality with your own.)

Add the extra columns to the entity by adding the indicated lines to the code above

protected void esDataSrc_esCreateEntity(object sender, esDataSourceCreateEntityEventArgs e)
{
    SubscriptionScope entity = new SubscriptionScope();
    if (e.PrimaryKeys != null)
        entity.LoadByPrimaryKey((int)e.PrimaryKeys[0]);

    // THESE LINES ADDED TO MAKE THE ENTITY RECOGNIZE THE EXTRA FIELD
    Dictionary<string,object> xtra = new Dictionary<string,object>();
    xtra.Add("ClientName", null);
    entity.SetExtraColumns(xtra);
 
    // Assign the Entity
    e.Entity = entity;
}

Written by Jim McMullen

August 12, 2012 at 12:18 am

Posted in How-To

Tagged with , ,