In the documentation of the Siemens TIA-Portal Openness API you can read the following:
There is an event, when a confirmation box opens, and an event when the confirmation is given by the user.
//Register event handler for Notification-Event
....
tiaPortal.Notification += TiaPortal_Notification;
....
private static void TiaPortal_Notification(object sender, NotificationEventArgs e)
{
....
}
//Register event handler for Confirmation-Event
....
tiaPortal.Confirmation += TiaPortal_Confirmation;
....
private static void TiaPortal_Confirmation(object sender, ConfirmationEventArgs e)
{
....
}
The documentation gives this much information on reacting to the events
I want to react on the notification event. But the NotificationEventArgs Class does not contain an result attribute which i can write on, and does not contain any method of some kind to send a confirmation. There is only one writeable attribute, called IsHandled. But nothing happens if i write to that, so i suggest this is only an internal confirmation
My understanding of the api documentation ist, that it is something native of c#/.net? maybe a function of some kind, to raise reactions on events?
This answer is valid for TIA 16 with Openness API V16.
Confirmations: If a prompt is triggered in TIA Portal that requires a user decision (i.e. has more than one button), ConfirmationEvent is raised, and it definitely happens prior to any decision being made by the user.
Set IsHandled, then set the Result value. That's all you need to do.
private static void Tiap_Confirmation(object sender, ConfirmationEventArgs e)
{
//signal to TIA Portal that the event is handled
e.IsHandled = true;
//handle the various events
if (CONDITIONS_FOR_CHOOSING_YES)
e.Result = ConfirmationResult.Yes;
if (CONDITIONS_FOR_CHOOSING_CANCEL)
e.Result = ConfirmationResult.Cancel;
...
}
Notifications: NotificationEvent is only raised for prompts that have one button. The example below is for a .NET Framework console application.
using Siemens.Engineering;
using System;
using System.Linq;
using System.Threading;
namespace OpennessConsoleTests
{
class Program
{
static void Main(string[] args)
{
//attach to the first tia portal process found
TiaPortal tiap = TiaPortal.GetProcesses().First().Attach();
//subscribe to confirmation event
tiap.Notification += Tiap_Notification;
//pause
Console.WriteLine("Waiting for events. Press Ctrl+C to quit");
//sleep indefinitely. User must ctrl+C out of this
Thread.Sleep(Timeout.Infinite);
}
private static void Tiap_Notification(object sender, NotificationEventArgs e)
{
e.IsHandled = true;
Console.WriteLine("*****Notification*****");
Console.WriteLine("Caption: " + e.Caption);
Console.WriteLine("Text: " + e.Text);
Console.WriteLine("DetailText: " + e.DetailText);
}
}
}
An important note: According to my testing, some prompts do not raise an event even if subscribed, for example "Subfolder already exists" when extracting an archive. This puts TIA Portal into a locked state until you stop your application.
I think it's just a (not optimal) translation into German. What they mean is you need to set a field in the event args you got. This is a normal pattern in .NET.
Something like this (don't have the library for syntax check but you should get what I mean):
private static void TiaPortal_Confirmation(object sender, ConfirmationEventArgs e)
{
// do your thing, open a confirmation dialog or something, then:
e.Result = Choices.OK;
}
Related
I want to create a program that subscribes to the Windows Application event log and raises an event when an event log entry is written for a specific source.
I have been following this Microsoft code snippet which has been very helpful, however, I have a couple of questions about it:
I'm having trouble understanding the purpose of the static AutoResetEvent object in this example. In my testing, the event handler fires properly without it, however, I'd like to know if there's an underlying issue I'm not seeing by omitting it.
Is there any way to only raise an event when an entry is written to a specific source. I know I can just look at the source in the event handler, but it seems wasteful to me to enter the event handler when we don't need to.
Here is my code. I have been testing it by concurrently creating test events with different Source names in a Powershell session. Any suggestions for improvements would be greatly appreciated.
class Program {
static string eventLogSource = "testEventLogEvent";
public static void Main() {
EventLog myNewLog = new EventLog("Application", ".", eventLogSource);
try {
myNewLog.EntryWritten += new EntryWrittenEventHandler(MyOnEntryWritten);
myNewLog.EnableRaisingEvents = true;
Console.ReadKey();
} finally {
myNewLog.Dispose();
}
}
public static void MyOnEntryWritten(object source, EntryWrittenEventArgs e) {
Console.WriteLine("In event handler");
if (e.Entry.Source.Equals(eventLogSource)) {
Console.WriteLine(e.Entry.Message);
}
}
}
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
This may have been asked several times, but I don't know what to search for..
Anyway. I have a class called Character. Inside of it I want to have a collision component that I have called RectangleCollision. Inside of it there is a function called IsOverlapping that checks for overlap.
I want to have a function that can be modified for each game object. For example create a function called OnBeginOverlap(); that will fire everytime the collision component detects a collision.
Is there any way that I can bind this function as delegate or event? Or something?
You have to read about events and delegates. There are plenty of examples on the web. The easiest I managed to find when I was trying to understand the subject was this:
The Simplest C# Events Example Imaginable
You can also check out the below (you can compile this as console application):
class Character
{
public delegate void OverlappingHandler(Character character, EventArgs e);
public event OverlappingHandler OverlappingEvent;
public void IsOverlapping()
{
bool overlapping = true;
if (overlapping)
{
if (OverlappingEvent != null)
{
OverlappingEvent(this, null);
}
}
}
}
class Program
{
static void Main(string[] args)
{
Character c = new Character();
c.OverlappingEvent += OverlappingEventHandler;
c.OverlappingEvent += OverlappingSecondEventHandler;
c.IsOverlapping();
Console.Read();
}
static void OverlappingEventHandler(Character character, EventArgs e)
{
Console.WriteLine("We have overlapping here!!");
}
static void OverlappingSecondEventHandler(Character character, EventArgs e)
{
Console.WriteLine("Seriously, we have overlapping !!");
}
}
So step by step:
Create a delegate, which is a bridge between your event and the code you want to run when event is triggered. You give parameters to a delegate, which are (object sender, EventArgs e) - in this example sender is the Character class, arguments are used to send additional info - for example type of character.
Create event of our delegate type
In our function IsOverlapping() there would be your logic checking if there is overlapping happening. If there is, you fire up event. You should check first if there is anything connected to the event (hence the if (OverlappingEvent != null)) - if some there is something, fire up the event.
In the Main() you create an instance of the class and...
Subscribe your event handlers to it, so the code that should be executed when the event is triggered. I connected two methods, just to show that you can subscribe more than one.
Now when you run c.IsOverlapping() this is what happens:
your logic to check overlapping runs,
if there is overlapping, there will be a check if OverlappingEvent has code subscribed (it does in Main()),
if it does event will be triggered,
code subscribed to the event runs - in this case your code in Main().
You can compile this as console app and it will display 2 lines:
We have overlapping here!!
Seriously, we have overlapping !!
Hope this helps.
I have an API (dll) that collects stock ticks via an event mechanism. Such as below:
...
using MT4API;
public partial class Blue : Form
{
...
public Blue()
{
...
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new System.EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
....
The idea is that on each chart tick I get an event. here is the event handler code:
private static void MT_OnQuote(object sender, QuoteEventArgs args)
{
GlobalClass.Ask = args.Ask;
GlobalClass.Bid = args.Bid;
// I have back ground worker code that updatestables from the global class
}
This all works fine. So long as I do not touch any other buttons on the form UI. As soon as I click a button on the form of the UI... I no longer receive events from my API, the UI application functions normally, but with no data from the API.
Why do events from the UI stop any further events coming from the API event?
Any idea whats going on here? Or suggestions how to design this?
Does the same problem occur if you comment out your code that updates the tables from the global object? and if you comment out the background worker?
It would be a good idea to distinguish if the event stops being fired just after you press some button on the UI, or if it stops being fired only after some line of code you wrote is being executed.
In order to be able to help you, we would need to know how the event on the MT4DDE class is triggered.
If you have the code for this class, posting it would help.
If you don't you may want to use a tool such as Reflector to decompile the assembly into C# and see what the MT4DDE class is doing that might cause it to stop invoking the event.
In addition, if you are doing anything related to background threads, or if you're doing anything unusual with your application's main message loop, it would be a good idea to mention it here.
I have tried to use the invoke command, it works, but after a few events it stops...here is the code isolated:
using MT4API;
namespace WindowsFormsApplication1
{
public delegate void UpdateTextCallback(double ask, double bid);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string symbol = "GBPUSD";
MT4DDE dde = new MT4DDE("");
dde.OnQuote += new EventHandler<QuoteEventArgs>(MT_OnQuote);
dde.Connect();
dde.Subscribe(symbol);
}
private void updateTickDisplay(double ask, double bid)
{
textBox1.Text = ask.ToString();
textBox2.Text = bid.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
private void MT_OnQuote(object sender, QuoteEventArgs args)
{
BeginInvoke(new UpdateTextCallback(this.updateTickDisplay),
new object[] { args.Ask, args.Bid });
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox3.Text = textBox1.Text;
}
}
}
The only difference from the real code is that I am using a data grid....as opposed to a text field. But it is clear that the UI blocks somehow the new events. It is strange that I get about 5 to 10 events and then it just stops. Strange. Any ideas on a differnet design?
I need to Enable/Disable my SAVE button in real time based on data in my fields.
Is the below an acceptable way on accomplishing this? It feels wrong but I don't know how else I could accomplish this.
Each User Control(CRUD Form) has a BackgroundWorker and the following related methods;
StartBGWorker()
StopBGWorker()
RequiredFieldsValid()
There purpose is self-explanatory, I hope. The process goes in such a way that when a User clicks NEW or EDIT it places a call to StartBGWorker() which creates a new BackgroundWorker and calls RunDataASync() for it. The DoWork() method of the BGWorker looks like this:
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
ucNavDiagnosis.btnSave.Enabled = Convert.ToBoolean(e.UserState);
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
bgWorker.ReportProgress(0, RequiredFieldsValid());
System.Threading.Thread.Sleep(500);
}
}
private bool RequiredFieldsValid()
{
// TODO: Add other required fields
return (!memAllergies.Text.Equals(string.Empty));
}
This works but feels "bush-league." Anyway, when the User clicks SAVE or CANCEL a call to StopBGWorker() is placed which Disposes the worker.
As a further disclaimer, this app does not currently use binding. It's a long explanation but that's that, as they say.
How about using the validating events to update the enabled status of your save button?!!
protected virtual void MyTextBox_OnValidating(CancelEventArgs e)
{
this.SaveButton.Enabled = (Validate(MyTextBox));
}
As well, if you are doing validation, I highly recommend looking into MS enterprise library validation to handle informing the user of input errors. Also, check out this.