Delegate SystemAction as constructor parameter - c#

I have a winform I'm working on that needs change a label when it hits a specific phase and the same label when it's complete. I have an interface setup and a class that implements this interface. In the class constructor, I'm trying to add the Action<> as a parameter but I am having trouble processing what I need to do in order to get it to flow correctly.
Never could get it to work :/

While your example i cannot give specific example because it confuses me. I can still answer some questions.
Does this help?
private Action _methodOnProgress;
public ChipLoadFirmware(Action method)
{
_methodOnProgress = method;
}
public bool LoadFirmWare()
{
(p) = >
{
_methodOnProgress();
}
}

Maybe an event pattern here will be more reliable.
First add a
public event EventHandler Progress
to your ChipLoadFirmware class, and make it public in the manager class (lets assume it's called Manager and the ChiLoad instance is called Loader)
So, when you instance the manager in the form you add
Manager.Loader.Progress += (o, e) => { /* Change the label */ };
And finally in the (p) => { } do
Progress(this, EventArgs.Empty);
EDIT Here is your code with the changes, supose the class Form is your form and labelToChange the label which must be changed;
/// <summary>
/// Represents an object that can load the firmware
/// </summary>
public interface ILoadFirmware
{
/// <summary>
/// Loads firmware on a device
/// </summary>
bool LoadFirmware();
event EventHandler Progress;
event EventHandler Status;
}
public class ChipLoadFirmware : ILoadFirmware
{
private readonly log4net.ILog logger = Logging.Log4NetManager.GetLogger();
private readonly ImageLoader imageLoader = new ImageLoader ();
private bool abort = false;
private string cmdText = string.Empty;
private string errortext = string.Empty;
private string isaIp;
public event EventHandler Progress;
public event EventHandler Status;
/// <summary>
/// Sets up an ImageLoader
/// </summary>
/// <param name="isaTargetIpAddress">The IP address of the ISA to load the firmware on</param>
public ChipLoadFirmware (string isaTargetIpAddress)
{
this.isaIp = isaTargetIpAddress;
imageLoader.EventHandler += (s, e) => { };
logger.DebugFormat("Using IP Address {0}", isaTargetIpAddress);
}
/// <summary>
/// Loads the firmware onto the device
/// </summary>
/// <returns>Returns true if successful</returns>
public bool LoadFirmware()
{
bool result = imageLoader.Run(this.isaIp,
false,
Command.Command,
string.Empty,
CommandFlag.CommandFlag,
2000,
(p) => { Progress(this, EventArgs.Empty); },
(s) => { Status(this, EventArgs.Empty); },
ref this.abort,
out this.cmdText,
out this.errortext);
if (!result)
{
result = false;
throw new InvalidOperationException(
string.Format(
"ImageLoader failed with message: {0}",
errortext));
}
return result;
}
}
public class CalibrationManager : ICalibrationManager
{
const uint startAddress = 0x00000000;
const uint startAddress = 0x00000000;
private readonly log4net.ILog logger;
private readonly ISignalGenerator sigGenerator;
public readonly ILoadFirmware loadFirmware;
private readonly IReader reader;
private readonly IValueParser valueParser;
private readonly IRepository<CalibrationValue> calibrationRepository;
private readonly IRepository<UIDList> uidRepository;
private string uid;
public CalibrationManager(ISignalGenerator sigGen, ILoadFirmware loadFirmware, IReader chipRreader, IValueParser chipValueParser, IRepository<UIDList> uidRepo, IRepository<CalibrationValue> calRepo)
{
if (sigGen == null ||
loadFirmware == null ||
chipReader == null ||
chipValueParser == null ||
calRepo == null ||
uidRepo == null) throw new ArgumentNullException();
logger = Logging.Log4NetManager.GetLogger();
this.sigGenerator = sigGen;
this.loadFirmware = loadFirmware;
this.reader = chipReader;
this.valueParser = chipValueParser;
this.uidRepository = uidRepo;
this.calibrationRepository = calRepo;
}
public void Calibrate(bool reuse)
{
int voltageG;
int currentG;
// Read uid from device
this.uid = uidReader.ReadUid();
logger.DebugFormat("Read UID {0}", this.uid);
var possibleCalibrations = getCalibrationDataFromRepo();
if (possibleCalibrations.Any() && reuse)
{
logger.Debug("Reusing existing values");
var existingCalibration = possibleCalibrations.OrderByDescending(c => c.DateCalibrated).First();
currentG = existingCalibration.CurrentGain;
voltageG = existingCalibration.VoltageGain;
}
else
{
logger.Debug("Reading new values");
// Set voltage and current for signal generator
sigGenerator.SetOutput(187, 60);
// Load firmware onto chip for CLI commands
loadFirmware.LoadFirmware();
UpdateStateChange(StateOfDevice.LoadFirmware);
// Read voltage from device and calibrate
voltageG = valueParser.ReadVoltageValue();
UpdateStateChange(StateOfDevice.CalibrateVoltage);
// Read current from device and calibrate
sigGenerator.SetOutput(38, 60);
currentG = valueParser.ReadCurrentValue();
UpdateStateChange(StateOfDevice.CalibrateCurrent);
// Insert values into table
CalibrationValue cv = new CalibrationValue();
cv.UidId = uidRepository.GetBy(e => e.UID.Equals(this.uid)).Id;
cv.CurrentGain = currentG;
cv.VoltageGain = voltageG;
calibrationRepository.Insert(cv);
calibrationRepository.Submit();
UpdateStateChange(StateOfDevice.DatabaseInsert);
}
logger.DebugFormat("Updated database for current gain to {0} and voltage gain to {1}", currentG, voltageG);
// Once here, device has passed all phases and device is calibrated
UpdateStateChange(StateOfDevice.CalibrationComplete);
}
private IQueryable<CalibrationValue> getCalibrationDataFromRepo()
{
int uidId = uidRepository.GetBy(e => e.UID.Equals(this.uid)).Id;
return calibrationRepository.SearchFor(c => c.UidId == uidId);
}
public bool CalibrationDataExists()
{
this.uid = uidReader.ReadUid();
logger.DebugFormat("Read UID {0}", this.uid);
return getCalibrationDataFromRepo().Any();
}
public event Action<StateOfDevice, string> StateChange;
private void UpdateStateChange(StateOfDevice state, string message = "")
{
var sc = StateChange;
if (sc != null)
{
StateChange(state, message);
}
}
}
public class Form
{
Label labelToModify;
void MagickButtonClick(object Source, EventArgs e)
{
CalibrationManager manager = new CalibrationManager(.......);
manager.loadFirmware.Progress += (o, e) => { labelToModify.Text = "Some progress achieved!!"; };
}
}

Related

How to implement Interface in Mainwindow

I'm rewriting my project, so it's easier to edit in future, and want to implement interface.
I've implemented the interface, but it doesn't work in MainWindow, I'm not able to call the method.
So I've tried to use the PalindromeChecker as the default implementation
PalindromeChecker = new PalindromeChecker(); , so I can call the method but it didn't work.
interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
......
//Code
}
}
}
namespace TextChecker
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
}
/// <summary>
/// User input and checking the input if the word a palindrome is.
/// </summary>
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = PalindromeChecker.IsPalindrome(text);
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
private void ButtonClicked(object sender, RoutedEventArgs e)
{
SubWindow subWindow = new SubWindow();
subWindow.Show();
}
}
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
I'm really stuck here, I would like to thank you in advance.
As far as can see, you don't want interfaces, class instances to check for a palindrome (do you really want to implement several algorithms to choose from?), but a static method:
// Let's class be partial one: if you want to add a method it it
// you don't have to modify this code but
// add a chunk public partial static class MyStringRoutine {...}
public partial static class MyStringRoutine {
public static bool IsPalindrome(string text) {
//DONE: do not forget about special cases
if (string.IsNullOrEmpty(text))
return true;
for (int i = 0; i < text.Length / 2; ++i)
if (text[i] != text[text.Length - 1 - i])
return false;
return true;
}
}
Then you can use it:
private void InputText_TextChanged(object sender, TextChangedEventArgs e) {
if (string.IsNullOrEmpty(InputText.Text))
OutputText.Clear();
else {
string suffix = MyStringRoutine.IsPalindrome(InputText.Text)
? "is a palindrome"
: "is NOT a palindrome";
OutputText.Text = $"{InputText.Text} {suffix}";
}
}
If you have to implement ICheckPalindrome interface, and thus to work with class instance, you have to create the instance:
private void InputText_TextChanged(object sender, TextChangedEventArgs e) {
if (string.IsNullOrEmpty(InputText.Text))
OutputText.Clear();
else {
// You have to create the instance (checker)
ICheckPalindrome checker = new PalindromeChecker();
// IsPalindrome is the instance method; you can't call is as
// PalindromeChecker.IsPalindrome
string suffix = checker.IsPalindrome(InputText.Text)
? "is a palindrome"
: "is NOT a palindrome";
OutputText.Text = $"{InputText.Text} {suffix}";
}
}
From code you've posted it'd seem that interface ICheckPalindrome is less accessible than PalindromeChecker. Not sure how and why your compiler would let that slip, but you should try changing your code to
public interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
So the interface will reflect its implementation (or rather other way around).

