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
Related
I have this code;
Button button = new Button();
MessageBox ms = new MessageBox(button);
Action<bool> action = ms.Show();
action += (b) =>
{
Console.WriteLine(b.ToString()); //this isnt working
Console.WriteLine("??");
};
Console.Read();
button.OnClick();
Console.ReadKey();
MessageBox class :
class MessageBox
{
Button _button;
public MessageBox(Button button) { _button = button; }//initialize button
public Action<bool> Show()
{
Action<bool> action = new Action<bool>(CallForTest);
_button.OnClick+=()=>{ action?.Invoke(true); };
return action;
}
//...working.*//
public void CallForTest(bool statu){}
}
I want to return an action and when button is clicked,call the action.But this isnt working? What is the problem? Action is a delegate so delegate is a reference type?(compiler generated class) What is wrong in this picture?
I think when "Show()" is ends,"action" is collected from gargabe collector.But this is working with other reference types? for example;
public Test Show()
{
Test test = new Test("??");
button.OnClick += () =>
{
test.JustForTest(); //working (cw("?????" + ctorvalue);
};
return test;
}
Delegates are immutable. When you are combining two delegates using +=, you are actually creating a new delegate. So when you have done act += ... in the above code, you have actually created a new delegate, it is different from what you have already created in Show() method.
I believe this is happening because when you use += to a delegate it does not append to the internal list. This is why you don't see b.string() being printed
Without changing your design you won't be able to append the action to the original delegate when the button is clicked.
What you are actually writing is somthing like:
var act2 = new Action<bool>((b) =>
{
Console.WriteLine(b.ToString()); //this isnt working
Console.WriteLine("??");
});
var act = act + act2;
as you can see act is getting a new reference to the combined expression of act + act2 rather than act itself concatenating act2 internally.
if you do act(false) you will see the extra results, but not if you invoke the button click.
What you should be using is event on the delegate within the Button, which is the way UI controls are written
class Button
{
public event EventHandler<BoolEventArgs> Click;
}
best to read up on using events when you want to have multicast delegates in this way. MSDN site
I want to capture 'Send' button event of outlook using UI Automation.
Right now i am able to get 'Focus Change Event' like whenever iam minimizing or maximizing the WINWORD window the the event is raised instead of that i want to get the event on Send button click.
private void SendButtonInvoke()
{
Process[] processes = Process.GetProcessesByName("WINWORD");
AutomationElement aeOutLook = null;
foreach (var item in processes)
{
aeOutLook = AutomationElement.FromHandle(item.MainWindowHandle);
}
//AutomationElement outlookelm = AutomationElement.FromHandle(processName.MainWindowHandle);
AutomationElement buttonAddInstance = aeOutLook.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Send"));
if (buttonAddInstance == null)
{
MessageBox.Show("Add button instance not found");
}
else
{
AutomationPropertyChangedEventHandler ButtonEvent =
new AutomationPropertyChangedEventHandler(ButtonChecked_EventHandler);
//Attaching the EventHandler
Automation.AddAutomationPropertyChangedEventHandler(buttonAddInstance, TreeScope.Children,
ButtonEvent, AutomationElement.NameProperty);
}
}
private void ButtonChecked_EventHandler(object sender, AutomationEventArgs e)
{
AutomationElement ar = sender as AutomationElement;
MessageBox.Show("Button Clicked Sucessfully.");
}
You have to specifiy the EventHandler for the involved UIA Pattern. (For your case it's likely to be the InvokePattern):
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, AutomationElement buttonAddInstance ,TreeScope.Element, new AutomationEventHandler(OnStartInvoke));
private static void OnStartInvoke(object src, AutomationEventArgs e)
{
//logic
}
I wrote and tested the code below and it seems to work for me.
private void AddEmailSendEvent()
{
// Find the new email window
PropertyCondition newEmailWindowCondition = new PropertyCondition(AutomationElement.NameProperty, "Untitled - Message (HTML) ");
AutomationElement NewEmailWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, newEmailWindowCondition);
// Find the Send Button
PropertyCondition sendEmailButtonCondition = new PropertyCondition(AutomationElement.NameProperty, "Send");
AutomationElement sendButton = NewEmailWindow.FindFirst(TreeScope.Descendants, sendEmailButtonCondition);
// If supported, add the invoke event
if (sendButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, sendButton, TreeScope.Element, handler);
}
private void handler(object sender, AutomationEventArgs e)
{
// Do whatever is needed, for testing this just adds a message to my forms Main UI
AddMessage("Invoke event occured");
}
I should note that I'm using the .Net 4.0 automation libs. I've found the older ones don't always work the way I want them. I also tested this with Outlook 2013, and both outlook and the new email message were already open when I tested this. It doesn't handle waiting for them to appear.
Just so your aware, these events don't always work for all controls. Some custom controls are made in such a way the invoke events are not reported to the UI in a way the event can register. With that said, from my testing you should be able to use this method on the send button.
Invoking vs mouse clicks: Just to add a little more detail, the standard control causes the invoke event to fire when a user clicks it. "Invoke" is just the standard event fired on clickable controls. The only time a click wouldn't fire the same invoke is if the developer decided to intercept the click somehow and redirect it elsewhere. I've seen this a lot when people build there own custom controls.
If your not sure about whether a control using/firing the invoke event or not you can get use the Accessible Event Watcher to watch a control as you click it. You can get more information on the tool here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317979(v=vs.85).aspx
I have some code that when I call my CustomMessageBox it displays the box with a user prompt for an amount of my object to add, once that is done I have it added to a list of objects. Once added, it then Displays a MessageBox.Show to just let the user know it was added.
My problem is that when I run the code it executes all the code, bypasses the display of the Custom message box, then displays the MessageBox.Show, and THEN displays the CMB.Show. I ran the code through the debugger and followed the trail and it hits the CMB.Show before the MessageBox.Show, but is displayed once the code is done. Sorry, I am still learning and might not be telling the problem well, please let me know if there is anything I can further explain upon.
Some code:
private int BasicLand(Card basicLand)
{
var countBox = new TextBox
{
Name = "count",
Width = 100,
};
var cmbCount = new CustomMessageBox
{
Caption = "Blah",
Content = countBox,
RightButtonContent = "ok",
};
cmbCount.Dismissed += (s1, e1) =>
{
switch (e1.Result)
{
case CustomMessageBoxResult.RightButton:
if (int.TryParse(countBox.Text, out tempInt) && Convert.ToInt32(countBox.Text) > 0)
{
countReturn = Convert.ToInt32(tempInt);
break;
}
else
{
//Some code for error....
}
}
};
cmbCount.Show();
return countReturn;
}
Then the other part that triggers first but is last in the code block.
MessageBox.Show("Object was added to List!");
I tried adding the ShowDialog to the custom box but it came up broken in VS. BasicLand is called within another method and when the object is added to the list it will display the MessageBox.Show.
The problem with your code is, it does not take into account that any user interaction is asynchronous. When you call Show() it will actually show the messagebox, but it will not block your currently running thread, the other statements after the call to Show() will be executed immediately and thus your method returns a returnvalue that has not been provided by the user but is just the default. To fix this you have to write your code in continuations.
private void PromtUserForFeeblefezerAmount(Action<int> continueFeeblefzing, Action cancel)
{
var messagebox = CreateFeeblefezerPromt();
messagebox.Dismissed += (sender, args) =>
{
if ( args.Result == CustomMessageBoxResult.RightButton )
continueFeeblefzing( GetFeeblefezerAmount(messagebox) );
else
cancel();
};
messagebox.Show();
}
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());
}
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).