I have a System.Web.UI.UserControl in my application which is to be used to display messages to the user, however after these messages are displayed to the user once I want them to clear (conditionally).
The simplified code I have now that I am trying to get to work is the following:
protected override void OnUnload(EventArgs e) {
if (_resetOnUnload) {
divMessageBlock.InnerHtml = "";
_resetOnUnload = false;
}
base.OnUnload(e);
}
However any changes to the view in the OnUnload event do not get transferred over on the next page load (form submit).
My question is how would I setup this user control to clear itself either before any messages can be added elsewhere or after the page has been rendered for the user and have stay that way?
Try this. Set the viewstate to save _resetOnUnload and then load it. OnLoad can stay the same, testing whether or not to clear.
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
if (_resetOnUnload) {
divMessageBlock.InnerHtml = "";
_resetOnUnload = false;
}
}
protected override void LoadViewState(object savedState) {
if (savedState != null) {
object[] myState = (object[])savedState;
_resetOnUnload = (bool)myState[0];
}
}
protected override object SaveViewState() {
object[] allStates = new object[]{ _resetOnUnload };
return allStates;
}
How about just outputting your message to a control with ViewState turned off? That way, your message will be displayed, and any subsequent Postback will clear the message.
I guess the OnPreRender event is the last one, where you can change what the control's output should be. After that, the control has been rendered, and your changes will have no effect.
Related
I'm trying to set a flag saying whether the last change on the Checked property was caused by the user or the program.
I'm using a custom RadioButton:
public class MyRadioButton : RadioButton
{
ValueChanger valueChanger = ValueChanger.Program;
public MyRadioButton()
{
this.Click += OnButtonClickedByUser;
this.CheckedChanged += OnCheckChange;
}
public void setChecked(bool val)
{
this.valueChanger = ValueChanger.Program;
this.Checked = val;
}
void OnButtonClickedByUser(Object sender, EventArgs e)
{
this.valueChanger = ValueChanger.User;
}
void OnCheckChange(Object sender, EventArgs e)
{
// do stuff depending on 'this.valueChanger'
}
enum ValueChanger
{
User,
Program
};
}
I call setChecked whenever the value was changed because of a message received from a serial connection, and I expect OnButtonClickedByUser to be called by the Click event whenever the value is changed through the UI.
My problem is that the CheckedChanged event fires before the Click event, which makes OnCheckChange unreliable.
Is there any way to fix that ?
User can change the value of the RadioButton by click on the control or by moving the focus to the control (arrow key, tab, mnemonic key combination).
Both OnEnter and ProcessMnemonic try to call PerformClick which calls OnClick which is responsible to checking the control. So you can override OnClick method:
protected override void OnClick(EventArgs e)
{
// Here CheckedChanged event has not been raised yet
base.OnClick(e);
}
To find out more about how RadioButton works internally, take a look at its source code.
I have a user control loaded by LoadControl, and on the code behind for said user control I try to access a control thats null.
Default.aspx:
protected void Page_Load(object sender, EventArgs e){
// ...
List<String> usersCustomers = custRepo.GetUserCustomers(currentUser.ID).Select(s => s.custName).ToList();
FileTrackingControl fileTrackingControl = (FileTrackingControl)LoadControl(typeof(FileTrackingControl), new object[] { usersCustomers, currentUser });
dashboardWidgetPanel.Controls.Add(fileTrackingControl);
// ...
}
FileTrackingControl.ascx:
public partial class FileTrackingControl : System.Web.UI.UserControl
{
List<string> _custNames;
User _currentUser;
public FileTrackingControl(List<string> custNames, User currentUser)
{
this._custNames = custNames;
this._currentUser = currentUser;
}
protected void Page_OnInit(object sender, EventArgs e)
{
StatToCwData scData = new StatToCwData();
GridView fileTrackingResultsFC = (GridView)FindControl("fileTrackingResults");
// CRASH HERE. NPE: fileTrackingResults is NULL
fileTrackingResults.DataSource = scData.GetControlData(6, _currentUser, _custNames);
fileTrackingResults.DataBind();
}
}
On the basis that you do have a grid view with ID fileTrackingResults (it's always worth double checking!), then I think you're trying to access the controls before they are created (as I recall MS says you shouldn't access the control tree in oninit events). In your case it's probably a case of simply moving the code into the Page_Load event, OR create a Page_PreLoad event and map the event handler manually if you need to reserve the Page_Load for some other sort of functionality. Alternatively, you could create a LoadData type method on your user control and fire it manually (use an interface to declare the method so you can roll it across other code).
Server Transfer with preserveForm true in EventHandler refires that event Handler and causes infinite loop.
MY QUESTION: How can I indicate in the handler that the event has been handled.
PS: I know we can set the preserveForm to false, but I dont want to do that.
Sample Code:
protected void rbThemes_SelectedIndexChanged(object sender, EventArgs e)
{
Server.Transfer(Request.FilePath, true);
}
Great question that I'm facing right now. I don't know the answer either, I would imagine one would modify Request.Form data to remove the event, though I'm not sure how to do this cleanly.
As a workaround, I use a guard flag in Context.Items, which is preserved as well.
protected void rbThemes_SelectedIndexChanged(object sender, EventArgs e)
{
if (IsSecondPass()) return;
Server.Transfer(Request.FilePath, true);
}
private bool IsSecondPass()
{
const string key = "SECOND_PASS_GUARD";
if (Context.Items[key] == null)
{
Context.Items[key] = new object();
return false;
}
else
{
Context.Items.Remove(key);
return true;
}
}
I wouldn't recommend this, but it works. (Method name is also very poorly chosen as it side-effects.)
There's also a shorter way:
protected void rbThemes_SelectedIndexChanged(object sender, EventArgs e)
{
if (PreviousPage != null) return;
Server.Transfer(Request.FilePath, true);
}
Beware that it doesn't have any undesired effects if you do other kinds of cross-page postings (though I don't know why or how you would cross-post a SelectedIndexChanged event from another page). Still not recommended.
Note: If you're coding a master page, you'll need to reference PreviousPage from the Page property on the master page class (Page.PreviousPage).
I'm trying to intercept data pasted into a WPF textbox.
For example, the user creates a screen capture with the Windows snipping tool,
which automatically places the image data on the clipboard. The idea here is to
allow the user to simply CTRL+V on the TextBox so that I can intercept it, check if it's
data and then do whatever I want with it.
public class PasteBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
DataObject.AddPastingHandler(AssociatedObject, new DataObjectPastingEventHandler(OnPaste));
}
protected override void OnDetaching()
{
base.OnDetaching();
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
if (e.SourceDataObject.GetDataPresent(DataFormats.Text))
return;
var formats = e.SourceDataObject.GetFormats();
foreach (var format in formats)
Console.WriteLine(format);
}
}
Using the behavior above, the code does get triggered when text is pasted into the TextBox
but it would seem the TextBox does not allow anything else to be pasted so it never even reaches this code if it's not text.
I'm wondering, is there a property that needs to be set on the TextBox, or something else
that would allow data to be pasted (even though the TextBox can never display that data)
If not, what UI elements do allow data to be pasted, as I might be able to use that to my advantage as well.
Update
Someone posted out to me that I'd have to use a RichTextBox to allow pasting
like this, which is not something I can use, so I decided to take a different (somewhat hacky) approach:
public class PasteBehavior : Behavior<UIElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PreviewKeyDown += AssociatedObject_PreviewKeyDown;
}
void AssociatedObject_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Control && e.Key == Key.V)
{
if (Clipboard.ContainsData(DataFormats.Dib))
{
using (var stream = new MemoryStream())
{
var image = Clipboard.GetImage();
var message = new ImagePastedMessage()
{
ImageData = GetImagePngData(image)
};
Messenger.Default.Send(message);
}
e.Handled = true;
}
else if (Clipboard.ContainsFileDropList())
{
var results = Clipboard.GetFileDropList();
var filenames = new string[results.Count];
results.CopyTo(filenames, 0);
var message = new FilesDroppedMessage()
{
Filenames = filenames
};
Messenger.Default.Send(message);
e.Handled = true;
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
}
private byte[] GetImagePngData(BitmapSource source)
{
using (var stream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(stream);
return stream.ToArray();
}
}
}
This allows me to paste images and files into the TextBox but only
using the CTRL+V keys, not using the default context menu of the TextBox.
So I'm still interested in knowing if there is a better/easier way
Update 2
Based on the solution by Daniel, which works really well, I've updated the OnAttached:
protected override void OnAttached()
{
base.OnAttached();
CommandManager.AddPreviewCanExecuteHandler(AssociatedObject, onPreviewCanExecute);
CommandManager.AddPreviewExecutedHandler(AssociatedObject, onPreviewExecuted);
}
And removed the PreviewKeyDownHandler.
You can use CommandManager.PreviewExecuted and CommandManager.PreviewCanExecute routed events to handle your pasting logic.
For example, let's suppose you want to accept an image from the clipboard when a user tries to paste it into your TextBox. So first, define the methods that will handle both events:
private void onPreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// In this case, we just say it always can be executed (only for a Paste command), but you can
// write some checks here
if (e.Command == ApplicationCommands.Paste)
{
e.CanExecute = true;
e.Handled = true;
}
}
private void onPreviewExecuted(object sender, ExecutedRoutedEventArgs e)
{
// If it is a paste command..
if (e.Command == ApplicationCommands.Paste)
{
// .. and the clipboard contains an image
if (Clipboard.ContainsImage())
{
// proccess it somehow
e.Handled = true;
}
}
}
Then, you have to associate those methods with the routed events (this could go in the constructor, for example):
CommandManager.AddPreviewExecutedHandler(myTextBox, onPreviewExecuted);
CommandManager.AddPreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
And it should work with both the keyboard shortcut and the menu 'button'.
It is important to handle the PreviewCanExecute event. By default, the TextBox will only accept text as a 'pasteable' content, so you need to mark that content somehow in order to paste it.
EDIT:
Also, it is a good practice to remove the 'listeners' from the event if you can. As you're using behaviors, you can do this by overriding the 'OnDetaching' method in your behavior. This could prevent memory leaks if the events are not Weak Events:
protected override void OnDetaching()
{
base.OnDetaching();
CommandManager.RemovePreviewExecutedHandler(myTextBox, onPreviewExecuted);
CommandManager.RemovePreviewCanExecuteHandler(myTextBox, onPreviewCanExecute);
}
I have a problem with a webform.
My Goal: Intially when a page is loading, it has to load every textbox empty. After filling the info and click submit, it has to get submitted(UpdatePaymentInfo())
Problem: Here, When the user fills the info and clicks Submit,it calls onload function even before the submit button and makes all text box empty.
Here is the code:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string QueryStringupdatecreditcard1 = Request.QueryString.ToString();
if (String.Equals(QueryStringupdatecreditcard1, "tabID=B"))
{
divTitle.Visible = false;
trmain.Visible = false;
tdOrderSummary.Visible = false;
trCCandBilling.Visible = true;
trtest2.Visible = false;
divUpdatecreditcard.Visible = true;
trusecompaddress.Visible = false;
txtFirstName.Text = "";
txtLastName.Text = "";
txtAddress1.Text = "";
txtAddress2.Text = "";
txtCity.Text = "";
txtZip.Text = "";
txtCardNo.Text = "";
txtVccNumber.Text = "";
trAmountCharged.Visible = false;
}
}
protected void imgbtnSubmit_Click(object sender, ImageClickEventArgs e)
{
try
{
UpdatePaymentInfo();
}
}
Wrap the current contents of your OnLoad method in:
if (!Page.IsPostBack)
{
// Code in here will only be executed when the page is *not* being loaded by a postback
}
This is because, as per the ASP.NET Page Life Cyle, the things that you care about in this instance happen in this order:
Load - During load, if the current request is a postback, control
properties are loaded with information
recovered from view state and control
state.
Postback event handling - If the request is a postback, control event
handlers are called. After that, the
Validate method of all validator
controls is called, which sets the
IsValid property of individual
validator controls and of the page.
So what happens is (somewhat simplified):
You click the image button, triggering the postback.
The data from your form is loaded into your controls.
Your OnLoad method overwrites the values in the controls to clear them.
Your click handler is run, but because of step 3 it sees empty values.
As others have sort-of mentioned, it wouldn't necessarily be a bad thing to refactor your OnLoad method whilst you're doing this. At the moment you seem to have it doing two distinct things:
Clearing the text fields
Setting the visibility of fields
It might be worth separating this into one or two (depending on if the visibility setting and field clearing will be done independently) separate methods and adjusting your OnLoad method so it looks like this:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsInPostBack)
{
SetFieldVisibility();
ClearFields();
}
}
Page_Load always occurs.
See the documentation on the Page Lifecycle
What you need to do is check to see if the Page_Load is being triggered by a Postback.
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
///do stuff in here that you want to occur only on the first lad.
}
else
}
// code that you want to execute only if this IS a postback here.
{
}
// do stuff you want to do on Page_Load regardless of postback here.
}
You can use the IsPostBack property of the Page as follows:
protected override void OnLoad(EventArgs e) {
if (!Page.IsPostBack) {
EmptyTextBoxes();
}
}
Have you tried wrapping the form reset code in a check to see if the page is a postback?
if(!Page.IsPostback) {
// Do form reset here
}
You thought about using the IsPostBack page variable?
protected override void OnLoad(EventArgs e)
{
if(!IsPostBack){
//all your logic here.
}
}
if it's the case, you might use a databound control and set it to insert mode