How to implement interface

I want to change my code, also I want to use Interfaces so if someone wants a future so it could be easily implemented, without changing too much code.
So I successfully implemented my interface IPalindromeChecker. But the problem is in MainWindow now. So I'm not sure but I would make another Interface and with public void Output(string text) method. I've tried adding a method in IPalindromeChecker public void Output(string text) but it didn't work.
interface ICheckPalindrome
{
bool IsPalindrome(string text);
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
}
/// <summary>
/// User input and checking the input if the word a palindrome is.
/// </summary>
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = TextChecker.PalindromeChecker(text); // HERE IS THE PROBLEM
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
public class PalindromeChecker : ICheckPalindrome
{
/// <summary>
/// Method for checking if the word/text is a palindrome.
/// </summary>
public bool IsPalindrome(string text)
{
int min = 0;
int max = text.Length - 1;
while (true)
{
if (min > max)
{
return true;
}
char a = text[min];
char b = text[max];
if (a != b)
{
return false;
}
min++;
max--;
}
}
}
It's unclear what TextChecker.PalindromeChecker is but if you want to be able to switch implementations of the ICheckPalindrome interface without having to modify your MainWindow, you should inject the window with an implementation of ICheckPalindrome at runtime and write your code against the interface.
You could for example use a property to do this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lblInput.Foreground = Brushes.ForestGreen;
lblResult.Foreground = Brushes.ForestGreen;
lblTitel.Foreground = Brushes.ForestGreen;
//use the PalindromeChecker as the default implementation
PalindromeChecker = new PalindromeChecker();
}
public ICheckPalindrome PalindromeChecker { get; set; } //<--
private void InputText_TextChanged(object sender, TextChangedEventArgs e)
{
string text = InputText.Text;
bool isPalindrome = PalindromeChecker.IsPalindrome(text);
OutputText.Text = text + (isPalindrome ? " is a palindrome" : " is NOT a palindrome");
if (InputText.Text == string.Empty)
{
OutputText.Clear();
}
}
}
Switching to another implementation is then as simple as just setting the PalindromeChecker property of the MainWindow to an instance of another class that implements the same ICheckPalindrome interface.

