I am trying to render a Wizard control to a HTML string on the click of a Button (using Control.Render). I am already disabling event validation with the following, which works fine and enables me to render the entire Page to the string. I do this within the user control that contains the Wizard:
protected void Page_Init(object sender, EventArgs e)
{
if (Request.Form["__EVENTTARGET"] != null
&& Request.Form["__EVENTTARGET"] == btnPrint.ClientID.Replace("_", "$"))
{
Page.EnableEventValidation = false;
}
}
While this works, I'd like to render the Wizard control on its own. I understand that I can override Page.VerifyRenderingInServerForm in order to prevent the page from throwing an exception when I attempt to render this control on its own (without runat="server" form tags), like so:
public override void VerifyRenderingInServerForm(Control control)
{
// base.VerifyRenderingInServerForm(control);
}
However, I don't want to override this completely. Is there a way I can bypass this dynamically, either:
For the specific PostBack in which the button in question is clicked, or...
Specifically for the Wizard control?
How about something like:
public override void VerifyRenderingInServerForm(Control control)
{
if (!SkipVerifyRenderingInServerForm)
{
base.VerifyRenderingInServerForm(control);
}
}
public bool SkipVerifyRenderingInServerForm
{
get
{
object o = HttpContext.Current.Items["SkipVerifyRenderingInServerForm"];
return (o == null) ? false : (bool) o;
}
set
{
HttpContext.Current.Items["SkipVerifyRenderingInServerForm"] = value;
}
}
You could then set SkipVerifyRenderingInServerForm to true in your button click event handler.
Related
I'm trying to implement some custom server-side validation logic for a custom form control running on Kentico v10.0.51 and .NET Framework 4.6. I want this logic to run on submit events, and I want to define the logic in the codebehind of the custom form control. How can I invalidate or stop processing of the form from inside a submit button click event handler? See attached for a reduced test case for example.
https://pastebin.com/Lnt0Rn9y
using System;
using CMS.FormEngine.Web.UI;
using CMS.Helpers;
// ReSharper disable ArrangeAccessorOwnerBody
namespace CMSApp.CMSFormControls.Custom
{
public partial class ServerSideValidator : FormEngineUserControl
{
public override object Value
{
get { return txtValue.Value; }
set { txtValue.Value = ValidationHelper.GetString(value, string.Empty); }
}
protected override void OnInit(EventArgs e)
{
Form.SubmitButton.Click += SubmitButtonOnClick;
base.OnInit(e);
}
private void SubmitButtonOnClick(object sender, EventArgs e)
{
var valid = CustomValidationHelper.ServerSideValidationMethod(Value);
if (!valid)
{
//TODO: Invalidate the form before save or notify. (Form.?)
}
}
}
}
Use the overrideed method:
/// <summary>
/// Returns true if a color is selected. Otherwise, it returns false and displays an error message.
/// </summary>
public override bool IsValid()
{
if ((string)Value != "")
{
return true;
}
else
{
// Sets the form control validation error message
this.ValidationError = "Please choose a color.";
return false;
}
}
Inside the else statement perform your validation on the field or expressions you want to validate and return a message based on what you're validating.
Today I got a problem in my development.
I have a Windows Form like this :
I need to enable the button "Appliquer" when the content of one of my textbox change.
I know that I can put the KeyPress event on each textbox and enable my button with that. In this window it can be easy to do that because there is only 10 textbox but I have an other window with more of 100 textbox and I think there is a better solution.
I tried to put the Keydown event directly in my windows form but it doesn't work.
So my question is, how can I do this. If someone have an idea ?
Thank you in advance !
Thomas
Since you already have 100+ textboxes in your form. I am assuming performance is not an issue for you.
In your form constructor, call this method. It will attach the event to all the textbox controls present in your form & inside sub controls such as groupbox, panel etc. (if you require)
There could be better ways of iteration..
public Form1()//your constructor
{
InitializeComponent();
AttachEvent(this);
}
void AttachEvent(Control CTrl)
{
foreach (Control c in CTrl.Controls)
{
if (c is TextBox)
{
c.TextChanged += new EventHandler(c_TextChanged);
continue;
}
if (c.HasChildren)
{
AttachEvent(c);
}
}
}
void c_TextChanged(object sender, EventArgs e)
{
//Your Code here btnGo.Enabled = !btnGo.Enabled;
}
What you can do is to extend TextBox make a field ( accessible from the designer ) to bind that TextBox into some other control.
public class MeTextBox
: TextBox
{
public override string Text
{
get { return base.Text; }
set
{
if ( m_DependantControl != null )
{
m_DependantControl.Enabled = !string.IsNullOrWhiteSpace(value);
}
base.Text = value;
}
}
Control m_DependantControl;
[Browsable(true)]
public Control DependantControl
{
get { return m_DependantControl; }
set { m_DependantControl = value; }
}
}
Now you can use MeTextBox as a regular TextBox. And if you want to make it control Enabled flag of some other Control you can just specify DependantControl property which will be accessible in the designer.
Fitting this into your example (code):
// assume you have a Button named btnConfirm
// and want to enable this button only when your `TextBox` has some text
MeTextBox mtb = new MeTextBox();
mtb.DependantControl = btnConfirm;
And if you do not want to make it in the code you can use designer directly.
To make it other way around ( one button dependant on many text boxes ) you can extend Button object :
public class MeButton
: Button
{
List<TextBox> m_DependantOn = new List<Control>();
[Browsable(true)]
public List<TextBox> DependantOn
{
get { return m_DependantOn; }
set { RemoveEvents(); m_DependantOn = value; AssignEvents(); }
}
void RemoveEvents()
{
foreach(TextBox ctrl in m_DependantOn)
ctrl.TextChanged -= WhenTextChanged;
}
void AssignEvents()
{
foreach(TextBox.ctrl in m_DependantOn)
ctrl.TextChanged += WhenTextChanged;
}
void WhenTextChanged(object sender, TextChangedEventArgs e)
{
this.Enabled = true;
}
}
In a c# code behind of a user control, how can i make the control invisible based on the value of a public property?
Let's say my user control is like so and contains content:
<Ctrl:ComponentChecker ID="ComponentChecker" runat="server" MakeInvisible="Yes">
<p>This is my content...</p>
</Ctrl:ComponentChecker>
My code behind is something like this:
public class ComponentChecker : AbstractController
{
public string MakeInvisible { get; set; }
protected override void OnPreRender(EventArgs e)
{
if (MakeInvisible != null && !string.IsNullOrEmpty(MakeInvisible) && MakeInvisible == "Yes")
{
//Hide the user control
}
}
}
Any ideas where I'm going wrong? I need to inherit from AbstractController for some other logic. After some reading online, inheriting from Placeholder and then this.Visible = false does the trick but I can't do that in my case.
I have a Custom Repeater control that inherits from Repeater and has paging functionality, however when I click the next page button the first time it refreshes the control but does not change the page, if I click it again after that it changes page perfectly.
I know what the issue is, when I click the next button it does a postback, then the data is bound to the repeater, and then after that the NextButton Event is handled.
Is there any way I can change the order of the page load events?? Or force the repeater to reload again after the event is handled??
I've included my Custom Repeater class bellow:
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Data;
using System.Collections;
using System;
namespace ASPresentation.Controls
{
[ToolboxData("<cc:PagedRepeater runat=server></cc:PagedRepeater>")]
public class PagedRepeater : Repeater
{
public int PageSize { get; set; }
public int CurrentPageIndex
{
get
{
return Convert.ToInt16(Page.Session["ProjectIndex"]);
}
set
{
Page.Session.Add("ProjectIndex", value);
}
}
public PagedDataSource pagedData = new PagedDataSource();
LinkButton NextBtn = new LinkButton();
LinkButton PrevBtn = new LinkButton();
public bool IsLastPage
{
get
{
return pagedData.IsLastPage;
}
}
public bool IsFirstPage
{
get
{
return pagedData.IsFirstPage;
}
}
public override object DataSource
{
get
{
return base.DataSource;
}
set
{
pagedData.DataSource = (IEnumerable)value;
}
}
protected void NextButtonClick(object sender, EventArgs e)
{
if (!IsLastPage)
{
CurrentPageIndex++;
}
}
protected void PrevButtonClick(object sender, EventArgs e)
{
if (!IsFirstPage)
{
CurrentPageIndex--;
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
NextBtn.Text = "Next";
PrevBtn.Text = "Prev";
NextBtn.Click += new EventHandler(NextButtonClick);
PrevBtn.Click += new EventHandler(PrevButtonClick);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
base.Controls.Add(PrevBtn);
base.Controls.Add(NextBtn);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
}
public override void DataBind()
{
pagedData.AllowPaging = true;
pagedData.PageSize = PageSize;
pagedData.CurrentPageIndex = CurrentPageIndex;
base.DataSource = pagedData;
base.DataBind();
}
}
}
A couple issues here that jump out at me.
One, why are you dynamically creating the prev/next button? Just put them in the ASCX. Show/hide them if you like based on whether your page index is first/last. If you must create them dynamically, do so in Init...
Two, do not store your page index in the session like that. What happens when you have two of these custom repeaters on one page? Use the ViewState. Key the string name to the control ID if necessary, but I think ViewState does this automatically(?).
Finally, what is triggering the DataBind? What event handler? It must be called from the Page that is hosting this control. If that's the case, then you also need to expose the Next/Prev clicks as events so that DataBind can be called in response to these events. This is how Microsoft's controls that handle paging work, such as the GridView. NextBtn.Click or PrevBtn.Click is guaranteed to be the last postback event handled.
You could handle the next/prev internally, but if you're going to do that you need to also call DataBind() in your code, so that it happens at the correct time.
Call "this.DataBind()" in the Page change functions.
This will mean you databind twice when changing pages but will work :-\
I've built a class that derives from System.Web.UI.WebControl. It basically renders pagination links (same as what you see on top of GridView when enabled) for use above a repeater.
I'm creating some anchor tags explicitly inside my nav control obviously, but they don't perform ajax postbacks. My understanding is that ajax requires POSTS to work right? Well, these would be GETs which I think is the problem.
Is there a way to achieve what I'm trying to do?
Thanks!
To take this advantage, you have to inherit the ICallbackEventHandler and implement its methods as follows.
public class CtlTest : WebControl, ICallbackEventHandler
{
private static readonly object EventClick = new object();
public CtlTest() : base(HtmlTextWriterTag.A) { }
public event EventHandler Click
{
add { base.Events.AddHandler(EventClick, value); }
remove { base.Events.RemoveHandler(EventClick, value); }
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + this.Page.ClientScript.GetCallbackEventReference(this, null, "null", null));
}
protected override void RenderContents(HtmlTextWriter writer)
{
base.RenderContents(writer);
writer.Write("Submit Query");
}
protected virtual void OnClick(EventArgs e)
{
EventHandler handler = this.Events[EventClick] as EventHandler;
if (handler != null)
handler(this, e);
}
#region ICallbackEventHandler Members
string ICallbackEventHandler.GetCallbackResult()
{
return string.Empty;
}
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this.OnClick(EventArgs.Empty);
}
#endregion
}
Whereas you are working on a data pager control and it requires to update some portions of the page, it's better to write a non Ajax enabled control and put it and its relative controls within an UpdatePanel.
Ok, I figured it out. I simply made my class implement the IPostBackEventHandler. This makes your control fire an event when the user takes action on something. In my case, it's clicking a nav pagenumber: [1][2][3][4][5][Next >]
Then, inside my render where I create the Anchor tags, I add this to each href (pageStartRow is different for each):
PostBackOptions options = new PostBackOptions(this, pageStartRow.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + Page.ClientScript.GetPostBackEventReference(options));
writer.RenderBeginTag(HtmlTextWriterTag.A);
The key is to pass something that uniquely identifies which link they clicked. This is done as the 2nd constructor parameter to the PostBackOptions class.
I then added the following items in my WebControl class:
// Defines the Click event.
public delegate void ClickHandler(object sender, GridPagingNavClickedEventArgs e);
public event ClickHandler Click;
//Invoke delegates registered with the Click event.
protected virtual void OnClick(GridPagingNavClickedEventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
public void RaisePostBackEvent(string eventArgument)
{
GridPagingNavClickedEventArgs e = new GridPagingNavClickedEventArgs(Convert.ToInt32(eventArgument));
OnClick(e);
}
The GridPagingNavClickedEventArgs contains a single item (pageNumber in my case).
Finally, in my aspx page (where I use the webcontrol), I do this in the Page_OnLoad:
gridNavTop.Click += new GridPagingNavigation.ClickHandler(gridNavTop_Click);
and this is the event code:
private void gridNavTop_Click(object sender, GridPagingNavClickedEventArgs e)
{
StartRow = e.PageStartRow;
}
As long as everything is inside an UpdatePanel, then it works perfectly!