ClickSuppressor in Web Applications - asp.net

for my Winform applications I have used ClickSuppressor so that pereventing multiple action by pressing button twice or more. I am wondering what about the WebForms and MVC applications? Do we also need to use ClickSuppressor in this kind of projects?
using (new MyClass.ClickSuppressor(this))
{
try
{
//my action code i.e. add record
}
}
MyClass:
/// <summary>
/// Stops to queue event in order to prevent from "multiple click" when adding a new record.
/// </summary>
internal class ClickSuppressor : IDisposable
{
private Control mCtrl;
public ClickSuppressor(Control ctrl)
{
mCtrl = ctrl;
mCtrl.Enabled = false;
mCtrl.Update();
}
public void Dispose()
{
Application.DoEvents();
/* Or this:
MethodInfo mi = typeof(Control).GetMethod("RemovePendingMessages", BindingFlags.Instance | BindingFlags.NonPublic);
mi.Invoke(mCtrl, new object[] { 0x201, 0x203 });
mi.Invoke(mCtrl.Parent, new object[] { 0x201, 0x203 });
*/
if (!mCtrl.IsDisposed) mCtrl.Enabled = true;
}
}

This needs to be done in the browser with scripts.
Set the disabled attribute on the element to "disabled" when it is clicked:
<button class="singleClick">
The jQuery:
$(".singleClick").on("click", function(){
this.attr("disabled", "disabled");
});
You can remove the disabled status later with:
$(element).removeAttr("disabled");

Related

send data from silverlight to asp.net (or php?) when the user click on a button

If I click on a button of my Silverlight app, another third-party web app must gets some data.
My experience, until now, has been to create web service functions that you can call when you need, but in this case I have to give the possibility to the customer to "handle the click event on the button". In the actual case the third-party app is ASP.Net, but, if it were possible, I would like to do something portable.
Before to start with some crazy idea that will comes in my mind, I would ask: How would you do that?
Pileggi
I Use This Class To Create And Post a Form Dynamically
public class PassData
{
public static PassData Default = new PassData();
public void Send(string strUrl, Dictionary<string, object> Parameters, string ContainerClientID = "divContainer")
{
var obj = HtmlPage.Document.GetElementById(ContainerClientID);
if (obj != null)
{
HtmlElement divContainer = obj as HtmlElement;
ClearContent((HtmlElement)divContainer);
HtmlElement form = HtmlPage.Document.CreateElement("form");
form.SetAttribute("id", "frmPostData");
form.SetAttribute("name", "frmPostData");
form.SetAttribute("target", "_blank");
form.SetAttribute("method", "POST");
form.SetAttribute("action", strUrl);
if (Parameters != null)
foreach (KeyValuePair<string, object> item in Parameters)
{
HtmlElement hidElement = HtmlPage.Document.CreateElement("input");
hidElement.SetAttribute("name", item.Key);
hidElement.SetAttribute("value", item.Value.ToString());
form.AppendChild(hidElement);
}
divContainer.AppendChild(form);
form.Invoke("submit");
ClearContent((HtmlElement)divContainer);
}
}
private void ClearContent(System.Windows.Browser.HtmlElement obj)
{
foreach (HtmlElement item in obj.Children)
{
obj.RemoveChild(item);
}
}
}
divContainer is id of a div in html

SharePoint Web Part Custom Properties Don't Take Effect Until Page Reload