Can I detect document saved (not changed) with Visual Studio Workspace in a VSIX?

In my extension I use to tap into the document saved event using DTE:
this._events2 = (Events2)this._dte.Events;
//setup document events
this._documentEvents = this._events2.DocumentEvents;
this._documentEvents.DocumentSaved += _documentEvents_DocumentSaved;
I am migrating my extension to VS 2017 and want to start using the Roslyn stuff instead of DTE. I figured out how to get the Visual Studio Workspace and tap into the workspace changed event. Now I have access to all these document events
/// <summary>
/// A document was added to the current solution.
/// </summary>
DocumentAdded = 9,
/// <summary>
/// A document was removed from the current solution.
/// </summary>
DocumentRemoved = 10,
/// <summary>
/// A document in the current solution was reloaded.
/// </summary>
DocumentReloaded = 11,
/// <summary>
/// A document in the current solution was changed.
/// </summary>
DocumentChanged = 12,
But there is no DocumentSaved. DocumentChange fires every time a keystroke is made and DocumentReloaded never seems to fire at all. Is it possible to detect just a document saved using the roslyn workspace events?
To detect document save event( OnBeforeSave() or OnAfterSave() ) you can implement IVsRunningDocTableEvents3 interface. You can do that by implementing this interface into a helper class and exposing a public event event OnBeforeSaveHandler BeforeSave and a public delegate delegate void OnBeforeSaveHandler(object sender, Document document).
To catch this event just : runningDocTableEvents.BeforeSave += OnBeforeSave and then you can write your code in the OnBeforeSave method.
My implementation of the IVsRunningDocTableEvents3 interface look like this:
public class RunningDocTableEvents : IVsRunningDocTableEvents3
{
#region Members
private RunningDocumentTable mRunningDocumentTable;
private DTE mDte;
public delegate void OnBeforeSaveHandler(object sender, Document document);
public event OnBeforeSaveHandler BeforeSave;
#endregion
#region Constructor
public RunningDocTableEvents(Package aPackage)
{
mDte = (DTE)Package.GetGlobalService(typeof(DTE));
mRunningDocumentTable = new RunningDocumentTable(aPackage);
mRunningDocumentTable.Advise(this);
}
#endregion
#region IVsRunningDocTableEvents3 implementation
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
{
return VSConstants.S_OK;
}
public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
{
return VSConstants.S_OK;
}
public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
{
return VSConstants.S_OK;
}
public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
return VSConstants.S_OK;
}
public int OnAfterSave(uint docCookie)
{
return VSConstants.S_OK;
}
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
return VSConstants.S_OK;
}
public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
return VSConstants.S_OK;
}
public int OnBeforeSave(uint docCookie)
{
if (null == BeforeSave)
return VSConstants.S_OK;
var document = FindDocumentByCookie(docCookie);
if (null == document)
return VSConstants.S_OK;
BeforeSave(this, FindDocumentByCookie(docCookie));
return VSConstants.S_OK;
}
#endregion
#region Private Methods
private Document FindDocumentByCookie(uint docCookie)
{
var documentInfo = mRunningDocumentTable.GetDocumentInfo(docCookie);
return mDte.Documents.Cast<Document>().FirstOrDefault(doc => doc.FullName == documentInfo.Moniker);
}
#endregion
}
I have used this implementation to format some documents when any kind of save command (CTRL + S, Save All, Compile, Build etc) from VS was triggered.
If you want to get the save event from a certain command like Compile you must add a check in your OnBeforeSave() method add some more code
First you must to keep a strong reference to CommandEvents var vommandEvents = dte.Events.CommandEvents and add a new method to the CommandEvents commandEvents.BeforeExecute += CommandEventsBeforeExecute;.
This will work because the CommandsEvents will always be called before the BeforeSave. This is how things works in Visual Studio, every action represents a command which requires some steps and events (Eg. Compile command contains in its workflow, before of anything, the save document event).
public override void OnBeforeSave(object sender, Document aDocument)
{
if (false == myCompileCommandFlag)
return;
// write your code here
}
public void CommandEventsBeforeExecute(string aGuid, int aId, object aCustomIn, object aCustomOut, ref bool aCancelDefault)
{
string commandName = GetCommandName(aGuid, aId);
if (0 != string.Compare("Build.Compile", commandName))
{
return;
}
myCompileCommandFlag= true;
}
public string GetCommandName(string aGuid, int aId)
{
if (null == aGuid)
return string.Empty;
if (null == mCommand)
return string.Empty;
Command cmd = mCommand.Item(aGuid, aId);
if (null == cmd)
return string.Empty;
return cmd.Name;
}
You have DTE2 interface and there you have Events.DocumentEvents.DocumentSaved
I didn't try it but its look promising.

