Select to view content in your preferred language

DelayedInvoker example

151
2
Jump to solution
2 weeks ago
BillyBuerger
Occasional Contributor II

I recently started doing some custom map editing development and have a tool where I want to show some overlay graphics as the user moves their mouse around.  The overlay stuff works well but if I just redraw on every mouse move event, it takes too long to process the redraw and it lags behind the mouse cursor.  I saw there's a DelayedInvoker class that sounds like it's meant to support these kinds of situations but the documentation is very light and I don't see any examples of how it would be used.  For now, I implemented my own method where I push all coordinates from the mouse move even to a stack and then once a redraw is done, it can pop the most recent coordinate from that stack and remove/ignore any points it wasn't able to process while it was drawing.  It works well but if there's already something that is able to provide a similar function, I would love to use that instead.  Does anyone have any examples of using the DelayedInvoker for mouse move events?

0 Kudos
1 Solution

Accepted Solutions
CharlesMacleod
Esri Regular Contributor

The pattern looks something like this but I dont think it is going to give u the results you are hoping for:

internal class MapTool1 : MapTool {

    private DelayedInvoker _invoker = new DelayedInvoker(50);
    ...

    protected async override void OnToolMouseMove(MapViewMouseEventArgs e){
       //do something with the invoker
       _invoker.InvokeTask(() => {
            return QueuedTask.Run(() => {
		var mpt = MapView.Active.ClientToMap(e.ClientPoint);
                //Do something with the point
             });

Basically, just like a "QueuedTask.Run(() => { ........... })" you can wrap whatever u want to be invoked with the delay (50ms in this case) within the "_invoker.InvokeTask" lambda. So, in this case, if I am tracking the cursor position w/ the invoker I will be "processing" its position every 50ms (or whatever delay u specify) which will be quite jittery and so on.

In the Pro SDK are a number of samples where we implement logic to cut down on unnecessary mouse movement - just search for "MouseMove" and you will see them - they are all using the same, basic pattern and are updating the graphics overlay in different ways.

https://github.com/search?q=repo%3AEsri%2Farcgis-pro-sdk-community-samples%20mousemove&type=code

I suggest using something built around the pattern they are using rather than the delayed invoker.

Another way, that I have used from time to time, is this basic pattern:

internal class SelectOnMove1 : MapTool
{
	private Point _lastLocation;
	private int _deltaPixels = 0;

   protected override Task OnToolActivateAsync(bool active) {
     if (_deltaPixels == 0)
      //usually 3 pixels - use whatever number u want
       _deltaPixels = SelectionEnvironment.SelectionTolerance;


  protected async override void OnToolMouseMove(MapViewMouseEventArgs e) {
    //compare current cursor position to last (captured) position
    if (_lastLocation.X >= e.ClientPoint.X - _deltaPixels &&
        _lastLocation.X <= e.ClientPoint.X + _deltaPixels &&
        _lastLocation.Y >= e.ClientPoint.Y - _deltaPixels &&
        _lastLocation.X <= e.ClientPoint.X + _deltaPixels)
           return;//within tolerance - ignore

     _lastLocation = e.ClientPoint; //we'll use this one
     //TODO - use the point
   

 

However, it is going to be v difficult to get truly smooth tracking and it will always be a little laggy/jittery. Your mileage will vary.

 

 

View solution in original post

2 Replies
CharlesMacleod
Esri Regular Contributor

The pattern looks something like this but I dont think it is going to give u the results you are hoping for:

internal class MapTool1 : MapTool {

    private DelayedInvoker _invoker = new DelayedInvoker(50);
    ...

    protected async override void OnToolMouseMove(MapViewMouseEventArgs e){
       //do something with the invoker
       _invoker.InvokeTask(() => {
            return QueuedTask.Run(() => {
		var mpt = MapView.Active.ClientToMap(e.ClientPoint);
                //Do something with the point
             });

Basically, just like a "QueuedTask.Run(() => { ........... })" you can wrap whatever u want to be invoked with the delay (50ms in this case) within the "_invoker.InvokeTask" lambda. So, in this case, if I am tracking the cursor position w/ the invoker I will be "processing" its position every 50ms (or whatever delay u specify) which will be quite jittery and so on.

In the Pro SDK are a number of samples where we implement logic to cut down on unnecessary mouse movement - just search for "MouseMove" and you will see them - they are all using the same, basic pattern and are updating the graphics overlay in different ways.

https://github.com/search?q=repo%3AEsri%2Farcgis-pro-sdk-community-samples%20mousemove&type=code

I suggest using something built around the pattern they are using rather than the delayed invoker.

Another way, that I have used from time to time, is this basic pattern:

internal class SelectOnMove1 : MapTool
{
	private Point _lastLocation;
	private int _deltaPixels = 0;

   protected override Task OnToolActivateAsync(bool active) {
     if (_deltaPixels == 0)
      //usually 3 pixels - use whatever number u want
       _deltaPixels = SelectionEnvironment.SelectionTolerance;


  protected async override void OnToolMouseMove(MapViewMouseEventArgs e) {
    //compare current cursor position to last (captured) position
    if (_lastLocation.X >= e.ClientPoint.X - _deltaPixels &&
        _lastLocation.X <= e.ClientPoint.X + _deltaPixels &&
        _lastLocation.Y >= e.ClientPoint.Y - _deltaPixels &&
        _lastLocation.X <= e.ClientPoint.X + _deltaPixels)
           return;//within tolerance - ignore

     _lastLocation = e.ClientPoint; //we'll use this one
     //TODO - use the point
   

 

However, it is going to be v difficult to get truly smooth tracking and it will always be a little laggy/jittery. Your mileage will vary.

 

 

BillyBuerger
Occasional Contributor II

Thanks, this is very helpful.  Since the documentation on DelayedInvoke doesn't give much information, I wasn't sure if it was the right thing to use or not.  Your description and example help a lot.  I was just reviewing some of the MouseMove examples in the ProSDK examples.  Not sure how I missed or forgot those when I was working on this.  It seems what I did is similar to those examples so I think I'm good.  The redraw is pretty quick smooth on my system.  Although we'll see how it performs on slower VDIs and such where performance can be lacking.

0 Kudos