Calling a controls event handler - c#

I decided to try a third-party's implementation of the DataGridView, specifically for the purpose of taking advantage of hierarchical grids, i.e. "Grid within a grid" functionality. While this is working out fairly well, I noticed some of the events and properties I depend on with normal DataGridViews don't exist in this implementation, and had to discover different ways to accomplish the same goals.
One such goal was that, when I use a CheckBoxColumn and the user "checks" or "un-checks" the box, I need to push that change to the grid (and subsequently the database) when it happens, otherwise the user would have to hit enter or leave the cell focus to actually commit the change.... Kind of wonky. To remedy this, I took advantage of the 'CurrentCellDirtyStateChanged' event, and manually committed the check or un-check to the grid, which in turn fired my 'CellValueChanged' event:
void dataGridView_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if ((dataGridView.IsCurrentCellDirty) & (dataGridView.CurrentCell.ColumnIndex == 0))
{
dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
However, this event doesn't exist with the third-party grid, and neither does the CommitEdit method. In order to achieve the same functionality, I ended up having to do this:
void GridView_ValueChanged(object sender, EventArgs e)
{
if(sender.GetType().ToString().Contains("RadCheckBoxEditor"))
{
RadCheckBoxEditor rad_chb = (RadCheckBoxEditor)sender;
GridCheckBoxCellElement checked_a_box = (GridCheckBoxCellElement)rad_chb.OwnerElement;
checked_a_box.Value = rad_chb.Value;
GridViewCellEventArgs new_args = new GridViewCellEventArgs(checked_a_box.RowInfo, checked_a_box.ColumnInfo, checked_a_box.Editor);
GridView_CellValueChanged(checked_a_box, new_args);
}
else
{
return;
}
}
I essentially caught the dirty value, assigned it to the value of a type recognizable by 'CellValueChanged' handler, defined my own event args, and explicitly called the handle. Are there any potential smells to something like this? It just feels wrong. I noticed others suggested making an independent function to access shared code, yet, those examples didn't seem to depend on a specific event pushing an event.

Related

Best way to handle controls automatically updating each other?

I have a question regarding updating Controls in .Net in such a way so that if the user updates one field, the other field will be updated with some data automatically and vice versa. I am using two NumericUpDown controls which convert some data.
The problem I am having is that, I am using the ValueChanged event. Because of this, sometimes these controls get stuck in a loop where one controls updates the other and the other tries to update the first one. The result is somewhat random.
So, what would be the best approach to handle this situation? In short, I only want to update the other control, if the first one was modified by a user himself.
Any help would be greatly appreciated.
Just use a boolean guard in the class to check whether you are inside an update method.
While you are updating, all future events fired from the NUDs will be ignored.
private boolean updating = false; // Class level variable
void event_handler(...) // The function hooked up to the ValueChanged event
{
if( !updating )
{
updating = true;
// Do your calculations and update the NUDs
updating = false;
}
}
I would recommend that you use data binding and bind to an object which serves as your model. Your model then is where the logic goes that alters the other value based on changing of a property. The model also raises IPropertyChanged / IPropertyChanging events, which the UI will pick up on. Not only will this prevent the issue you describe, it also keeps this business logic outside of your UI layer should you move to something else (say from WinForms to WPF, or Asp.Net MVC).
If method Foo handles the event of one control and method Bar handles the event for the other, then Foo should change the values of Bar's control and vice-versa. But you should use a control variable somewhere (say, a reference to the control that fired the event is a good idea). So that if Foo is called:
Foo updates the value of Bar's control;
Bar's control fires its event, and Bar is called;
Bar checks the reference for the control that shot first, sees that it's not its control, and does nothing.
Same logic applies to Bar.
that way you don't get an infinite loop.
In code, it'd, look like this:
nud1.ValueChanged += new Eventhandler(Foo);
nud2.ValueChanged += new Eventhandler(Bar);
NumericUpDown shooter = null;
private void Foo (object sender, EventArgs e)
{
if (this.shooter == null)
{
this.shooter = nud1;
nud2.Value = nud1.Value;
}
else this.shooter = null;
}
private void Bar (object sender, EventArgs e)
{
if (this.shooter == null)
{
this.shooter = nud2;
nud1.Value = nud2.Value;
}
else this.shooter = null;
}
Of course, this is a crude example (for example, it assumes the value of both controls is always changing. Adapt to your situation.
I like Andy's response about going with an MVC pattern, but if that's too radical of a change for this specific situation, you should set the values only if the current value is different than the value being assigned. That would prevent the ValueChanged event from firing again and stop the infinite loop the first time recursion happens.
// Inside your value changed handler for Control2,
// instead of directly setting the value of Control1, do this:
if(Control1.Value != valueBeingSet)
{
Control1.Value = valueBeingSet;
}

ASP.NET - How to check if one or more field values have been changed

I have a web form where I register a new employee. There're 3 parts in the form: Personal info, Address info, Special Status. But there's only one button for the whole form. When I submit the form all the information is updated to the database. So three Update statements are executed against the database. The methods are UpdatePersonalInfo, UpdateAddressInfo and UpdateSpStatus. Is there a way to check if there's been a change in any field in the certain part and run update method only if it's true. So something like this:
if (There's been any change to the personal data of the employee)
{
UpdatePersonalInfo;
}
if (There's been any change to the address information of the employee)
{
UpdateAddressInfo;
}
Sure I know, I can save all the previous values in a session object in PageLoad and then compare them one by one before running the method. But I thought maybe there's a magic way of doing this more easily.
Not sure that this is a better solution than any of the alternatives you already mentioned, but you could create a default handler to attach to the TextChanged, SelectedIndexChanged, etc events of your controls to keep track of which ones have changed.
List ChangedControls = new List(Of, String);
private void ChangedValue(object sender, System.EventArgs e) {
WebControl cntrl = (WebControl) sender;
ChangedControls.Add(cntrl.ID);
}
Then on your button click scour the ChangedControls list for the relevant controls.

Changing button event after button click in C#

I've a form that navigates webpage and access data. It looks like something below.
private void LoginButton_Click(object sender, EventArgs e)
{
if(LoginButton.Text == Login)
{
LoginButton.Text = "Logging in...";
....
...
Login process goes here...
..
if(Login Successed)
{
LoginButton.Text ="Download";
}
else
{
LoginButton.Text = "Login";
}
}
else if(LoginButton.Text==Download)
{
Download data here...
}
}
Same button(And same event too), doing two process and seems like different events with a label.
1) If there any problem like inefficiency run?
2) Any alternate ways to do this like different flag schemes?
3) Any method to have with more than one event for same button to achieve same idea?
Thanks.
1) If there any problem like inefficiency run?
Button clicks run at human time. You can burn half a billion cpu instructions without inconveniencing the user.
2) Any alternate ways to do this like different flag schemes?
Using the Text property of the button is fragile, your code will break when somebody decides to change the button text or when you localize your app to another language. A simple private bool field in your class is much better.
3) Any method to have with more than one event for same button to achieve same idea?
No. You could of course use two buttons, placed on top of each other and one of them always hidden. Makes localization much simpler and you'll get that bool field for free.
like Daniel A. White said
have two buttons
may be on some event like oncreate/onload do check..jst a pseudo code
if process is login then
do
//then showLoginButton
btnlogin.visible
else
//download
btndonload.visible
inside the login button
if(Login Successed)
{
btndonload.visible
}
else
{
LoginButton.Text = "Login";
}
this may be better with two buttons then single..and cleaner also
Write custom event handlers for the mouseClick
Write separate methods for login and download.
Register your custom event handlers to the button click event
I assume there is some logic that decides that the button text should be "download" or "login". At that point, set the button text of course, but also register the appropriate event handler.
This will allow you to have a single button that does anything
protected void Login_MouseClickHandler (object obj ,MouseClickEventArgs e) {
// login logic
// this would be the logic you say is "inside the login button"
}
protected void Download_MouseClickHandler (object obj ,MouseClickEventArgs e) {
// download logic
}
// pseudo code
// note that there is only one button
if process is login then
theButton.text = "login"
theButton.MouseClick += new MouseClickEventHandler(Login_MouseClickHandler)
else
button.text = "download"
theButton.MouseClick += new MouseClickEventHandler (Download_MouseClickHandler)
end if
Software Design Thoughts
Easier to extend. We don't need another button for every new thing to do
Separation of Concerns - All login code, for example, is in a separate method that does only login stuff.
Change is isolated and minimized. Writing new, separate methods is less error prone than in-lining that code in your if else structure. And consequently the if else structure is kept simple and comprehensible.
It is generally a bad idea use the text as the state. Ideally, you should have 2 buttons that fire different events and call out the main logic to a presenter in the MVP pattern.
Use control containers such as Panel and GroupBox. You can have a whole bunch of Panels for controls in different states.