How to send and receive push notifications for Windows Phone 8.1

I followed Microsofts article about sending and receiving push notifications on Windows Phone 8.0:
https://msdn.microsoft.com/en-us/library/windows/apps/hh202967(v=vs.105).aspx
It works fine, but now we are creating a new Windows Phone 8.1 app and try to rewrite the same 8.0 code, but some classes are not available in WP 8.1.
Please help me how we can implement these for Windows Phone 8.1.
Here is my class which I use for receiving push notifications and handling the ChannelUri. Just call the UpdateChannelUri method. The channelUri will be updated if need and the ChannelUriUpdated event will be fired and the same will be saved to application data settings.
If your app is running and you receive a notification, one of the four methods with notification content will be executed, determined by the notification type.
public sealed class PushService
{
private const string ChannelUriKey = "ChannelUri";
private const string ChannelUriDefault = null;
private PushNotificationChannel _channel;
private string _channelUri;
/// <summary>
/// Initializes a new instance of the <see cref="Services.PushService"/> class.
/// </summary>
public PushService()
{
this._channelUri = LocalSettingsLoad(ApplicationData.Current.LocalSettings, ChannelUriKey, ChannelUriDefault);
}
/// <summary>
/// Gets the push notification channel URI. If no channel URI was yet created
/// then the value will be <c>null</c>.
/// </summary>
public string ChannelUri
{
get { return _channelUri; }
private set
{
if (_channelUri != value)
{
this._channelUri = value;
LocalSettingsStore(ApplicationData.Current.LocalSettings, ChannelUriKey, value);
}
}
}
/// <summary>
/// Requests a new push channel URI.
/// </summary>
public async Task<string> UpdateChannelUri()
{
var retries = 3;
var difference = 10; // In seconds
var currentRetry = 0;
do
{
try
{
_channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
_channel.PushNotificationReceived += OnPushNotificationReceived;
if (!_channel.Uri.Equals(ChannelUri))
{
ChannelUri = _channel.Uri;
// TODO send channel uri to your server to your server
this.RaiseChannelUriUpdated();
return _channel.Uri;
}
}
catch
{
// Could not create a channel
}
await Task.Delay(TimeSpan.FromSeconds(difference));
} while (currentRetry++ < retries);
return null;
}
private void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
switch (args.NotificationType)
{
case PushNotificationType.Badge:
this.OnBadgeNotificationReceived(args.BadgeNotification.Content.GetXml());
break;
case PushNotificationType.Tile:
this.OnTileNotificationReceived(args.TileNotification.Content.GetXml());
break;
case PushNotificationType.Toast:
this.OnToastNotificationReceived(args.ToastNotification.Content.GetXml());
break;
case PushNotificationType.Raw:
this.OnRawNotificationReceived(args.RawNotification.Content);
break;
}
args.Cancel = true;
}
private void OnBadgeNotificationReceived(string notificationContent)
{
// Code when a badge notification is received when app is running
}
private void OnTileNotificationReceived(string notificationContent)
{
// Code when a tile notification is received when app is running
}
private void OnToastNotificationReceived(string notificationContent)
{
// Code when a toast notification is received when app is running
// Show a toast notification programatically
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(notificationContent);
var toastNotification = new ToastNotification(xmlDocument);
//toastNotification.SuppressPopup = true;
ToastNotificationManager.CreateToastNotifier().Show(toastNotification);
}
private void OnRawNotificationReceived(string notificationContent)
{
// Code when a raw notification is received when app is running
}
public event EventHandler<EventArgs> ChannelUriUpdated;
private void RaiseChannelUriUpdated()
{
if (ChannelUriUpdated != null)
{
ChannelUriUpdated(this, new EventArgs());
}
}
public static T LocalSettingsLoad<T>(ApplicationDataContainer settings, string key, T defaultValue)
{
T value;
if (settings.Values.ContainsKey(key))
{
value = (T)settings.Values[key];
}
else
{
// Otherwise use the default value.
value = defaultValue;
}
return value;
}
public static bool LocalSettingsStore(ApplicationDataContainer settings, string key, object value)
{
bool valueChanged = false;
if (settings.Values.ContainsKey(key))
{
// If the key exists
if (settings.Values[key] != value)
{
// If the value has changed, store the new value
settings.Values[key] = value;
valueChanged = true;
}
}
else
{
// Otherwise create the key
settings.Values.Add(key, value);
valueChanged = true;
}
return valueChanged;
}
}