I am developing a sharepoint 2007 web part that uses custom properties. Here is one:
[Personalizable(PersonalizationScope.User), WebDisplayName("Policy Update List Name")]
[WebDescription("The name of the SharePoint List that records all the policy updates.\n Default value is Policy Updates Record.")]
public string PolicyUpdateLogName
{
get { return _PolicyUpdateLogName == null ? "Policy Updates Record" : _PolicyUpdateLogName; }
set { _PolicyUpdateLogName = value; }
}
The properties work fine except that the changes are not reflected in the web part until you leave the page and navigate back (or just click on the home page link). Simply refreshing the page doesn't work, which makes me think it has something to do with PostBacks.
My current theory is that the ViewState is not loading postback data early enough for the changes to take effect. At the very least, the ViewState is involved somehow with the issue.
Thanks,
Michael
Here is more relevant code:
protected override void CreateChildControls()
{
InitGlobalVariables();
FetchPolicyUpdateLog_SPList();
// This function returns true if the settings are formatted correctly
if (CheckWebPartSettingsIntegrity())
{
InitListBoxControls();
InitLayoutTable();
this.Controls.Add(layoutTable);
LoadPoliciesListBox();
}
base.CreateChildControls();
}
...
protected void InitGlobalVariables()
{
this.Title = "Employee Activity Tracker for " + PolicyUpdateLogName;
policyColumnHeader = new Literal();
confirmedColumnHeader = new Literal();
pendingColumnHeader = new Literal();
employeesForPolicy = new List<SPUser>();
confirmedEmployees = new List<SPUser>();
pendingEmployees = new List<SPUser>();
}
...
// uses the PolicyUpdateLogName custom property to load that List from Sharepoint
private void FetchPolicyUpdateLog_SPList()
{
site = new SPSite(siteURL);
policyUpdateLog_SPList = site.OpenWeb().GetList("/Lists/" + PolicyUpdateLogName);
}
...
protected void InitListBoxControls()
{
// Init ListBoxes
policies_ListBox = new ListBox(); // This box stores the policies from the List we loaded from SharePoint
confirmedEmployees_ListBox = new ListBox();
pendingEmployees_ListBox = new ListBox();
// Postback & ViewState
policies_ListBox.AutoPostBack = true;
policies_ListBox.SelectedIndexChanged += new EventHandler(OnSelectedPolicyChanged);
confirmedEmployees_ListBox.EnableViewState = false;
pendingEmployees_ListBox.EnableViewState = false;
}
...
private void LoadPoliciesListBox()
{
foreach (SPListItem policyUpdate in policyUpdateLog_SPList.Items)
{
// Checking for duplicates before adding.
bool itemExists = false;
foreach (ListItem item in policies_ListBox.Items)
if (item.Text.Equals(policyUpdate.Title))
{
itemExists = true;
break;
}
if (!itemExists)
policies_ListBox.Items.Add(new ListItem(policyUpdate.Title));
}
}
Do some reading up on the Sharepoint web part life cycle. Properties are not updated until the OnPreRender event.

Unable to hook into PropertyChanged event using MVVM-Light

Greetings, creating my first MVVM based WPF app and trying to figure out why I'm unable to hook into the PropertyChanged event of a dependency property.
Code in the parent view model:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
foreach (var client in clients)
{
client.PropertyChanged += onClientPropertyChanged;
}
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
}
// Never gets called
void onClientPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
{
//...
}
}
ViewableCollection is a simple extension of ObservableCollection to encapsulate a View.
In the ClientViewModel the setters are being called but RaisePropertyChanged isn't working as I would expect, because onClientPropertyChanged isn't being invoked. Both view models inherit from ViewModelBase.
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
client.Name = value;
RaisePropertyChanged("Name");
}
}
If I wire up PropertyChanged to a method inside the ClientViewModel then it is being fired, so I'm stumped as to why this isn't working in the parent view model. Where am I going wrong?
This SO question explains the problem; ObservableCollection protects the PropertyChanged event.
One solution is to use MVVM-Light Messenger:
void createClients()
{
var clients = from client in Repository.GetClients()
select new ClientViewModel(Repository, client);
Clients = new ViewableCollection<ClientViewModel>(clients);
Clients.CollectionChanged += onClientsCollectionChanged;
Messenger.Default.Register<PropertyChangedMessage<string>>(this, (pcm) =>
{
var clientVM = pcm.Sender as ClientViewModel;
if (clientVM != null && pcm.PropertyName == "Name")
{
// ...
}
});
}
createClients() should be refactored, but for consistency with the question code I'll leave it in there. Then a slight change to the property setter:
public string Name
{
get { return client.Name; }
set
{
if (value == client.Name) return;
string oldValue = client.Name;
client.Name = value;
RaisePropertyChanged<string>("Name", oldValue, value, true);
}
}

