There is a class library project that I use in many asp.net web application solutions.
In this project i have a class that i use for database operations.
Very simply it looks like as follows
(all codes are pseudo)
CoreDb.cs
{
public class cCoreDb()
{
string TableName;
Hashtable htFieldAndValue;
Hashtable htConditionFieldAndValue;
public int Insert()
{
...
}
public int Update()
{
...
}
public int Delete()
{
...
}
}
}
I use this class in solution's main project like as follows
WebForm1.cs
{
btnInsert_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
btnUpdate_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.htConditionFieldAndValue.Add("ID", "3");
CoreDb.Update();
}
btnDelete_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htConditionFieldAndValue.Add("ID", "3");
CoreDb.Delete();
}
}
WebForm2.cs
{
btnInsert1_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table1";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
btnInsert2_Click()
{
cCoreDb CoreDb = new cCoreDb();
CoreDb.TableName = "Table2";
CoreDb.htFieldAndValue.Add("Field1", "Value1");
CoreDb.htFieldAndValue.Add("Field2", "Value2");
CoreDb.Insert();
}
}
WebForm3.cs
...
In all solutions which i use this class I want to catch these class methods calls (like a db trigger) in a central place (like global.asax or master page OR listen messages like WndProc)
Global.asax.cs OR Site.Master.cs OR another place in project
{
public class cCoreDbTrigger(cCoreDb CodeDb)
{
bool BeforeInsert()
{
if (CoreDb.TableName = "PRODUCT")
{
if (CoreDb.htFieldAndValue["CODE"] == already exists)
throw exception "product code already exists";
}
else if (CoreDb.TableName = "STOCK")
{
if (CoreDb.htFieldAndValue["NEW_STOCK"] < CriticalStock)
throw exception "invalid stock value";
}
else if (CoreDb.TableName = "XXX")
{
insert log_table;
}
...
}
}
bool AfterInsert()
{
...
}
bool BeforeUpdate()
{
...
}
bool AfterUpdate()
{
...
}
bool BeforeDelete()
{
...
}
bool AfterDelete()
{
...
}
}
How can I invoke before and after methods from coredb class for this like as follows
CoreDb.cs
{
public class cCoreDb()
{
public int Insert()
{
invoke cCoreDbTrigger.BeforeInsert(); //this message will be processed by the main project
do something;
invoke cCoreDbTrigger.AfterInsert(); //this message will be processed by the main project
}
}
}
notes:
i know that there may be better solutions for these scenarios. (like DB trigger, using entity objects, etc)
But i am trying to find a solution for the structure of my own application.
You could for example use events:
public class cCoreDb()
{
public event EventHandler BeforeInsert;
public event EventHandler AfterInsert;
public int Insert(Hashtable htFieldAndValue)
{
BeforeInsert?.Invoke();
do something;
AfterInsert?.Invoke();
}
}
...
public class cCoreDbTrigger(cCoreDb codeDb){
pubic cCoreDbTrigger(){
{
// attach event handlers
coreDb.BeforeInsert += BeforeInsert;
coreDb.EfterInsert += EfterInsert;
}
void BeforeInsert(object? sender, EventArgs e){
...
}
void EfterInsert(object? sender, EventArgs e){
...
}
}
Events allow for some other class to be informed when the event is triggered. You can also add parameters to the event if you so want.
Note that whenever you are using event handlers you should also keep in mind when they should be removed. A common cause of memory leaks is if you always add but never remove event handlers.
Related
I am creating a car simulator where I have a key that turns on an engine. The engine is tied to a specific key with a callback method which calls the OnEngineTurn method which raises the event. No matter what I do to the EventHandler, I it never works because it always is null. Here is the code below. I am relatively new to C# so any help is appreciated
public delegate void MyEventHandler(object sender, EventArgs e);
class Engine
{
public event MyEventHandler EngineTurn;
//raise the event
protected virtual void OnEngineTurn(EngineEventArgs e)
{
MyEventHandler engineTurn = EngineTurn;
if (engineTurn != null)
{
MessageBox.Show("Hello World");
engineTurn(this, e);
}
else
{
MessageBox.Show("Null");
}
}
public CarKey GetNewKey()
{
return new CarKey(new KeyCallBack(OnEngineTurn));
}
}
class EngineEventArgs : EventArgs
{
public string name { get; set; }
}
delegate void KeyCallBack(EngineEventArgs e);
class CarKey
{
//we need a way to hook the engine up to the car so we don't crank, but one car with one key
private KeyCallBack keyCallBack;
public CarKey(KeyCallBack callBackDelegate)
{
this.keyCallBack = new KeyCallBack(callBackDelegate);
}
public void TurnTheKey(EngineEventArgs e)
{
if (keyCallBack != null)
{
MessageBox.Show("A");
keyCallBack(e);
}
}
}
carKey = engine1.GetNewKey() should tie a specific key to a specific engine with a callback method that calls back to the EngineTurn Event.... carKey.TurnTheKey(engineEventArgs) is suppose to raise the event.... Below is the constructor for CarKey... I have it inside the Engine class for the callback method...
carKey = engine1.GetNewKey();
engineEventArgs = new EngineEventArgs();
carKey.TurnTheKey(engineEventArgs);
public CarKey GetNewKey()
{
return new CarKey(new KeyCallBack(OnEngineTurn));
}
Solved the problem
class Simulator
{
private Engine engine = new Engine();
private Transmission transmission;
CarKey carKey;
//public ObservableCollection<string> FanSays { get { return fan.FanSays; } }
//public ObservableCollection<string> PitcherSays { get { return pitcher.PitcherSays; } }
// public int Trajectory { get; set; }
//public int Distance { get; set; }
public Simulator()
{
transmission = new Transmission(engine);
carKey = engine.GetNewKey();
}
public async void StartSimulator()
{
EngineEventArgs engineEventArgs = new EngineEventArgs("America!");
await new MessageDialog("made it inside the start method").ShowAsync();
carKey.StartTheEngine(engineEventArgs);
}
}
I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}
I'm making QuestSystem in unity.
what i want to do is assigning to my questData an event so it can know when the quest objective has been completed.
Lets say there is a class named A and action called a.
and i want Class B, Action b want to have reference to A.a
So if i do
b = A.a;,
b+= someAction;, it actually does a+=someAction;
but when if i do that. It will just simply b+=someAction and A.a will remain null
what should i do to perform what i want?
here are some tags. (i don't know what the answer would be. so..)
# event
# subscribing event
# assigning event
# referencing event
# action
# delegate
====== Edited =======
here is my code.
QuestData.cs
public class QuestData
{
public string questName;
public string qusetID;
public string questDescription;
public SceneType questSceneType;
private string isActivePrefsKey { get { return $"QuestKey{qusetID}"; } }
public bool isActive {
get {
return Convert.ToBoolean (PlayerPrefs.GetInt (isActivePrefsKey));
}
set { PlayerPrefs.SetInt (isActivePrefsKey, Convert.ToInt16 (value)); }
}
public QuestObjective questObjective;
public QuestReward questReward;
public void Activate ()
{
if (AppController.CurrentScene == questSceneType) {
questObjective.ActivateObjective ();
}
}
}
QuestObjective.cs
public class QuestObjective
{
// TODO rename all
public int goalObjectiveCount;
public int currentObjectiveCount;
public Action questAction;
public void OnConditionMatch ()
{
Debug.Log ("OnConditionMatch");
currentObjectiveCount += 1;
}
public void ActivateObjective ()
{
questAction += OnConditionMatch;
}
}
QuestManager.cs
public class QuestManager : MonoBehaviour
{
List<QuestData> questDatas;
void Awake ()
{
PrepareQuestDatas ();
ActivateActiveQuests ();
}
void ActivateActiveQuests ()
{
var activeQuests = GetActiveQuests ();
foreach (var activeQuest in activeQuests) {
activeQuest.Activate ();
}
}
List<QuestData> GetActiveQuests ()
{
// for debuging
return questDatas;
// real code
return questDatas.Where (q => q.isActive == true).ToList ();
}
public void PrepareQuestDatas ()
{
questDatas = new List<QuestData> {
new QuestData {
questName = "Foot Print",
questDescription = "win the game for first time",
questSceneType = SceneType.Main,
questObjective = new QuestObjective {
goalObjectiveCount = 1,
questAction = GamePlayController.instance.endGameCon.onWinGame
},
questReward = new QuestCoinReward{
rewardAmount = 100,
},
}
};
}
}
One potential solution is to create a new set of EventArgs, like this:
public class QuestCompletedEventArgs : System.EventArgs
{
public QuestObjective FinishedObjective { get; }
public QuestCompletedEventArgs(QuestObjective objectiveIn) {
this.FinishedObjective = objectiveIn;
}
}
(probably in a different file)
... and use it like this:
First, create an event delegate:
public delegate void QuestObjectiveCompleteHandler(object sender, QuestCompletedEventArgs e);
Instantiate the event delegate:
public event QuestObjectiveCompletedHandler CompletedObjective;
Define the method that will do something when the objective is completed:
public void ObjectiveCompleted(object sender, QuestCompletedEventArgs e)
{
// do something
}
Assign that method to the event:
this.CompletedObjective += this.ObjectiveCompleted;
From here, you can make the FinishedObjective object within the QuestCompletedEventArgs a List<QuestObjective>, and FinishedObjective.add(objectiveIn) whenever appropriate.
You should also be able to make the event handling method act differently when a certain amount of objectives have been completed, or whatever you want to do with that information.
Of course, you can also add multiple different methods to respond to this event by adding more this.CompletedObjective += this.methodName; lines, as long as the signature of the new method(s) carry that same signature.
Reading into your example, I have written up some code where "A" is QuestObjective and "B" is Quest. The Quest object needs to know when objective has been marked as completed.
Using event handlers, we can set it up so that B is notified when an action occurs on A.
Like this:
// B
public class Quest
{
public Quest()
{
Objectives = new List<QuestObjective>();
// load objectives... Fake
Objectives.Add(new QuestObjective("obj 1"));
Objectives.Add(new QuestObjective("obj 2"));
Objectives.Add(new QuestObjective("obj 3"));
foreach(var o in Objectives) // subscribe to QuestObjective events
{
o.ObjectiveCompleted += (sender, args) => ReportObjectiveCompleted();
}
}
public void ReportObjectiveCompleted()
{
// let 'em know
}
public List<QuestObjective> Objectives { get; set; }
}
// A
public class QuestObjective
{
public string Name { get; set; }
public QuestObjective(string name = "unknown")
{
Name = name;
}
public event EventHandler ObjectiveCompleted;
public void MarkCompleted()
{
// when a task is marked as complete and IF there are
// subscribers to this event then call the event handler
var a = ObjectiveCompleted;
if (a != null)
{
a(this, new EventArgs()); // use different event args to pass data
}
}
}
I was given a generic API class, that contains a custom event which always needs to be invoked by the main UI thread.
My job is to banish these invocation call from the custom class, to make it "painless".
It should be synchronized like the default events in WinForms (eg the Timer "Elapsed" event, which also needs no invocation when it published values to a text box)
Is it possible to solve this, since the custom class needs to know where to invoke?
Here's the (important part of the) code:
public class ContactSensorHelper
{
public event OnReleaseStateChanged ReleaseStateChanged;
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (ReleaseStateChanged != null)
ReleaseStateChanged(new ContactSensorEventArgs()
{
State = recentReleaseState
});
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
The call from main UI:
public void SensorInit()
{
//....
sensorHelper.ReleaseStateChanged += releaseStateChanged;
//....
}
private void releaseStateChanged(ContactSensorEventArgs e)
{
//example
textBox1.Text = e.State.ToString(); // Thread exception (obviously)
}
Does anybody have me a hint to start?
You could do this by using your own event calling, and storing a reference to the thread, when the event is attached.
With the event add/remove syntax, you can have the caller attach to the event like before, but internally you store a list, with a reference to the thread (using an AsyncOperation) and the delegate to be called (used a Tuple containing both in the example)
Below is an example. I tested it, and it worked as expected when testing, but you might have to add some locking of the list to make it thread safe in case events are added/removed simultaneously.
public class ContactSensorHelper:IDisposable
{
public delegate void OnReleaseStateChanged(ContactSensorEventArgs e);
private ContactSensorEventArgs.ReleaseState recentReleaseState;
public void ReportStateChanged()
{
if (statechangedList.Count > 0)
{
var e = new ContactSensorEventArgs()
{
State = recentReleaseState
};
statechangedList.ForEach(t =>
t.Item1.Post(o => t.Item2((ContactSensorEventArgs)o), e));
}
}
List<Tuple<AsyncOperation, OnReleaseStateChanged>> statechangedList = new List<Tuple<AsyncOperation,OnReleaseStateChanged>>();
public event OnReleaseStateChanged ReleaseStateChanged
{
add
{
var op = AsyncOperationManager.CreateOperation(null);
statechangedList.Add(Tuple.Create(op, value));
}
remove
{
var toremove = statechangedList.Where(t => t.Item2 == value).ToArray();
foreach (var t in toremove)
{
t.Item1.OperationCompleted();
statechangedList.Remove(t);
}
}
}
public void Dispose()
{
statechangedList.ForEach(t => t.Item1.OperationCompleted());
statechangedList.Clear();
}
public class ContactSensorEventArgs : EventArgs
{
//......
public ReleaseState State { get; set; }
//......
public enum ReleaseState
{
FullReleased,
PartlyReleased,
NotReleased
}
}
}
So im trying to make a data-grid that displays some information about local window services, mine in particular, I would like to have the display name and status of the service, and then have a button to click to start or stop. I can link the button method up fine, but the service status does not change, any suggestions an how to make this property observable to the datagrid, and also possible change the button on the fly from start to stop based on the status, secondly I would like to make the stop command be be a button command if possibe.
Any suggestions?
You need to wrap your the service into your own class that implements INotifyPropertyChanged. As you start/stop the service, raise property change event on that instance.
This is what I ended up implamenting. It works pretty well for the most part, however I up for any code suggestions anyone might.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.ServiceProcess;
namespace v7quickbar
{
class NotifiableServiceController : INotifyPropertyChanged
{
private ServiceController m_oServiceController = null;
private System.Timers.Timer m_oServiceCheckTimer = new System.Timers.Timer();
public ServiceControllerStatus Status { get { return this.m_oServiceController.Status; } }
public string DisplayName { get { return this.m_oServiceController.DisplayName; } }
public string ServiceName { get { return this.m_oServiceController.ServiceName; } }
public bool CanStop { get { return this.m_oServiceController.CanStop; } }
public NotifiableServiceController(ServiceController oService)
{
CreateObject(oService, TimeSpan.FromSeconds(.5));
}
public NotifiableServiceController(ServiceController oService, TimeSpan oInterval)
{
CreateObject(oService, oInterval);
}
private void CreateObject(ServiceController oService, TimeSpan oInterval)
{
m_oServiceController = oService;
m_oServiceCheckTimer.Interval = oInterval.TotalMilliseconds;
m_oServiceCheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_oServiceCheckTimer_Elapsed);
m_oServiceCheckTimer.Start();
}
public void Start()
{
try
{
this.m_oServiceController.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
catch (Exception)
{
}
}
public void Stop()
{
try
{
this.m_oServiceController.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
catch (Exception)
{
}
}
public void Restart()
{
try
{
if (m_oServiceController.CanStop && (m_oServiceController.Status == ServiceControllerStatus.Running || m_oServiceController.Status == ServiceControllerStatus.Paused))
{
this.Stop();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Stopped);
}
if (m_oServiceController.Status == ServiceControllerStatus.Stopped)
{
this.Start();
this.m_oServiceController.WaitForStatus(ServiceControllerStatus.Running);
}
}
catch (Exception)
{
}
}
void m_oServiceCheckTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
ServiceControllerStatus oCurrentStatus = m_oServiceController.Status;
m_oServiceController.Refresh();
if (oCurrentStatus != m_oServiceController.Status)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Status"));
}
}
public static IEnumerable<NotifiableServiceController> GetServices()
{
List<NotifiableServiceController> oaServices = new List<NotifiableServiceController>();
foreach (ServiceController sc in ServiceController.GetServices())
{
oaServices.Add(new NotifiableServiceController(sc));
}
return oaServices;
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Sadly because ServiceController.GetServices() call would always return an array, we have to have DispatcherTimer and in its tick, make call to ServiceController.GetServices() and raise notify property changed for that property that holds the array of services.
Making it observable for the sake of observability isnt practical right? We wont gain any advantage out of it anyways.