Passing variables to timer event in a class

I have a method in a class that receives and returns multiple parameters from/to Form1.
I need to use a timed event to execute some code using those parameters.
I have arranged this simplified code to show the dynamic:
class Motor
{
public static System.Timers.Timer _timer;
int valReg = 30;
public void PID(decimal _actualSpeed, Decimal _speedRequest, out Decimal _pwmAuto, out decimal _preValReg)
{
_timer = new System.Timers.Timer();
_timer.Interval = (3000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timerAutoset);
_timer.Enabled = true;
// {....}
_pwmAuto = valReg;
_preValReg = valReg - 1;
}
static void _timerAutoset(object sender, System.Timers.ElapsedEventArgs e)
{
/* here I need to work with:
_actualSpeed
_speedRequest
_pwmAuto
_preValReg
and send back the last two variables
*/
}
}
This is how I pass and receive the variables from Form1 button :
private void button4_Click(object sender, EventArgs e)
{
// some code ................
Motor mtr = new Motor();
mtr.PID(speedRequest, actualSpeed, out pwmAuto, out xxx);
//..more code
How can I pass/get back those parameters to/from _timerAutoset event?
I tend to solve this problem using anonymous delegates.
public void PID(decimal _actualSpeed, Decimal _speedRequest, out Decimal _pwmAuto, out decimal _preValReg)
{
_pwmAuto = valReg;
_preValReg = valReg - 1;
// Because we cannot use [out] variables inside the anonymous degegates,
// we make a value copy
Decimal pwmAutoLocal = _pwmAuto;
Decimal preValRegLocal = _preValReg;
_timer = new System.Timers.Timer();
_timer.Interval = (3000);
_timer.Elapsed += (sender, e) => { HandleTimerElapsed(_actualSpeed, _speedRequst, pwmAutoLocal, preValRegLocal); };
_timer.Enabled = true;
// {....}
}
static void HandleTimerElapsed(Decimal actualSpeed, Decimal speedRequst, Decimal pwmAuto, Decimal preValReg)
{
// (...)
}
(You have to be mindful when the delegate accesses local variables from the enclosing block. Double-check the code to ensure the values stored in those variables will not change between the assignment of the event handler and the invocation of this handler).
It seems these parameters are coming from somewhere else. One approach could be to pass a callback via delegate and use it to get the updated values from.
Another approach will be to make a class and pass it to Motor's constructor and use its reference in the _timerAutoset to get the updated values.
Using Delegates:
class Motor
{
public static System.Timers.Timer _timer;
int valReg = 30;
public delegate TimerParam ParameterizedTimerDelegate();
public static ParameterizedTimerDelegate TimerCallback { get; set; }
public void PID()
{
_timer = new System.Timers.Timer();
_timer.Interval = (3000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timerAutoset);
_timer.Enabled = true;
// {....}
//Param.PwmAuto = valReg;
//Param.PreValReg = valReg - 1;
}
static void _timerAutoset(object sender, System.Timers.ElapsedEventArgs e)
{
TimerParam param = TimerCallback();
/* here you can use:
Param.ActualSpeed
Param.SpeedRequest
Param.PwmAuto
Param.PreValReg
*/
}
}
Using a shared instance:
class TimerParam
{
public decimal ActualSpeed { get; set; }
public decimal SpeedRequest { get; set; }
public Decimal PwmAuto { get; set; }
public decimal PreValReg { get; set; }
}
class Motor
{
public static System.Timers.Timer _timer;
int valReg = 30;
public TimerParam Param { get; set; }
public void PID(TimerParam param)
{
Param = param;
_timer = new System.Timers.Timer();
_timer.Interval = (3000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timerAutoset);
_timer.Enabled = true;
// {....}
Param.PwmAuto = valReg;
Param.PreValReg = valReg - 1;
}
static void _timerAutoset(object sender, System.Timers.ElapsedEventArgs e)
{
/* here you can use:
Param.ActualSpeed
Param.SpeedRequest
Param.PwmAuto
Param.PreValReg
*/
}
}
You can then update the instance of TimerParam that you passed to the Motor class and timer will always get the updated values.
you could try using lambda expression for inserting additional arguement..
_timer.Elapsed += (sender, e) => _timerAutoset(sender, e, _actualSpeed,_speedRequest);
your method be like
static void _timerAutoset(object sender, System.Timers.ElapsedEventArgs e,decimal speed,decimal speedRequest)
You just could initialize them in your class, so all methods could access them...
private void StartTimerForDeleteMessage(UC_ChatReceiveMessageControl ucChatReceiveMessageControl)
{
try
{
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, ucChatReceiveMessageControl);
aTimer.Interval = 1000;
aTimer.Enabled = true;
}
catch (Exception ex)
{
Helper.WriteToLogFile("SetMessageBodyContentAfterAcknoledged ex::" + ex.Message, LoggingLevel.Errors);
}
}
static void MyElapsedMethod(object sender, ElapsedEventArgs e, UC_ChatReceiveMessageControl ucChatReceiveMessageControl)
{
try
{
}
catch (Exception ex)
{
Helper.WriteToLogFile("SetMessageBodyContentAfterAcknoledged ex::" + ex.Message, LoggingLevel.Errors);
}
}
I'm using a Backgroundworker styled class called "ScheduledWorker" which executes a recurring operation on a separate thread and returns to the main thread after each execution of this background operation.
For data exchange an object variable can be passed to the background operation when starting the ScheduledWorker and can also be changed while the ScheduledWorker is running. Inside the background procedure this object can be called via DoScheduledWorkEventArgs.Argument. The time when the DoWork event was raised can be called via DoScheduledWorkEventArgs.SignalTime property. The way ScheduledWorker reports result and progress of the background operation to the main thread is the same as the BackgroundWorker class.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
namespace ScheduledWorker
{
/// <summary>
/// Executes a recurring operation on a separate thread.
/// </summary>
[DefaultEvent("DoWork")]
[HostProtection(SharedState = true)]
public partial class ScheduledWorker : Component, ISupportInitialize
{
private bool enabled;
private bool delayedEnable;
private bool initializing;
private bool disposed;
private readonly ManualResetEvent doNotDisposeWaitHandle = new ManualResetEvent(false);
private int disposeWaitMSec;
private bool cancellationPending;
private bool isRunning;
private bool isOccupied;
private bool isWorking;
private object argument;
private readonly object statusChangeLockObject = new object();
private readonly object doWorkKey = new object();
private readonly object runWorkerCompletedKey = new object();
private readonly object progressChangedKey = new object();
private readonly EventHandler<DoScheduledWorkEventArgs> workHandler;
private readonly SendOrPostCallback completedCallback;
private readonly SendOrPostCallback progressCallback;
private AsyncOperation mainThreadOperation;
private Timer timer;
private double interval;
/// <summary>
/// Initializes a new instance of the ScheduledWorker class and sets the <see cref="ScheduledWorker.Interval"/> property to 100 milliseconds.
/// </summary>
public ScheduledWorker() : this(100, -1) { }
/// <summary>
/// Initializes a new instance of the ScheduledWorker class, and sets the <see cref="ScheduledWorker.Interval"/> property to the specified number of milliseconds.
/// </summary>
/// <param name="interval">The time, in milliseconds, between events. The value must be greater than zero and less than or equal to <see cref="int.MaxValue"/>."/></param>
public ScheduledWorker(double interval, int disposeWaitMSec) : base()
{
this.interval = interval;
this.disposeWaitMSec = disposeWaitMSec;
completedCallback = new SendOrPostCallback(AsynOperationCompleted);
progressCallback = new SendOrPostCallback(ProgressReporter);
initializing = false;
delayedEnable = false;
workHandler = new EventHandler<DoScheduledWorkEventArgs>(WorkerThreadStart);
}
/// <summary>
/// Occurs when <see cref="ScheduledWorker.RunWorkerAsync"/> or <see cref="ScheduledWorker.RunWorkerAsync(object)"/> are called.
/// </summary>
public event EventHandler<DoScheduledWorkEventArgs> DoWork
{
add
{
Events.AddHandler(doWorkKey, value);
}
remove
{
Events.RemoveHandler(doWorkKey, value);
}
}
/// <summary>
/// Occurs when the background operation has completed, has been canceled, or has raised an exception.
/// </summary>
public event EventHandler<RunWorkerCompletedEventArgs> RunWorkerCompleted
{
add
{
Events.AddHandler(runWorkerCompletedKey, value);
}
remove
{
Events.RemoveHandler(runWorkerCompletedKey, value);
}
}
/// <summary>
/// Occurs when <see cref="ScheduledWorker.ReportProgress(int)"/> or <see cref="ScheduledWorker.ReportProgress(int, object)"/> are called.
/// </summary>
public event EventHandler<ProgressChangedEventArgs> ProgressChanged
{
add
{
Events.AddHandler(progressChangedKey, value);
}
remove
{
Events.RemoveHandler(progressChangedKey, value);
}
}
/// <summary>
/// Starts raising the <see cref="ScheduledWorker.DoWork"/> event by setting Enabled to true.
/// </summary>
public void RunWorkerAsync()
{
RunWorkerAsync(null);
}
/// <summary>
/// Starts raising the <see cref="ScheduledWorker.DoWork"/> event by setting Enabled to true.
/// </summary>
/// <param name="argument">A parameter for use by the background operation to be executed in the <see cref="ScheduledWorker.DoWork"/> event handler.</param>
public void RunWorkerAsync(object argument)
{
Argument = argument;
Enabled = true;
}
/// <summary>
/// Stops raising the <see cref="ScheduledWorker.DoWork"/> event by setting Enabled to false.
/// </summary>
public void Stop()
{
Enabled = false;
}
/// <summary>
/// Gets or sets a value indicating whether the <see cref="ScheduledWorker.DoWork"/> event should be raised.
/// </summary>
[Category("Behavior")]
public bool Enabled
{
get
{
lock (statusChangeLockObject)
{
return enabled;
}
}
set
{
if (DesignMode)
{
delayedEnable = value;
enabled = value;
}
else if (initializing)
{
delayedEnable = value;
}
else if (enabled != value)
{
lock (statusChangeLockObject)
{
if (!value)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
enabled = false;
if (!isWorking)
{
if (!isOccupied)
{
isRunning = false;
}
SetMainThreadOperationCompleted();
}
}
else
{
enabled = true;
if (timer == null && !isRunning)
{
if (disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
else
{
int roundedInterval = Convert.ToInt32(Math.Ceiling(interval));
isRunning = true;
isOccupied = false;
isWorking = false;
cancellationPending = false;
SetMainThreadOperationCompleted();
mainThreadOperation = AsyncOperationManager.CreateOperation(null);
timer = new Timer(MyTimerCallback, null, roundedInterval, roundedInterval);
}
}
else if (isRunning)
{
throw new InvalidOperationException("ScheduledWorker is busy.");
}
else
{
UpdateTimer();
}
}
}
}
}
}
/// <summary>
/// Gets or sets the interval, expressed in milliseconds, at which to raise the <see cref="ScheduledWorker.DoWork"/> event.
/// It can be changed while the ScheduledWorker is running.
/// </summary>
[Category("Behavior"), DefaultValue(100d), SettingsBindable(true)]
public double Interval
{
get
{
return interval;
}
set
{
if (value <= 0)
{
throw new ArgumentException("Minimum interval is 1.");
}
else
{
interval = value;
if (timer != null)
{
UpdateTimer();
}
}
}
}
/// <summary>
/// Gets or sets a value indicating whether the ScheuledWorker can report progress updates.
/// </summary>
[DefaultValue(false)]
public bool WorkerReportsProgress { get; set; }
/// <summary>
/// Raises the ProgressChanged event.
/// </summary>
/// <param name="percentProgress">The percentage, from 0 to 100, of the background operation that is complete.</param>
public void ReportProgress(int percentProgress)
{
ReportProgress(percentProgress, null);
}
/// <summary>
/// Raises the ProgressChanged event.
/// </summary>
/// <param name="percentProgress">The percentage, from 0 to 100, of the background operation that is complete.</param>
/// <param name="userState">The state object passed to <see cref="ScheduledWorker.RunWorkerAsync(object)"/>.</param>
public void ReportProgress(int percentProgress, object userState)
{
if (!WorkerReportsProgress)
{
throw new InvalidOperationException("This ScheduledWorker does not support reporting progress.");
}
else
{
mainThreadOperation.Post(progressCallback, new ProgressChangedEventArgs(percentProgress, userState));
}
}
/// <summary>
/// Gets or sets a value indicating whether the ScheduledWorker supports asynchronous cancellation.
/// </summary>
[DefaultValue(false)]
public bool WorkerSupportsCancellation { get; set; }
/// <summary>
/// Gets a value indicating whether the application has requested cancellation of a background operation.
/// </summary>
[Browsable(false)]
public bool CancellationPending
{
get
{
lock (statusChangeLockObject)
{
return cancellationPending;
}
}
}
/// <summary>
/// Requests cancellation of a pending background operation.
/// </summary>
public void CancelAsync()
{
if (!WorkerSupportsCancellation)
{
throw new InvalidOperationException("This ScheduledWorker does not support cancellation.");
}
else
{
lock (statusChangeLockObject)
{
cancellationPending = true;
Stop();
}
}
}
/// <summary>
/// Gets a value indicating whether the ScheduledWorker is running an asynchronous operation. This is the case until the SchedeuledWorker has been stopped (<see cref="ScheduledWorker.Enabled"/> = false)
/// and the last <see cref="ScheduledWorker.DoWork"/> event has completed.
/// </summary>
[Browsable(false)]
public bool IsBusy
{
get
{
lock (statusChangeLockObject)
{
return isRunning;
}
}
}
/// <summary>
/// A parameter for use by the background operation to be executed in the <see cref="ScheduledWorker.DoWork"/> event handler.
/// It can be changed while the ScheduledWorker is running.
/// </summary>
[Browsable(false)]
public object Argument
{
get
{
return Interlocked.Exchange(ref argument, argument);
}
set
{
Interlocked.Exchange(ref argument, value);
}
}
/// <summary>
/// Begins the run-time initialization of a ScheduledWorker that is used on a form or by another component.
/// </summary>
public void BeginInit()
{
Close();
initializing = true;
}
/// <summary>
/// Ends the run-time initialization of a ScheduledWorker that is used on a form or by another component.
/// </summary>
public void EndInit()
{
initializing = false;
enabled = delayedEnable;
}
private void MyTimerCallback(object state)
{
lock (statusChangeLockObject)
{
try
{
if (enabled && !isOccupied)
{
doNotDisposeWaitHandle.Reset();
isOccupied = true;
isWorking = true;
FILE_TIME fileTime = new FILE_TIME();
SafeNativeMethods.GetSystemTimeAsFileTime(ref fileTime);
workHandler.BeginInvoke(this,
new DoScheduledWorkEventArgs(Argument,
DateTime.FromFileTime((long)((((ulong)fileTime.ftTimeHigh) << 32) | (((ulong)fileTime.ftTimeLow) & 0xffffffff)))),
null,
null);
}
}
catch { }
}
}
private void WorkerThreadStart(object sender, DoScheduledWorkEventArgs args)
{
Exception Error = null;
try
{
if (CancellationPending)
{
args.Cancel = true;
}
else
{
OnDoWork(args);
}
if (args.Cancel)
{
args.Result = null;
cancellationPending = true;
}
}
catch (Exception ex)
{
Error = ex;
args.Result = null;
}
finally
{
mainThreadOperation.Post(completedCallback, new RunWorkerCompletedEventArgs(args.Result, Error, args.Cancel));
doNotDisposeWaitHandle.Set();
}
}
protected void OnDoWork(DoScheduledWorkEventArgs args)
{
((EventHandler<DoScheduledWorkEventArgs>)Events[doWorkKey])?.Invoke(this, args);
}
private void AsynOperationCompleted(object args)
{
lock (statusChangeLockObject)
{
isWorking = false;
if (!enabled)
{
isRunning = false;
SetMainThreadOperationCompleted();
}
}
OnRunWorkerCompleted((RunWorkerCompletedEventArgs)args);
lock (statusChangeLockObject)
{
isOccupied = false;
if (!enabled)
{
isRunning = false;
SetMainThreadOperationCompleted();
}
}
}
protected void OnRunWorkerCompleted(RunWorkerCompletedEventArgs args)
{
((EventHandler<RunWorkerCompletedEventArgs>)Events[runWorkerCompletedKey])?.Invoke(this, args);
}
private void SetMainThreadOperationCompleted()
{
if (mainThreadOperation != null)
{
mainThreadOperation.OperationCompleted();
mainThreadOperation = null;
}
}
private void ProgressReporter(object arg)
{
OnProgressChanged((ProgressChangedEventArgs)arg);
}
protected void OnProgressChanged(ProgressChangedEventArgs args)
{
((EventHandler<ProgressChangedEventArgs>)Events[progressChangedKey])?.Invoke(this, args);
}
private void UpdateTimer()
{
int roundedInterval = Convert.ToInt32(Math.Ceiling(interval));
timer.Change(roundedInterval, roundedInterval);
}
protected override void Dispose(bool disposing)
{
disposed = true;
Close();
base.Dispose(disposing);
}
public void Close()
{
if (timer != null)
{
timer.Change(Timeout.Infinite, Timeout.Infinite);
using (ManualResetEvent disposeWaitHandle = new ManualResetEvent(false))
{
if (timer.Dispose(disposeWaitHandle))
{
disposeWaitHandle.WaitOne(disposeWaitMSec, false);
}
timer = null;
}
}
initializing = false;
delayedEnable = false;
enabled = false;
doNotDisposeWaitHandle.WaitOne(disposeWaitMSec, false);
doNotDisposeWaitHandle.Close();
SetMainThreadOperationCompleted();
}
[StructLayout(LayoutKind.Sequential)]
internal struct FILE_TIME
{
internal int ftTimeLow;
internal int ftTimeHigh;
}
private sealed class SafeNativeMethods
{
[ResourceExposure(ResourceScope.None)]
[DllImport("Kernel32"), SuppressUnmanagedCodeSecurityAttribute()]
internal static extern void GetSystemTimeAsFileTime(ref FILE_TIME lpSystemTimeAsFileTime);
}
}
/// <summary>
/// Provides data for the <see cref="ScheduledWorker.DoWork"/> event.
/// </summary>
public sealed class DoScheduledWorkEventArgs : DoWorkEventArgs
{
internal DoScheduledWorkEventArgs(object arg, DateTime signalTime) : base(arg)
{
SignalTime = signalTime;
}
/// <summary>
/// Gets the date/time when the <see cref="ScheduledWorker.DoWork"/> event was raised.
/// </summary>
public DateTime SignalTime { get; }
}
}
I just coded this class. I wish it helpful to others.
private class CustomTimer : IDisposable
{
private int duration = 1000;
private Action<object> tick;
private object obj;
private Thread thread;
private bool start = false;
public CustomTimer(int duration, Action<object> tick)
{
this.duration = duration;
this.tick = tick;
}
public void Start(object obj)
{
this.obj = obj;
start = true;
if (thread == null)
{
keepRunning = true;
thread = new Thread(ThreadMethod);
thread.Start();
}
else
{
if (thread.ThreadState == ThreadState.WaitSleepJoin)
thread.Interrupt();
}
}
public void Stop()
{
if (!start)
return;
start = false;
if (thread.ThreadState == ThreadState.WaitSleepJoin)
thread.Interrupt();
}
public bool IsStopped
{
get { return !start; }
}
private bool keepRunning = false;
private void ThreadMethod()
{
while (keepRunning)
{
if (start)
{
try { Thread.Sleep(duration); } catch { }
if (start && keepRunning)
tick(this.obj);
}
else if(keepRunning)
{
try { Thread.Sleep(int.MaxValue); } catch { }
}
}
}
public void Dispose()
{
this.keepRunning = false;
this.start = false;
if (thread.ThreadState == ThreadState.WaitSleepJoin)
thread.Interrupt();
if (thread.ThreadState == ThreadState.WaitSleepJoin)
thread.Interrupt();
}
}

Categories