Is there a simple solution/idea/strategy to create a setTimeout equivalent function in a WinForms app. I'm primarily a web developer but am not sure how I'd go about this in a WinForms App. Basically, I have a textbox, and after each keystroke I want to run a task to populate a list (like an auto-complete type thingy) but want to be able to cancel (e.g. clearTimeout) if the user keeps entering characters...
My only guess is to perhaps use a BackGroundWorker and make it sleep initially, and while it is sleeping, it could be cancelled, if the user stops entering keys and the sleep period ends, it then goes and runs the task etc
(i don't care if an example is C# or Vb.Net)
I know this is an old question but an alternative solution would be to use Task.Delay(delay).ContinueWith((task) => { /* Code */ });.
Thread.Sleep vs Task.Delay?
or there is await Task.Delay(delay);
https://social.msdn.microsoft.com/Forums/vstudio/en-US/345f0402-3af0-4f96-a501-073674883ba3/building-an-async-settimeout-function?forum=csharpgeneral
You can use a System.Timers.Timer: set AutoReset to false and use Start/Stop methods and create a handler for the Elapsed event.
Here's an example implementation in vb.net:
Public Sub SetTimeout(act As Action, timeout as Integer)
Dim aTimer As System.Timers.Timer
aTimer = New System.Timers.Timer(1)
' Hook up the Elapsed event for the timer.
AddHandler aTimer.Elapsed, Sub () act
aTimer.AutoReset = False
aTimer.Enabled = True
End Sub
public void setTimeout(Action TheAction, int Timeout)
{
Thread t = new Thread(
() =>
{
Thread.Sleep(Timeout);
TheAction.Invoke();
}
);
t.Start();
}
Timer Implementation:
public void SetTimeout(Action action, int timeout)
{
var timer = new System.Windows.Forms.Timer();
timer.Interval = timeout;
timer.Tick += delegate (object sender, EventArgs args)
{
action();
timer.Stop();
};
timer.Start();
}
I can propose following
internal class Timeout : System.Timers.Timer
{
public Timeout (Action action, double delay)
{
this.AutoReset = false;
this.Interval = delay;
this.Elapsed += (sender, args) => action();
this.Start();
}
}
// Example
var timeout = new Timeout(() => {
Console.WriteLine("init 1");
}, 500);
timeout.Stop();
You can use also:
Delay.Do(3000 /*in ms*/, () => { /* Do somthing */ });
Where Delay.Do is:
using System;
using System.Timers;
public class Delay
{
public static void Do(int after, Action action)
{
if (after <= 0 || action == null) return;
var timer = new Timer { Interval = after, Enabled = false };
timer.Elapsed += (sender, e) =>
{
timer.Stop();
action.Invoke();
timer.Dispose();
GC.SuppressFinalize(timer);
};
timer.Start();
}
}
Note: When updating a control in the UI thread use Control.Invoke:
Delay.Do(2000, () => { lblOk.Invoke((MethodInvoker)(() => { lblOk.Visible = false; })); });
This is my way, use C# 7.0 syntax feature.
Some differ with js, when timeout action execute then will can't be clear.
internal static class JsStyleTimeout
{
private static readonly ConcurrentDictionary<int, Thread> InnerDic;
private static int _handle;
static JsStyleTimeout()
{
InnerDic = new ConcurrentDictionary<int, Thread>();
}
public static int Set(Action action, int delayMs)
{
var handle = Interlocked.Increment(ref _handle);
var thread = new Thread(new ThreadStart(delegate
{
Thread.Sleep(delayMs);
InnerDic.TryRemove(handle, out var _);
Task.Factory.StartNew(action);
}));
InnerDic.TryAdd(handle, thread);
thread.Start();
return handle;
}
public static void Clear(int handle)
{
if (InnerDic.TryRemove(handle, out var thread))
thread.Abort();
}
}
public void setTimeout(Action act, int timeout)
{
Action action = () =>
{
Thread.Sleep(Timeout);
act();
};
new Thread(() => Invoke(action)).Start();
}
when using Task.Delay() and your action to edit/set winforms control. you have to add TaskScheduler.FromCurrentSynchronizationContext() or will get error Cross thread operation
void SetTimeout(Action action, int ms)
{
Task.Delay(ms).ContinueWith((task) =>
{
action();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
SetTimeout(() => {
myButton.Enabled = true;
}, 3000);
I'd recommend using reactive programming for this. See https://github.com/Reactive-Extensions/Rx.NET for the Reactive Extensions for .NET and http://reactivex.io/ for the general information about Reactive programming.
I'm afraid I'm only familiar with the JavaScript reactive library, so I can't give you a C-Sharp example, but in JavaScript it'd work something like this:
Rx.Observable.fromEvent(..eventdetails..)
.debounceTime(300)
.distinctUntilChanged()
.subscribe(eventHandler);
Using a setup like this you can chain operators to map and merge all kinds of events from a source to a subscriber. The simple example above reacts to an event, a keyUp for instance, and waits until there is no new keyUp for 300 ms and then calls the eventHandler, but only if the new value (after 300ms) is different from the last emitted value.
Related
I have created a Blazor Webassembly Project and added a key listener in JavaScript, which is listening to every key stroke on the DOM document. Everything works as expected, however when I open the Blazor page where the key listener is registered and later open it again, the following error occurs in the Web Browser:
There is no tracked object with id '2'. Perhaps the
DotNetObjectReference instance was already disposed. (Parameter
'dotNetObjectId')
Obviously the object "dotnethelper" is disposed but the Javascript is still listening / getting called.
Basically I implemented the "Component instance .NET method helper class" from the Microsoft Documentation.
Blazor Page:
Note: The IDisposable is injected on the top and the Dispose function is getting called.
#code {
private KeyListenerInvokeHelper _keyListenerInvokeHelper;
private DotNetObjectReference<KeyListenerInvokeHelper>? objRef;
protected override async Task OnInitializedAsync()
{
objRef = DotNetObjectReference.Create(_keyListenerInvokeHelper);
await JS.InvokeVoidAsync("initializeKeyListener", objRef);
}
public void Dispose()
{
objRef?.Dispose();
}
}
Javascript File:
window.initializeKeyListener = (dotnetHelper) => {
document.addEventListener('keydown', logKey);
function logKey(e) {
dotnetHelper.invokeMethod('OnKeyDown', e.key);
console.log('key down ' + e.key);
}
}
KeyListenerInvokeHelper:
public class KeyListenerInvokeHelper
{
private readonly Action<string> action;
public KeyListenerInvokeHelper(Action<string> action)
{
this.action = action;
}
[JSInvokable("OnKeyDown")]
public void OnKeyDown(string key)
{
action.Invoke(key);
}
}
What have I tried so far?
I tried to reset the function on window.initializeKeyListener (i.e. setting window.initializeKeyListener), however this did not achieve anything
I tried removing the eventlistener on the 'keydown' event.
When you dispose of your object, you need to remove the event listener as well. You mentioned I tried removing the eventlistener on the 'keydown' event., but perhaps the way you did it was not correct?
My javascript is a little rusty, but I think you could do something like the following:
var logkey;
window.initializeKeyListener = (dotnetHelper) => {
logkey = (e) => {
dotnetHelper.invokeMethod('OnKeyDown', e.key);
console.log('key down ' + e.key);
};
document.addEventListener('keydown', logkey);
}
window.removeKeyListener = () => {
document.removeEventListener('keydown', logkey);
}
and then in your component:
#implements IAsyncDisposable
public async ValueTask DisposeAsync()
{
await JSRuntime.InvokeVoidAsync("removeKeyListener");
objRef?.Dispose();
}
Having said that, perhaps calling a static method in C# using [JSInvokable] would be better suited for your use case?
I have a javascript event called Hello:
addEventListener('hello', function () {
alert("event listener");
})
and, in another javascript function, I raise the event:
let event = new Event("hello", { bubbles: true });
document.dispatchEvent(event);
What I want to do now is let the event trigger in a javascript function.
Blazor should listen to the event, not javascript calling a Blazor method.
Hope anyone can assist me.
Regards me,
For custom events, you will need to manually utilize JavaScript/.NET interoperability.
Using the Instance Method Call method:
Pass the .NET instance by reference to JavaScript:
Make a static call to DotNetObjectReference.Create.
Wrap the instance in a DotNetObjectReference instance and call Create on the DotNetObjectReference instance. Dispose of DotNetObjectReference objects (an example appears later in this section).
Invoke .NET instance methods on the instance using the invokeMethod or invokeMethodAsync functions. The .NET instance can also be passed as an argument when invoking other .NET methods from JavaScript.
Example
Note, this is a very simplified example. You probably want to add a few things; start by IDisposable on your interop classes to avoid memory leaks.
In C#, create a helper class to manage the interop:
public class CustomEventHelper
{
private readonly Func<EventArgs, Task> _callback;
public CustomEventHelper(Func<EventArgs, Task> callback)
{
_callback = callback;
}
[JSInvokable]
public Task OnCustomEvent(EventArgs args) => _callback(args);
}
public class CustomEventInterop : IDisposable
{
private readonly IJSRuntime _jsRuntime;
private DotNetObjectReference<CustomEventHelper> Reference;
public CustomEventInterop(IJSRuntime jsRuntime)
{
_jsRuntime = jsRuntime;
}
public ValueTask<string> SetupCustomEventCallback(Func<EventArgs, Task> callback)
{
Reference = DotNetObjectReference.Create(new ScrollEventHelper(callback));
// addCustomEventListener will be a js function we create later
return _jsRuntime.InvokeAsync<string>("addCustomEventListener", Reference);
}
public void Dispose()
{
Reference?.Dispose();
}
}
In a Blazor component, add an instance of the interop class (Interop) and add a local method as a callback (HandleCustomEvent):
private CustomEventInterop Interop { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender) {
if (!firstRender)
{
return;
}
Interop = new(JS);
await Interop.SetupCustomEventCallback(args => HandleCustomEvent(args));
HasRendered = true;
}
private void HandleCustomEvent(EventArgs args) {
// ... handle custom event here
}
In JavaScript, add a method that references the DotNetObjectReference and can call the interop in C#:
function addCustomEventListener(dotNetObjectRef) {
document.addEventListener('hello', (event) => {
// Calls a method by name with the [JSInokable] attribute (above)
dotNetObjectRef.invokeMethodAsync('OnCustomEvent')
});
}
If using TypeScript, you might check out this GitHub Issue.
For anyone wondering the solution proposed by #Mister Magoo is no longer a preview for .NET 6 and is documented here with some exemples.
In a nutshell :
Create a C# class with the EventHandlerAttribute :
[EventHandler("oncityclicked", typeof(CustomSelectionCityEventArgs),
enableStopPropagation: true, enablePreventDefault: true)]
public static class EventHandlers
{
}
public class CustomSelectionCityEventArgs: EventArgs
{
public Guid Id { get; set; }
}
Add JS inside ./wwwroot/index.html and after <script src="_framework/blazor.webview.js" autostart="false"></script> :
<script>
Blazor.registerCustomEventType('cityclicked', {
createEventArgs: event => {
return {
id: event.detail
};
}
});
</script>
In you razor :
#code {
private void HandleCityClicked(CustomSelectionCityEventArgs eventArgs)
{
Console.WriteLine("Bouep");
}
}
<div id="CarteLeaflet" #oncityclicked="HandleCityClicked"></div>
And finally in the JS you can dispatch the event :
function OnMarkerClick(pId) {
const event = new CustomEvent('cityclicked', {
bubbles: true,
detail: pId
});
document.getElementById('CarteLeaflet').dispatchEvent(event);
}
Don't make the same mistake as me, the event name in the C# should start with "on" (JS : "cityclicked", C# : "oncityclicked").
I am converting some Q-promise based typescript code to ES6-promises.
At a certain point, I used Q.defer and in my migration I just rewritten defer as an ES6 promise like explained in this comment:
https://stackoverflow.com/a/49825137/13116953
I was trying to get rid of this defer approach, if possible, and was looking for alternative solutions.
Reasons of my question are:
Deferred promises are considered an anti-pattern, in general
Want to know if this scenario is one of the few where deferred are really the only way
Here's my scenario:
// app start, this is my low level API
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
...
private _myRecord: MyRecordObj | null = null;
private _recordReceivedDef = newDeferred(); // was Q.defer()
// callback will be called when record is received
private readonly onAdd = (recordObj: MyRecordObj) => {
this._myRecord = recordObj;
this._recordReceivedDef.resolve(recordObj);
}
...
// here's my async code that requires the deferred
public readonly doStuff = async () => {
// ...
const myRec = await this._recordReceivedDef.promise;
// use myRef
}
My question is: is there a way I can get rid of this defer?
I was thinking of something that resolves when _myRecord changes, but have no idea how to do it.
Side note:
I use MobX in other parts of our app, thus having
await when(() => this._myRecord); // of course _myRecord must be #observable
would be handy, but unfortunately I cannot use MobX in this particular piece of code.
Any help is much appreciated.
Thanks a lot!
Assuming init is called before doStuff, the proper way would be
init() {
this._myRecordPromise = new Promise((resolve, reject) => {
commService.subscribe("myRecordId", {
onRecordAdd: (recordObj: MyRecordObj) => {
// callback will be called when record is received
resolve(this._myRecord = recordObj);
}
});
});
}
…
private _myRecord: MyRecordObj | null = null;
private _myRecordPromise: Promise<MyRecordObj>;
…
public readonly doStuff = async () => {
…
const myRec = await this._myRecordPromise;
// use myRef
}
You might even drop the _myRecord completely and keep only the _myRecordPromise.
However, you might want to consider not constructing your instance at all before the record is received, see Is it bad practice to have a constructor function return a Promise?.
If init is called at some arbitrary time, you will need some kind of defer pattern, but you don't need newDeferred() for that. Just write
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
…
private _myRecordPromise: Promise<MyRecordObj> = new Promise(resolve => {
this.onAdd = resolve;
});
private readonly onAdd: (recordObj: MyRecordObj) => void;
For people who might be interested, there is also another solution that uses an event emitter approach.
Suppose you have a class EventEmitter that implements the following interface:
// pseudo code
type UnsubscribeFn = () => void;
interface IEventEmitter<T> {
/** Fires an event */
emit(args: T): void;
/** Subscribes to emissions of this event and provides an unsubscribe function */
subscribe((args: T) => void): UnsubscribeFn;
}
you can provide your clients with a whenAdded function like below
// pseudo code
class Subscriber {
readonly onAddEmitter = new EventEmitter<MyRecordObj>();
// app start
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
onAdd = (rec: MyRecordObj) => {
// do some stuff
onAddEmitter.emit(rec);
}
whenAdded(): Promise<MyRecordObj> {
return new Promise((res) => {
const unsub = onAddEmitter.subscribe((record: MyRecordObj) => {
unsub();
res(record);
});
});
}
}
The goals achieved are:
Get rid of Q promises in favour of ES6 ones
Adapt the commService event API to a promise-based one
I'm trying to refresh Canvas on DoubleTap in android. I use GestureDetector in custom View.
final GestureDetector mDetector = new GestureDetector(
getContext(), new GestureDetector.OnGestureListener() {
#Override
public boolean onDoubleTap(MotionEvent e) {
invalidate();
return true;
}
}
But I'm getting the error
The method onDoubleTap(MotionEvent) of type new
GestureDetector.OnGestureListener(){} must override or implement a
supertype method
with
Remove '#Override' annotation
solution. I remove override and get this warning
The method onDoubleTap(MotionEvent) from the type new
GestureDetector.OnGestureListener() {} is never used locally.
Then I tried to test whether this works and made a function to change TextView string whenever I DoubleTap. Nothing happens.
I also looked at GestureDetector Reference for explanations, but they don't even have DoubleTap there, which everybody uses. What should I do?
try this
final GestureDetector mDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
});
For the ones, who were wondering how to set it also to the corresponding view:
final GestureDetector gDetector = new GestureDetector(getBaseContext(), new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public boolean onDoubleTap(MotionEvent e) {
doIt();
return true;
}
});
// Set it to the view
mButton.setOnTouchListener((v, event) -> gDetector.onTouchEvent(event));
My approach to this problem was different since I needed to perform something for the onClick listener as well, and also it was in a list view, so I needed to know what was the item content.
here is my approach using kotlin Job:
At the top of the class I've declared something like this:
private var doubleTapTimerJob: Job = Job()
private var clickedViewItem: CartViewItem? = null
val DOUBLE_TAP_DELAY = 200L
where CartViewItem is the model that is used in the list.
and this is my onClickListener logic:
if (clickedViewItem == null || clickedViewItem != cartViewItem) {
doubleTapTimerJob.cancel()
doubleTapTimerJob = lifecycleScope.launch {
delay(DOUBLE_TAP_DELAY)
clickedViewItem = null
}
clickedViewItem = cartViewItem
onClicked(cartViewItem)
} else {
onDoubleClicked(cartViewItem)
clickedViewItem = null
doubleTapTimerJob.cancel()
}
here I wait for 200 milliseconds for the second tap, and if it didn't happen, I will make clickedViewItem null, so its not valid anymore
I'm trying to make a base class that issues a method for throtteling highly frequented event calls like the document.onscroll event. Here is my base class:
class ThrottledRunner {
private timerId: number;
lastTimeRun: number;
runAtMostEvery = 100;
// Here is the Method
runThrottled(action: (e: ThrottledRunner) => void) {
var now: number = new Date().getTime();
if (this.timerId == null) {
if (now - this.lastTimeRun > (3 * this.runAtMostEvery)) {
action(this);
this.lastTimeRun = now;
}
this.timerId = setTimeout(function (e: ThrottledRunner) {
e.timerId = null;
e.lastTimeRun = new Date().getTime();
action(e);
}, this.runAtMostEvery);
}
}
}
My derived class:
class MyTest extends ThrottledRunner {
myProp: string = "works";
constructor() {
super();
window.addEventListener("scroll", () => this.runThrottled(this.onScroll(this)));
// Supplied parameters do not match any signature of call target.
// Could not select overload for 'call' expression.
}
onScroll(self: MyTest): void {
alert(self.myProp);
}
}
Since MyTest derives from ThrottledRunner, runThrottled() should accept it as a parmeter but it seems i am wrong. I moved completely to Typescript + vanillajs, so no jQuery answers please.
Have you had a look at using underscorejs throttle() function ?
_.throttle(function, wait, [options])
Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.
Underscore has a number of extremely useful functions, and has full TypeScript and nuGet support : underscore.TypeScript.DefinitelyTyped
You can't call onScroll the way you are as it's calling it immediately upon executing, when it really needs to wait until your runThrottled application is ready. I've changed the onScroll method to not need a parameter as the this context is set correctly.
If you change your class to:
class MyTest extends ThrottledRunner {
myProp: string = "works";
constructor() {
super();
window.addEventListener("scroll",
() => this.runThrottled(() => this.onScroll()));
}
onScroll(): void {
console.log(this.myProp);
}
}
The this will be correct within the context of the runThrottled.