How to use ASP.Net server controls inside of Substitution control?

while the method we use in Substitution control should return strings, so how is it possible to use a donut caching in web forms on a server control which should be rendered server side?
for example Loginview control?
UPDATE
This is now a fully working example. There a few things happening here:
Use the call back of a substitution control to render the output of the usercontrol you need.
Use a custom page class that overrides the VerifyRenderingInServerForm and EnableEventValidation to load the control in order to prevent errors from being thrown when the usercontrol contains server controls that require a form tag or event validation.
Here's the markup:
<asp:Substitution runat="server" methodname="GetCustomersByCountry" />
Here's the callback
public string GetCustomersByCountry(string country)
{
CustomerCollection customers = DataContext.GetCustomersByCountry(country);
if (customers.Count > 0)
//RenderView returns the rendered HTML in the context of the callback
return ViewManager.RenderView("customers.ascx", customers);
else
return ViewManager.RenderView("nocustomersfound.ascx");
}
Here's the helper class to render the user control
public class ViewManager
{
private class PageForRenderingUserControl : Page
{
public override void VerifyRenderingInServerForm(Control control)
{ /* Do nothing */ }
public override bool EnableEventValidation
{
get { return false; }
set { /* Do nothing */}
}
}
public static string RenderView(string path, object data)
{
PageForRenderingUserControl pageHolder = new PageForUserControlRendering();
UserControl viewControl = (UserControl) pageHolder.LoadControl(path);
if (data != null)
{
Type viewControlType = viewControl.GetType();
FieldInfo field = viewControlType.GetField("Data");
if (field != null)
{
field.SetValue(viewControl, data);
}
else
{
throw new Exception("ViewFile: " + path + "has no data property");
}
}
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
}
See these related questions:
Turn off page-level caching in a
user control
UserControl’s RenderControl is
asking for a form tag in (C#
.NET)
One thing Micah's answer left out is that the substitution function must be static, accept a HttpContext parameter, and return a string. See this msdn page for more info.
I've also extended Micah's helper class to be a little more flexible.
Markup
<asp:Substitution ID="Substitution1" MethodName="myFunction" runat="server" />
Implemenation
public static string myFunction(HttpContext httpContext){
ViewManager vm = new ViewManager();
//example using a Button control
Button b = new Button();
b.Text = "click me"; //we can set properties like this
//we can also set properties with a Dictionary Collection
Dictionary<string,object> data = new Dictionary<string,object>();
data.add("Visible",true);
String s = vm.RenderView(b,data); //don't do anything (just for example)
//we can also use this class for UserControls
UserControl myControl = vm.GetUserControl("~mypath");
data.clear();
data.add("myProp","some value");
return vm.RenderView(myControl,data); //return for Substitution control
}
Class
using System.IO;
using System.ComponentModel;
public class ViewManager
{
private PageForRenderingUserControl pageHolder;
public ViewManager()
{
pageHolder = new PageForRenderingUserControl();
}
public UserControl GetUserControl(string path)
{
return (UserControl)pageHolder.LoadControl(path);
}
public string RenderView(Control viewControl, Dictionary<string, object> data)
{
pageHolder.Controls.Clear();
//Dim viewControl As UserControl = DirectCast(pageHolder.LoadControl(Path), UserControl)
if (data != null) {
Type viewControlType = viewControl.GetType();
dynamic properties = TypeDescriptor.GetProperties(viewControl);
foreach (string x in data.Keys) {
if ((properties.Item(x) != null)) {
properties.Item(x).SetValue(viewControl, data[x]);
}
}
}
pageHolder.Controls.Add(viewControl);
StringWriter result = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, result, false);
return result.ToString();
}
private class PageForRenderingUserControl : Page
{
public override void VerifyRenderingInServerForm(Control control)
{
// Do nothing
}
public override bool EnableEventValidation {
get { return false; }
// Do nothing
set { }
}
}
}
Thanks again to Micah for the code
I'm fairly certain you can't do this - the Substitution control will only allow you to insert a string into an outputcached page.
This makes sense if you think about the whole output of a server control, which could be a <table> that'll disrupt all your carefully crafted markup and/or something that requires a load of <script> injected into the page - whereas injecting a single string is something that's relatively straightforward.