When Control.ParentChanged Event occurs?

I read about Control.ParentChanged Event on MSDN
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.parentchanged(VS.71).aspx
But I don't understand the sample code: there's no ParentChanged at all appearing in source code ?
private void currencyTextBox_TextChanged(object sender, EventArgs e)
{
try
{
// Convert the text to a Double and determine if it is a negative number.
if(double.Parse(currencyTextBox.Text) < 0)
{
// If the number is negative, display it in Red.
currencyTextBox.ForeColor = Color.Red;
}
else
{
// If the number is not negative, display it in Black.
currencyTextBox.ForeColor = Color.Black;
}
}
catch
{
// If there is an error, display the text using the system colors.
currencyTextBox.ForeColor = SystemColors.ControlText;
}
}
So I don't understand what Control.ParentChanged Event is or does.
Hehe, they just couldn't come up with a good example. And punted by showing a generic FooChanged event handler instead. Yeah, useless.
It is quite unusual to implement a ParentChanged event handler yourself. It's a big deal in the Winforms internals, properties like BackColor, ForeColor, Font are 'ambient' properties. If they are not overridden from the default then they'll get the value of the Parent. Which of course means that it is really important to notice that the parent changed. The winforms code already takes care of it, you very rarely have to worry about it. Unless you create your own ambient property of course.
There would be another piece of code elsewhere that registers this as an event handler:
currencyTextBox.ParentChanged += new EventHandler(currencyTextBox_TextChanged);
However, I agree - the method name is misleading.
This event handler will fire when you change the parent control of this control to a different parent control.
You may want to read up on raising and consuming events.

