In the following mini-app, I am wondering why the BtnOk_Validating event handler is never called. I expected that clicking the Ok button would call the event handler.
The real dialog has many more controls, each that have a validating event handler. My plan was to use the Ok button validating event handler to call each of the other event handlers before allowing the dialog to close.
If it's not obvious, I'm quite the novice when it comes to Forms development.
using System.ComponentModel;
using System.Windows.Forms;
namespace ConsoleApp
{
class Program
{
static void Main( string[] args )
{
Dialog dialog = new Dialog();
dialog.ShowDialog();
}
}
public class Dialog : Form
{
Button m_BtnOk;
Button m_BtnCancel;
public Dialog()
{
m_BtnOk = new System.Windows.Forms.Button();
m_BtnCancel = new System.Windows.Forms.Button();
m_BtnOk.CausesValidation = true;
m_BtnOk.DialogResult = DialogResult.OK;
m_BtnOk.Text = "Ok";
m_BtnOk.Location = new System.Drawing.Point( 0, 0 );
m_BtnOk.Size = new System.Drawing.Size( 70, 23 );
m_BtnOk.Validating += new CancelEventHandler( BtnOk_Validating );
m_BtnCancel.CausesValidation = false;
m_BtnCancel.DialogResult = DialogResult.Cancel;
m_BtnCancel.Text = "Cancel";
m_BtnCancel.Location = new System.Drawing.Point( 0, 30 );
m_BtnCancel.Size = new System.Drawing.Size( 70, 23 );
Controls.Add( this.m_BtnOk );
Controls.Add( this.m_BtnCancel );
}
private void BtnOk_Validating( object sender, CancelEventArgs e )
{
System.Diagnostics.Debug.Assert( false ); // we never get here
}
}
}
Edit: Please see my follow-up question for a more complete example that works (well mostly).
Its because the button will never loose focus with it being the only control. If you add a TextBox or something that can take the focus of the button, then you will see it fire.
From MSDN
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl.ActiveControl property to the current form, focus events occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus
When you change the focus by using the mouse or by calling the Focus method, focus events occur in the following order:
Enter
GotFocus
LostFocus
Leave
Validating
Validated
If the CausesValidation property is set to false, the Validating and Validated events are suppressed.
Update: Like Hans mentions, you'll need to extract the validating you do in each of the Validating events for all the other controls into separate functions. Then you can create a ValidateAll function to check all values. If the function returns false, then you dont close the Form. If it returns true, you call this.Close(). So it might look like this:
// pseudo code
textbox1.Validating += ValidateTx1();
textbox2.Validating += ValidateTx2();
btnOk.Click += OkBtnClicked();
private void OkBtnClicked(...)
{
if(ValidateAll())
{
this.Close();
}
}
private bool ValidateTx1(...)
{
DoTx1Validation();
}
private bool ValidateTx2(...)
{
DoTx2Validation();
}
private bool ValidateAll()
{
bool is_valid = DoTx1Validation();
return (is_valid && DoTx2Validation());
}
Related
I'm trying to execute code in a SizeChangedEventHandler but the following is not working:
[TestMethod]
public void TestSizeChanged()
{
var panel = new System.Windows.Controls.StackPanel();
bool handled = false;
panel.SizeChanged += (o, e) =>
{
handled = true; // how to get this to be executed
};
panel.Width = 100; // naive attempt to change size!
Assert.IsTrue(handled);
}
I originally tried to use the RaiseEvent method but I was not been able to supply it with the correct xxxEventArgs type, due to not knowing the constructor arguments and the object browser is not helping:
panel.RaiseEvent(new System.Windows.SizeChangedEventArgs()) // does not compile
Obviously, the above test serves no purpose but I'm after correct way of getting the event to fire in a unit-tested environment.
It's very strange that the SizeChanged event doesn't fire with your code, it appears to be correct. Maybe the StackPanel doesn't exists in the visual tree because it's not really shown on the screen, so the event is never fired.
Try to show a real window with a StackPanel on the screen, and programmatically change his width or height.
[TestMethod]
public void TestSizeChanged()
{
Window wnd = new Window();
wnd.Content = new System.Windows.Controls.StackPanel();
bool handled = false;
wnd.SizeChanged += (o, e) =>
{
handled = true; // how to get this to be executed
};
wnd.Show();
wnd.Width = 100; // naive attempt to change size!
Assert.IsTrue(handled);
}
You can't use the RaiseEvent method, because SizeChanged is not a RoutedEvent.
Using the below reflection you can succeed:
//panel =>System.Windows.Controls.Panel instance..
SizeChangedInfo sifo = new SizeChangedInfo(panel, new Size(0, 0), true, true);
SizeChangedEventArgs ea = typeof(System.Windows.SizeChangedEventArgs).GetConstructors(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).FirstOrDefault().Invoke(new object[] {(panel as FrameworkElement),sifo }) as SizeChangedEventArgs;
ea.RoutedEvent = Panel.SizeChangedEvent;
panel.RaiseEvent(ea);
So I'm still debuting with Xamarin.Forms. So far so good, if I put aside a few pesky bugs I encountered. Here's the new guy. Maybe one of you could tell me if I'm doing something wrong.
Basically, I have a Xamarin.Forms.Switch on my interface, and I'm listening for changes in its state with the Toggled event. The doc says about this event: "Event that is raised when this Switch is toggled by the user."
Unfortunately, when I update the value of the switch programmatically, the event fires.
var mySwitch = new Switch ();
mySwitch.Toggled += (object sender, ToggledEventArgs e) => {
Console.WriteLine ("Switch.Toggled event sent");
};
mySwitch.IsToggled = true;
Any way to prevent the event from firing / to know that it's not the user who triggered it?
The behavior you're experiencing is correct: every time the IsToggled property changes, the switch will fire the Toggled event.
I'm not sure if the Xamarin.Forms documentation has been updated recently, but as of today it says this about the Toggled event:
Event that is raised when this Switch is toggled
Sample Code
Here is sample code that prevents the Toggled event from being handled when the Toggled event is not fired by the user
using System;
using Xamarin.Forms;
namespace SwitchToggle
{
public class SwitchPage : ContentPage
{
public SwitchPage()
{
var mySwitch = new Switch
{
IsToggled = true
};
mySwitch.Toggled += HandleSwitchToggledByUser;
var toggleButton = new Button
{
Text = "Toggle The Switch"
};
toggleButton.Clicked += (sender, e) =>
{
mySwitch.Toggled -= HandleSwitchToggledByUser;
mySwitch.IsToggled = !mySwitch.IsToggled;
mySwitch.Toggled += HandleSwitchToggledByUser;
};
var mainLayout = new RelativeLayout();
Func<RelativeLayout, double> getSwitchWidth = (parent) => parent.Measure(mainLayout.Width, mainLayout.Height).Request.Width;
Func<RelativeLayout, double> getToggleButtonWidth = (parent) => parent.Measure(mainLayout.Width, mainLayout.Height).Request.Width;
mainLayout.Children.Add(mySwitch,
Constraint.RelativeToParent((parent) => parent.Width / 2 - getSwitchWidth(parent) / 2),
Constraint.RelativeToParent((parent) => parent.Height / 2 - mySwitch.Height / 2)
);
mainLayout.Children.Add(toggleButton,
Constraint.RelativeToParent((parent) => parent.Width / 2 - getToggleButtonWidth(parent) / 2),
Constraint.RelativeToView(mySwitch, (parent, view) => view.Y + view.Height + 10)
);
Content = mainLayout;
}
async void HandleSwitchToggledByUser(object sender, ToggledEventArgs e)
{
await DisplayAlert(
"Switch Toggled By User",
"",
"OK"
);
}
}
public class App : Application
{
public App()
{
MainPage = new NavigationPage(new SwitchPage());
}
}
}
Toggled event will every-time called, when you manually change the toggle that time also Toggled event will fire.
The solution is just set
mySwitch.Toggled -= HandleSwitchToggledByUser;
before you manually change toggle value, and write
mySwitch.Toggled += HandleSwitchToggledByUser;
after you manual change of toggle,
Hope, this will help you
I have this code:
private void HandleGUI()
{
if (_currentForm == null)
{
navigationSideBar1.Visible = false;
pnlToolbar.Visible = false;
return;
}
if (_currentForm.ShowNavigationBar)
{
HandleNavigationButton(_currentForm);
}
btnSave.Visible = _currentForm.ShowSaveButton;
btnClose.Visible = _currentForm.ShowCloseButton;
btnSave.Paint += new PaintEventHandler(btnSave_Paint);
navigationSideBar1.Visible = _currentForm.ShowNavigationBar;
pnlToolbar.Visible = _currentForm.ShowToolBar;
btnSave.Refresh();
btnSave.Invalidate();
}
I am registered on the onpaint event of the save button (btnSave), but this event is not fired, even when I call Refresh or Invalidate. How is this possible?
EDIT:
This is how the saave button class looks like:
public class SaveButton : ButtonX
{
public SaveButton()
{
this.Image = Properties.Resources.Save;
this.Text = "Opslaan";
this.Size = new Size(108, 39);
}
}
Try adding a regular DevComponent button (i.e. not a subclass of it) to a test form and see if it ever fires its Paint event. They may have exposed a Paint event (so that their interface matches that of a regular button) but not actually implemented it.
Setting userpaint to true will fire the onpaint event
this.SetStyle(ControlStyles.UserPaint, true);
From MSDN:
Calling the Invalidate method does not force a synchronous paint; to force a synchronous paint, call the Update method after calling the Invalidate method.
So, you need an Update call. Now, Refresh is just Invalidate w/ update children + Update, so theoretically you're taken care of. All I can think is that Windows doesn't call Paint unless it really needs to, ie when the form is shown on the UI or written to a graphics device ("screenshot" of an invisible window). Are either of these the case?
It looks like you're not calling the base class constructor in the constructor of your SaveButton component.
public class SaveButton : ButtonX
{
public SaveButton() : base()
...
I have a strange problem with devexpress AlertControl. I create an alertu using this code
AlertInfo alertInfo = new AlertInfo(caption, text);
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(null,alertInfo);
this code is placed in backgroundWorker_DoWork function and it is supposed to display alerts from time to time. The problem is that alerts are not shown. I can see that show method is invoked however alerts are not shown.
Acording to documentation is I pass null as a parametr of Show function , notification should be shown on main monitor.
What can I do to make it work ?
Considering you're using a worker, I guess it's a thread problem. Try wrapping your code inside an Action object:
Action action = () =>
{
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(this, alertInfo); // "this" being a Form
};
this.Invoke(action);
I use a similar code inside a form with good results and once did a similar code using an AlertControl too.
Your AlertControl need a Parent Control.
AlertControl control = new AlertControl();
control.FormLocation = AlertFormLocation.BottomRight;
control.Show(MyForm,alertInfo); //replace null with a Form/Control instance
You call the Show method with a null paramater - where you should have use an instance of a Form/Control
Don't know anything about the devexpress controls, but maybe you have to show the alert from the main thread via invoke methode?
using DevExpress.XtraBars.Alerter;
// Create a regular custom button.
AlertButton btn1 = new AlertButton(Image.FromFile(#"c:\folder-16x16.png"));
btn1.Hint = "Open file";
btn1.Name = "buttonOpen";
// Create a check custom button.
AlertButton btn2 = new AlertButton(Image.FromFile(#"c:\clock-16x16.png"));
btn2.Style = AlertButtonStyle.CheckButton;
btn2.Down = true;
btn2.Hint = "Alert On";
btn2.Name = "buttonAlert";
// Add buttons to the AlertControl and subscribe to the events to process button clicks
alertControl1.Buttons.Add(btn1);
alertControl1.Buttons.Add(btn2);
alertControl1.ButtonClick += new AlertButtonClickEventHandler(alertControl1_ButtonClick);
alertControl1.ButtonDownChanged +=
new AlertButtonDownChangedEventHandler(alertControl1_ButtonDownChanged);
// Show a sample alert window.
AlertInfo info = new AlertInfo("New Window", "Text");
alertControl1.Show(this, info);
void alertControl1_ButtonDownChanged(object sender,
AlertButtonDownChangedEventArgs e) {
if (e.ButtonName == "buttonOpen") {
//...
}
}
void alertControl1_ButtonClick(object sender, AlertButtonClickEventArgs e) {
if (e.ButtonName == "buttonAlert") {
//...
}
}
ref:https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraBarsAlerterAlertControltopic
How can I prevent the firing of multiple events of the same kind triggered by a single action?
For example, I have a ListView containing some items. When I select or deselect all items, the SelectedIndexChanged event is fired once for each item. Rather, I would like to receive a single event indication the user's action (selection/deselection of items), regardless of the number of items.
Is there any way to achieve this?
You can't change the ListView code, and subclassing it doesn't provide many options.
I would suggest that you simply add a small delay (200ms or similar) to your code - i.e. you only do the calculation a little while after the last update. Something like:
using System;
using System.Windows.Forms;
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
ListView list;
TextBox txt;
Timer tmr = new Timer();
tmr.Interval = 200;
Form form = new Form {
Controls = {
(txt = new TextBox { Dock = DockStyle.Fill, Multiline = true}),
(list = new ListView { Dock = DockStyle.Right, View = View.List,
Items = { "abc", "def" , "ghi", "jkl", "mno" , "pqr"}})
}
};
list.SelectedIndexChanged += delegate {
tmr.Stop();
tmr.Start();
};
tmr.Tick += delegate {
tmr.Stop();
txt.Text += "do work on " + list.SelectedItems.Count + " items"
+ Environment.NewLine;
};
Application.Run(form);
}
}
Only by by coming at the problem from a slightly different direction. E.g. subscribe loss of focus.
In the end, the application or runtime cannot raise an event on "all selection changes done" without actually using something else because there is no way for the application to predict whether the user will perform another click on the control while it retains focus.
Even using focus, the user could switch back to that control.
If your ListView is in virtual mode, you could use the
VirtualItemsSelectionRangeChanged event. This event will be fired only once for the user's action (selection/deseclection).