How to validate against Multiple validation groups?

I have two validation groups: parent and child
I have an add button that needs to only validate the child validation group which is easily done. The save button needs to validate against the parent and child validation groups, both client side and server side. I think I know how to do it server side by calling the Page.Validate("groupname") method for each group, but how can it be done client side?
You should be able to accomplish this by creating a javascript function that uses Page_ClientValidate and then having the button call that function
<asp:Button ID="btnSave" Text="Save" OnClientClick="return validate()" runat="server" />
<script type="text/javascript">
function validate() {
var t1 = Page_ClientValidate("parent");
var t2 = Page_ClientValidate("child");
if (!t1 || !t2) return false;
return true;
}
</script>
The problem with CAbbott's answer is that validation errors that occur in the "parent" group will not be displayed after the call to validate the "child" group. The more minor problem with Oleg's answer is that validation of the "child" group will not occur until the "parent" group is ready.
All we really need to do to allow client-side validation of more than one group at the same time is to override the Javascript IsValidationGroupMatch method which determines whether or not a control is to be included in the current set being validated.
For example:
(function replaceValidationGroupMatch() {
// If this is true, IsValidationGroupMatch doesn't exist - oddness is afoot!
if (!IsValidationGroupMatch) throw "WHAT? IsValidationGroupmatch not found!";
// Replace ASP.net's IsValidationGroupMatch method with our own...
IsValidationGroupMatch = function(control, validationGroup) {
if (!validationGroup) return true;
var controlGroup = '';
if (typeof(control.validationGroup) === 'string') controlGroup = control.validationGroup;
// Deal with potential multiple space-delimited groups being validated
var validatingGroups = validationGroup.split(' ');
for (var i = 0; i < validatingGroups.length; i++) {
if (validatingGroups[i] === controlGroup) return true;
}
// Control's group not in any being validated, return false
return false;
};
} ());
// You can now validate against multiple groups at once, for example:
// space-delimited list. This would validate against the Decline group:
//
// Page_ClientValidate('Decline');
//
// while this would validate against the Decline, Open and Complete groups:
//
// Page_ClientValidate('Open Decline Complete');
//
// so if you wanted to validate all three upon click of a button, you'd do:
<asp:Button ID="yourButton" runat="server"
OnClick="ButtonSave_Click" CausesValidation="false"
OnClientClick="return Page_ClientValidate('Open Decline Complete');" />
If you call Page_ClientValidate(..) twice, only the last validation result will be shown and it can be OK while the first is not. So the second call should be made only if the first has returned true
<script type="text/javascript">
var parentOk= Page_ClientValidate('parent');
var childOk = false;
if (parentOk) {
childOk = Page_ClientValidate('child');
}
return parentOk && childOk;
</script>
Whatever way you do it requires some hacking to get round ASP.Net's assumption that you wouldn't try to do this. I favour a reusable approach which is explicit about the hackery involved.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WebSandbox.Validators
{
/// <summary>
/// <para>
/// Validates a different validation group. Among the use cases envisioned are
/// <list type="">
/// <item>
/// Validating one set of rules when the user clicks "Save draft" and validating those rules plus some
/// extra consistency checks when they click "Send".
/// </item>
/// <item>
/// Grouping controls in a <code>fieldset</code> into a validation group with a
/// <code>ValidationSummary</code> and then having a final <code>ValidationSummary</code> which tells the
/// user which groups still have errors.
/// </item>
/// </list>
/// </para>
/// <para>
/// We include checks against setting <code>GroupToValidate</code> to the same value as
/// <code>ValidationGroup</code>, but we don't yet include checks for infinite recursion with one validator
/// in group A which validates group B and another in group B which validates group A. Caveat utilitor.
/// </para>
/// </summary>
public class ValidationGroupValidator : BaseValidator
{
public string GroupToValidate
{
get { return ViewState["G2V"] as string; }
set { ViewState["G2V"] = value; }
}
protected override bool ControlPropertiesValid()
{
if (string.IsNullOrEmpty(GroupToValidate)) throw new HttpException("GroupToValidate not specified");
if (GroupToValidate == ValidationGroup) throw new HttpException("Circular dependency");
// Don't call the base, because we don't want a "control to validate"
return true;
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute("evaluationfunction", "ValidateValidationGroup");
writer.AddAttribute("GroupToValidate", GroupToValidate);
}
protected override void OnPreRender(EventArgs e)
{
// The standard validation JavaScript is too restrictive for this validator to work, so we have to replace a key function.
// Fortunately this runs later than the standard JS, so we can simply overwrite the existing value of Page_ClientValidate.
Page.ClientScript.RegisterStartupScript(typeof(ValidationGroupValidator), "validationJS", _ValidationJS);
base.OnPreRender(e);
}
protected override bool EvaluateIsValid()
{
if (string.IsNullOrEmpty(GroupToValidate)) return false;
bool groupValid = true;
foreach (IValidator validator in Page.GetValidators(GroupToValidate))
{
validator.Validate();
groupValid &= validator.IsValid;
}
return groupValid;
}
private const string _ValidationJS = #"<script type=""text/javascript"">
function ValidateValidationGroup(val) {
if (typeof(val.GroupToValidate) == ""string"") {
val.valid = PageMod_DoValidation(val.GroupToValidate);
}
}
function Page_ClientValidate(validationGroup) {
Page_InvalidControlToBeFocused = null;
if (!Page_Validators) return true;
var i, ctrl;
// Mark everything as valid.
for (i = 0; i < Page_Validators.length; i++) {
Page_Validators[i].finalValid = true;
}
if (Page_ValidationSummaries) {
for (i = 0; i < Page_ValidationSummaries.length; i++) {
Page_ValidationSummaries[i].finalDisplay = ""none"";
}
}
// Validate.
var groupValid = PageMod_DoValidation(validationGroup);
// Update displays once.
for (i = 0; i < Page_Validators.length; i++) {
ctrl = Page_Validators[i];
ctrl.isvalid = ctrl.finalValid;
ValidatorUpdateDisplay(ctrl);
}
if (Page_ValidationSummaries) {
for (i = 0; i < Page_ValidationSummaries.length; i++) {
ctrl = Page_ValidationSummaries[i];
ctrl.style.display = ctrl.finalDisplay;
}
}
ValidatorUpdateIsValid();
Page_BlockSubmit = !Page_IsValid;
return Page_IsValid;
}
function PageMod_DoValidation(validationGroup) {
var groupValid = true, validator, i;
for (i = 0; i < Page_Validators.length; i++) {
validator = Page_Validators[i];
ValidatorValidate(validator, validationGroup, null);
validator.finalValid &= validator.isvalid;
groupValid &= validator.isvalid;
}
if (Page_ValidationSummaries) {
ValidationSummaryOnSubmit(validationGroup, groupValid);
var summary;
for (i = 0; i < Page_ValidationSummaries.length; i++) {
summary = Page_ValidationSummaries[i];
if (summary.style.display !== ""none"") summary.finalDisplay = summary.style.display;
}
}
return groupValid;
}
</script>";
}
}

Resources