What is the last event to fire when loading a new WPF/C# window?

I am trying to load a preferences window for my application and I would like the apply button to initially be disabled, then when a preference is updated, the apply button gets enabled again. I have some controls data bound to a preferences object and what happens is that after the window loads, the combobox events get triggered. Is there any event that is guaranteed to happen dead last after everything is stable?
Here is what my code looks like (the apply button is always enabled after the window loads):
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_preferencesData = new PreferencesDataContext();
LayoutRoot.DataContext = _preferencesData;
ButtonApply.IsEnabled = false;
}
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
ButtonApply.IsEnabled = true;
}
Is it also interesting to note that this only happens with textboxes and comboboxes, not checkboxes or radiobuttons.
Best solution for simple need
Joseph's answer is the best solution by far for your simple need: Just use data binding and let the data model handle it.
Answer to question as posed
There are more complex scenarios when you really do need control after absolutely everything has finished loading and all events have fired. There is no single event that occurs "dead last", but it is easy to effectively roll your own using the Dispatcher queue.
This is how to do it:
Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
{
var x = ComputeSomething(1, 2, 3);
DoSomething(x, "Test");
}));
Everything inside the { } will be executed when WPF finishes everything at a higher priority than ContextIdle, which includes all event handlers, loaded events, input events, rendering, etc.
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be found applied in addition to any element-specific initialization you may define [note: Initialized event not fired for leaves in a logical tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional object tree construction including more constructors and getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is shown. The other steps generally don't happen until a Window is shown, but they can happen earlier if triggered manually.
The Window.ContentRendered event fulfilled my requirements.
I just did kind of the same thing behaviorly in a systray WPF app.
However, I didn't do it using event handling. I simply bound the Enabled property of my button to a property in my ViewModel, and had the property updated whenever I needed the behavior.
You can use ManagedSpy to figure this out on your own.
http://msdn.microsoft.com/en-us/magazine/cc163617.aspx
Setting the DataContext will likely fire the SelectionChanged event, and you can't rely on when exactly it's fired. Some logic checking on what exactly is selected would be more reliable:
private void ComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (myComboBox.SelectedItem == null)
{
buttonApply.IsEnabled = false;
}
else
{
buttonApply.IsEnabled = true;
}
}
The reason it's happening afterwards with your code as-is is because the event gets queued on the thread for the UI, so it's up to Windows if it will execute the next line of code in Load, or to handle the other events on the queue.
Not to throw a whole lot of stuff at you that you may or may not be familiar with, but if this is a relatively new codebase, you may want to consider using the MVVM pattern and use Commands instead of the archaic (emphasis mine) eventing model.
Order of Events in Windows Forms
Control.HandleCreated
Control.BindingContextChanged
Form.Load
Control.VisibleChanged
Form.Activated
Form.Shown

Categories

Resources