Oct 06, 2010, 3:18 PM
[CLOSED] DirectMethod missing when control created dynamically
Guys,
This might be a little complicated to explain, and I'll try to give all the relevant code samples. OK, here goes:
Setup:
From a few places in my application, I want to be able to create/edit some data using a window. So, instead of replicating the same window in markup in 2 places, I thought it would be good code design/reuse to use the same strategy as the 'SimpleTasks' example on examples.ext.net. I got everything working first in markup, and have been spending the last several days trying to get everything working again after creating it dynamically.
In a nutshell, the problem I'm having is:
On my window, I have a button that fires a client side 'save' function. This in turn, collects the values of the form fields and submits them to a 'Save' direct method. When I create the Window in code, even when registering the control as a directmethod control (as in the example), my DirectMethod is not showing up in the client side DOM.
Here goes with as simplified an example as I can:
First, grid on ASPX page:
Thanks so much in advance for your help.
Jason
This might be a little complicated to explain, and I'll try to give all the relevant code samples. OK, here goes:
Setup:
From a few places in my application, I want to be able to create/edit some data using a window. So, instead of replicating the same window in markup in 2 places, I thought it would be good code design/reuse to use the same strategy as the 'SimpleTasks' example on examples.ext.net. I got everything working first in markup, and have been spending the last several days trying to get everything working again after creating it dynamically.
In a nutshell, the problem I'm having is:
On my window, I have a button that fires a client side 'save' function. This in turn, collects the values of the form fields and submits them to a 'Save' direct method. When I create the Window in code, even when registering the control as a directmethod control (as in the example), my DirectMethod is not showing up in the client side DOM.
Here goes with as simplified an example as I can:
First, grid on ASPX page:
<ext:GridPanel ID="gpPasswords" runat="server" StoreID="stPasswords" TrackMouseOver="true"
StripeRows="true" Header="false" Border="false"
Collapsible="true" AnimCollapse="true" AutoHeight="true">
<ColumnModel>
<Columns>
<ext:ImageCommandColumn>
<Commands>
<ext:ImageCommand CommandName="EditPassword" Text="Edit" Icon="TableEdit">
<ToolTip Text="Edit Password" />
</ext:ImageCommand>
</Commands>
</ext:ImageCommandColumn>
<ext:Column ColumnID="PasswordType" Header="Type" Sortable="true" DataIndex="PasswordType" />
<ext:Column ColumnID="Resource" Header="Resource" Sortable="true" DataIndex="Resource" />
<ext:Column ColumnID="Location" Header="Location/Url" Sortable="true" DataIndex="Location" />
<ext:Column ColumnID="Username" Header="Username" Sortable="true" DataIndex="Resource" />
<ext:Column ColumnID="Password" Header="Password" Sortable="false" DataIndex="Password">
<Renderer Fn="pwdRenderer" />
<Commands>
<ext:ImageCommand Icon="Magnifier" ToolTip-Text="Show Password" CommandName="ShowPassword"></ext:ImageCommand>
<ext:ImageCommand Icon="PastePlain" ToolTip-Text="Copy to Clipboard" CommandName="CopyToClipboard"></ext:ImageCommand>
</Commands>
</ext:Column>
<ext:DateColumn ColumnID="ActiveDate" Header="Active From" Sortable="true" DataIndex="ActiveDate"
Format="d">
</ext:DateColumn>
<ext:DateColumn ColumnID="ExpirationDate" Header="Expires" Sortable="true" DataIndex="ExpirationDate"
Format="d">
</ext:DateColumn>
<ext:DateColumn ColumnID="ModifiedDate" Header="Last Modified" Sortable="true" DataIndex="ModifiedDate"
Format="n/j/Y g:i a" />
<ext:Column ColumnID="ModifiedBy" Header="Modified By" Sortable="true" DataIndex="ModifiedBy"></ext:Column>
</Columns>
</ColumnModel>
<SelectionModel>
<ext:RowSelectionModel ID="SelectedRowModel1" runat="server" SingleSelect="true" />
</SelectionModel>
<Listeners>
<Command Handler="gpPasswords_Command(command, record);" />
</Listeners>
<View>
<ext:GridView ID="GridView1" runat="server" ForceFit="true" />
</View>
<Plugins>
<ext:RowExpander ID="RowExpander1" runat="server">
<Template ID="NoteTemplate" runat="server">
<Html>
<table border="0" cellpadding="10" cellspacing="0" width="100%">
<tr>
<td style="font-weight:bold; width:50px; padding-top: 5px; padding-bottom: 5px; padding-left: 10px;">Notes:</td>
<td style="text-align:left; width:90%; padding-top: 5px; padding-bottom:5px;">
{Notes}
</td>
</tr>
</table>
</Html>
</Template>
</ext:RowExpander>
<ext:GridFilters runat="server" ID="GridFilters1" Local="true">
<Filters>
<ext:StringFilter DataIndex="PasswordType" />
<ext:StringFilter DataIndex="Resource" />
<ext:StringFilter DataIndex="Location" />
<ext:StringFilter DataIndex="Username" />
<ext:DateFilter DataIndex="ActiveDate">
<DatePickerOptions runat="server" TodayText="Now" />
</ext:DateFilter>
<ext:DateFilter DataIndex="ExpirationDate">
<DatePickerOptions runat="server" TodayText="Now" />
</ext:DateFilter>
<ext:DateFilter DataIndex="ModifiedDate">
<DatePickerOptions runat="server" TodayText="Now" />
</ext:DateFilter>
<ext:StringFilter DataIndex="ModifiedBy" />
</Filters>
</ext:GridFilters>
</Plugins>
</ext:GridPanel>
The pertinent parts are that I have an Edit ImageCommand column and it gets handled by gpPasswords_command (client side on the same page): <script language="javascript" type="text/javascript">
var gpPasswords_Command = function (command, record) {
switch (command) {
case "EditPassword":
Ext.net.DirectMethods.EditPassword(record.data.PasswordId);
break;
}
</script>
The Edit Password DirectMethod is on the same ASPX page (in codebehind): [DirectMethod]
public void EditPassword(int PasswordId)
{
new LogicSpeak.Harmony.Portal.UC.Ext.PasswordWindow(PasswordId).Render(this.Page.Form.ClientID, RenderMode.RenderTo);
}
My PasswordWindow is an Ext.net Window with additional "stuff":#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Ext.Net;
using Ext.Net.Utilities;
using LogicSpeak.Extensions;
using System.Web.UI.WebControls;
using LogicSpeak.Harmony.Components;
using LogicSpeak.Harmony.BLL;
using LogicSpeak.Configuration;
using LogicSpeak.Data.Sql;
#endregion
namespace LogicSpeak.Harmony.Portal.UC.Ext
{
[DirectMethodProxyID(IDMode = DirectMethodProxyIDMode.None)]
public class PasswordWindow : Window
{
#region Instance
private const string SCOPE = "LogicSpeak.PasswordWindow";
TList<Lookup> PasswordTypes;
private Password PasswordInfo;
private int PasswordId;
private global::Ext.Net.Store stPasswordTypes;
private global::Ext.Net.FormPanel passwordForm;
private global::Ext.Net.Container outerContainer;
private global::Ext.Net.Container leftColumn;
private global::Ext.Net.Container rightColumn;
private global::Ext.Net.Container bottomRow;
private PasswordFolderField ddlFolder;
private global::Ext.Net.TextField txtResource;
private global::Ext.Net.TextField txtLocation;
private global::Ext.Net.ComboBox ddlPasswordType;
private global::Ext.Net.TextField txtUsername;
private global::Ext.Net.TriggerField txtPassword;
private global::Ext.Net.DateField dfActive;
private global::Ext.Net.DateField dfExpiration;
private global::Ext.Net.TextArea txtNotes;
private global::Ext.Net.Button btnPasswordSave;
private global::Ext.Net.Button btnPasswordCancel;
private global::Ext.Net.KeyBinding kbPasswordSave;
private global::Ext.Net.KeyBinding kbPasswordCancel;
#endregion
public PasswordWindow()
{
this.PasswordId = 0;
LoadData();
InitControls();
LoadDefaultForm();
}
public PasswordWindow(int PasswordId)
{
this.PasswordId = PasswordId;
LoadData();
InitControls();
LoadForm();
}
#region Methods
private void LoadData()
{ //Unrelated database access code
}
private void LoadDefaultForm()
{
ddlFolder.UnderlyingValue = string.Empty;
ddlFolder.Text = string.Empty;
ddlPasswordType.ClearValue();
txtResource.Text = string.Empty;
txtLocation.Text = string.Empty;
txtUsername.Text = string.Empty;
txtPassword.Text = string.Empty;
txtNotes.Text = string.Empty;
dfActive.Clear();
dfExpiration.Clear();
}
private void LoadForm()
{
LogicSpeak.Security.Encryption decrypter = new Security.Encryption();
ddlFolder.UnderlyingValue = PasswordInfo.PasswordFolderId.ToString();
ddlFolder.Text = PasswordInfo.PasswordFolderIdSource.Name;
ddlPasswordType.SelectByValue(PasswordInfo.PasswordTypeId.ToString());
txtResource.Text = PasswordInfo.Resource;
txtLocation.Text = PasswordInfo.Location;
txtUsername.Text = PasswordInfo.Username;
txtPassword.Text = decrypter.Decrypting(PasswordInfo.Password, ConfigurationManagerHelper.GetAppSetting("PasswordSalt"));
txtNotes.Text = PasswordInfo.Notes;
if (PasswordInfo.ActiveDate.IsNot(SqlHelper.MinDateTime))
{
dfActive.SelectedDate = PasswordInfo.ActiveDate;
}
if (PasswordInfo.ExpirationDate.IsNot(SqlHelper.MinDateTime))
{
dfExpiration.SelectedDate = PasswordInfo.ExpirationDate;
}
}
private void InitIcons()
{
this.ResourceManager.RegisterIcon(global::Ext.Net.Icon.KeyAdd);
this.ResourceManager.RegisterIcon(global::Ext.Net.Icon.Key);
this.ResourceManager.RegisterIcon(global::Ext.Net.Icon.Disk);
}
private void InitControls()
{
#region this(window) & Outer Container
this.ID = string.Format("PasswordWindow_{0}", PasswordId);
this.AutoHeight = true;
this.ButtonAlign = Alignment.Right;
this.CloseAction = global::Ext.Net.CloseAction.Close;
this.Collapsible = false;
this.Icon = PasswordId.Is(0) ? global::Ext.Net.Icon.KeyAdd : global::Ext.Net.Icon.Key;
this.InitCenter = true;
this.Layout = "AutoLayout";
this.Modal = true;
this.PaddingSummary = "5px 5px 0";
this.Resizable = false;
this.Title = PasswordId.Is(0) ? "Enter New Password" : "Edit Password";
this.Width = new Unit(450, UnitType.Pixel);
this.InitIcons();
this.CustomConfig.Add(new ConfigItem("passwordId", PasswordId.ToString(), ParameterMode.Raw));
passwordForm = new FormPanel()
{
BaseCls = "x-plain",
Ref = "passwordForm",
RenderFormElement = false,
CustomConfig =
{
new ConfigItem("margins", "10 10 5 10", ParameterMode.Value)
}
};
outerContainer = new Container();
outerContainer.Layout = "Column";
outerContainer.Height = new Unit(200, UnitType.Pixel);
#endregion
#region Left Column
leftColumn = new Container();
leftColumn.LabelAlign = global::Ext.Net.LabelAlign.Top;
leftColumn.Layout = "Form";
leftColumn.ColumnWidth = .5;
ddlFolder = new PasswordFolderField();
ddlFolder.ID = "ddlFolder";
ddlFolder.AnchorHorizontal = "95%";
ddlFolder.DataIndex = "PasswordFolderId";
ddlFolder.TabIndex = 1;
leftColumn.Items.Add(ddlFolder);
stPasswordTypes = new Store();
stPasswordTypes.ID = "stPasswordTypes";
stPasswordTypes.AutoLoad = false;
JsonReader reader = new JsonReader();
reader.Fields.Add(new RecordField("LookupId"));
reader.Fields.Add(new RecordField("Description"));
stPasswordTypes.Reader.Add(reader);
stPasswordTypes.DataSource = PasswordTypes;
stPasswordTypes.DataBind();
ddlPasswordType = new ComboBox();
ddlPasswordType.ID = "ddlPasswordType";
ddlPasswordType.FieldLabel = "Password Type";
ddlPasswordType.ValueField = "LookupId";
ddlPasswordType.DisplayField = "Description";
ddlPasswordType.ForceSelection = true;
ddlPasswordType.SelectOnFocus = true;
ddlPasswordType.LazyInit = false;
ddlPasswordType.Mode = DataLoadMode.Default;
ddlPasswordType.TriggerAction = TriggerAction.All;
ddlPasswordType.TypeAhead = true;
ddlPasswordType.ValidateOnBlur = true;
ddlPasswordType.BlankText = "Type is required.";
ddlPasswordType.AllowBlank = false;
ddlPasswordType.DataIndex = "PasswordTypeId";
ddlPasswordType.Store.Add(stPasswordTypes);
ddlPasswordType.TabIndex = 2;
leftColumn.Items.Add(ddlPasswordType);
txtLocation = new TextField();
txtLocation.ID = "txtLocation";
txtLocation.FieldLabel = "Location/Link";
txtLocation.AnchorHorizontal = "95%";
txtLocation.DataIndex = "Location";
txtLocation.TabIndex = 3;
leftColumn.Items.Add(txtLocation);
dfActive = new DateField();
dfActive.ID = "dfActive";
dfActive.DataIndex = "Active";
dfActive.FieldLabel = "Active Date";
dfActive.AnchorHorizontal = "95%";
dfActive.TabIndex = 7;
leftColumn.Items.Add(dfActive);
outerContainer.Items.Add(leftColumn);
#endregion
#region Right Column
rightColumn = new Container();
rightColumn.LabelAlign = global::Ext.Net.LabelAlign.Top;
rightColumn.Layout = "Form";
rightColumn.ColumnWidth = .5;
txtResource = new TextField();
txtResource.ID = "txtResource";
txtResource.FieldLabel = "Resource";
txtResource.AnchorHorizontal = "95%";
txtResource.DataIndex = "Resource";
txtResource.TabIndex = 4;
rightColumn.Items.Add(txtResource);
txtUsername = new TextField();
txtUsername.ID = "txtUsername";
txtUsername.FieldLabel = "Username";
txtUsername.AnchorHorizontal = "95%";
txtUsername.DataIndex = "Username";
txtUsername.TabIndex = 5;
rightColumn.Items.Add(txtUsername);
txtPassword = new TriggerField();
txtPassword.ID = "txtPassword";
txtPassword.FieldLabel = "Password";
txtPassword.InputType = InputType.Password;
txtPassword.AnchorHorizontal = "95%";
txtPassword.TabIndex = 6;
txtPassword.DataIndex = "Password";
FieldTrigger ftShowPassword = new FieldTrigger();
ftShowPassword.Icon = TriggerIcon.SimpleMagnify;
ftShowPassword.Qtip = "Show password";
txtPassword.Triggers.Add(ftShowPassword);
FieldTrigger ftCopyToClipboard = new FieldTrigger();
ftCopyToClipboard.Icon = TriggerIcon.SimpleGet;
ftCopyToClipboard.Qtip = "Copy to clipboard";
txtPassword.Triggers.Add(ftCopyToClipboard);
rightColumn.Items.Add(txtPassword);
dfExpiration = new DateField();
dfExpiration.ID = "dfExpiration";
dfExpiration.DataIndex = "Expiration";
dfExpiration.FieldLabel = "Expiration Date";
dfExpiration.AnchorHorizontal = "95%";
dfExpiration.TabIndex = 8;
rightColumn.Items.Add(dfExpiration);
outerContainer.Items.Add(rightColumn);
#endregion
passwordForm.Items.Add(outerContainer);
#region Bottom Row
bottomRow = new Container();
bottomRow.LabelAlign = global::Ext.Net.LabelAlign.Top;
bottomRow.Layout = "Form";
txtNotes = new TextArea();
txtNotes.ID = "txtNotes";
txtNotes.Height = new Unit(125, UnitType.Pixel);
txtNotes.FieldLabel = "Notes";
txtNotes.AnchorHorizontal = "100%";
txtNotes.DataIndex = "Notes";
txtNotes.TabIndex = 9;
bottomRow.Items.Add(txtNotes);
passwordForm.Items.Add(bottomRow);
#endregion
#region Actions
btnPasswordSave = new global::Ext.Net.Button();
btnPasswordSave.ID = "btnPasswordSave";
btnPasswordSave.Icon = global::Ext.Net.Icon.Disk;
btnPasswordSave.Text = "Save";
btnPasswordSave.TabIndex = 10;
this.Buttons.Add(btnPasswordSave);
btnPasswordCancel = new global::Ext.Net.Button();
btnPasswordCancel.ID = "btnPasswordCancel";
btnPasswordCancel.Text = "Cancel";
btnPasswordCancel.TabIndex = 11;
this.Buttons.Add(btnPasswordCancel);
this.Items.Add(passwordForm);
kbPasswordSave = new KeyBinding();
kbPasswordSave.Alt = true;
kbPasswordSave.Keys.Add(new Key() {Code = KeyCode.S});
this.KeyMap.Add(kbPasswordSave);
kbPasswordCancel = new KeyBinding();
kbPasswordCancel.Alt = true;
kbPasswordCancel.Keys.Add(new Key() { Code = KeyCode.C });
this.KeyMap.Add(kbPasswordCancel);
#endregion
}
#endregion
#region Event Handlers
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.ResourceManager.AddDirectMethodControl(this);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
this.DefaultButton = btnPasswordSave.ClientID;
btnPasswordSave.Handler = new JFunction(string.Concat(SCOPE, ".savePassword(", this.ClientID, ");")).ToScript();
kbPasswordSave.Listeners.Event.Handler = new JFunction(string.Concat(SCOPE, ".savePassword(", this.ClientID, ");")).ToScript();
btnPasswordCancel.Handler = new JFunction(this.ID + ".close();").ToScript();
kbPasswordCancel.Listeners.Event.Handler = new JFunction(this.ID + ".close();").ToScript();
txtPassword.Listeners.TriggerClick.Fn = string.Concat(SCOPE, ".passwordTrigger");
}
[DirectMethod]
public void ShowPassword(bool ShowPassword)
{
try
{
txtPassword.InputType = ShowPassword ? InputType.Text : InputType.Password;
rightColumn.Render(txtPassword, RenderMode.RenderTo);
global::Ext.Net.ResourceManager.AjaxSuccess = true;
}
catch (Exception ex)
{
global::Ext.Net.ResourceManager.AjaxSuccess = false;
global::Ext.Net.ResourceManager.AjaxErrorMessage = ex.Message;
}
}
[DirectMethod]
public void SavePassword(int PasswordId, JsonObject values)
{
string Resource = string.Empty;
string Location = string.Empty;
string Username = string.Empty;
string Password = string.Empty;
DateTime ActiveDate = SqlHelper.MinDateTime;
DateTime ExpirationDate = SqlHelper.MinDateTime;
string Notes = string.Empty;
int PasswordTypeId = 0;
int PasswordFolderId = 0;
try
{
foreach (KeyValuePair<string, object> item in values)
{
if (item.Key.Contains("txtResource")) Resource = item.Value.ToString();
else if (item.Key.Contains("txtLocation")) Location = item.Value.ToString();
else if (item.Key.Contains("txtUsername")) Username = item.Value.ToString();
else if (item.Key.Contains("txtPassword")) Password = item.Value.ToString();
else if (item.Key.Contains("txtNotes")) Notes = item.Value.ToString();
else if (item.Key.Contains("ddlFolder")) PasswordFolderId = item.Value.ChangeType<int>();
else if (item.Key.Contains("ddlPasswordType")) PasswordTypeId = item.Value.ChangeType<int>();
else if (item.Key.Contains("dfActive")) ActiveDate = item.Value.ChangeType<DateTime>();
else if (item.Key.Contains("dfExpiration")) ExpirationDate = item.Value.ChangeType<DateTime>();
}
if (PasswordId.Is(0))
{
PasswordService.Instance.CreatePassword(
PasswordFolderId,
Resource,
Location,
PasswordTypeId,
Username,
Password,
Notes,
ActiveDate,
ExpirationDate,
EmployeeInfo.EmployeeId,
EmployeeInfo.Email1);
}
else
{
PasswordService.Instance.UpdatePassword(
PasswordId,
PasswordFolderId,
Resource,
Location,
PasswordTypeId,
Username,
Password,
Notes,
ActiveDate,
ExpirationDate,
EmployeeInfo.EmployeeId,
EmployeeInfo.Email1);
}
global::Ext.Net.ResourceManager.AjaxSuccess = true;
}
catch (Exception ex)
{
global::Ext.Net.ResourceManager.AjaxSuccess = false;
global::Ext.Net.ResourceManager.AjaxErrorMessage = ex.Message;
}
}
#endregion
}
}
This control loads fine (except that the dropdown for PasswordType can't be set...for some reason when the SetByValue method is called, there are no entries in the collection, even though I had just loaded them up). The problem comes when clicking save. It calls a client side savePassword method in a "PasswordWindow.js" file:Ext.ns("LogicSpeak");
LogicSpeak.PasswordWindow = {
copyToClipboard: function (ft) {
if (window.clipboardData && window.clipboardData.setData) {
window.clipboardData.setData("Text", ft.getValue());
}
else {
Ext.Msg.alert("Not Supported", "Sorry, your browser currently does not support copy to clipboard functionality.");
}
},
savePassword: function (w) {
if (!w.passwordForm.isValid()) {
return;
}
var values = Ext.encode(w.passwordForm.getForm().getFieldValues());
Ext.Msg.wait("Saving password...", "Progress");
Ext.net.DirectMethods.SavePassword(w.passwordId, values, {
success: function () {
Ext.Msg.hide();
w.close();
//Rebind grid?!?
},
failure: function (error, response) {
Ext.Msg.alert("Error saving password", response.responseText);
}
});
},
showPassword: function (ft) {
if (ft.inputType == "password") {
Ext.net.DirectMethods.ShowPassword(true, {
success: function (response) {
},
failure: function (error, response) {
Ext.Msg.alert("Error showing password", response.responseText);
}
});
}
else {
Ext.net.DirectMethods.ShowPassword(false, {
success: function () { },
failure: function (error, response) {
Ext.Msg.alert("Error hiding password", response.responseText);
}
});
}
},
passwordTrigger: function (el, trigger, index) {
switch (index) {
case 0:
LogicSpeak.PasswordWindow.showPassword(el);
break;
case 1:
LogicSpeak.PasswordWindow.copyToClipboard(el);
break;
}
}
};
Finally...here's the problem. The savePassword attempts to call the Ext.net.DirectMethods.SavePassword (which is on the PasswordWindow) but it is undefined. I have been beating my head against a wall for 2 days trying to figure out why, given that I've followed the examples code as closely as I could, but I am thus far unsuccessful. Any ideas? Do I have some fundamentally wrong logic or properties set that are causing this to not work?Thanks so much in advance for your help.
Jason
Last edited by geoffrey.mcgill; Oct 07, 2010 at 7:45 PM.
Reason: [CLOSED]