Hi Everybody,
I`ve a new question regarding a new Add-In I`m writing. It`s still for ArcGIS Pro 1.4 and SDK version 1.3.0.5861 because it extents another system on that state. Basically it monitors a local GPS device via serial port and shows the data in different ways. Everything works as desired at the moment but I`m experiencing the behaviour that my ArcGIS Pro buttons from time to time grey out /disable for a short moment.
I know this may have something to do that my UI-thread is blocked somehow but I can`t imagine the reason.
Because my Add-In has already some code in it I decided to attach the complete project as attachment. It will not run as stand-alone because it expects another add-in to be present but maybe the full code will be helpful.
Additionally I`ll try to explain what I`m doing in simplified pseudo code here:
protected GpsPanelViewModel()
{
_gpsReader = new GPSReader();
_gpsReader.RaiseGpsValuesReceivedEvent += HandleNewGPSValuesReceivedEvent;
}
private async void HandleNewGPSValuesReceivedEvent(object sender, GpsValueReceivedEventArgs e)
{
var disposable = await QueuedTask.Run(() =>
{
// Do something with received GPS-Data
// Add map point and update view model parameters for UI update
}
}
public void DoSomethingWhenGPSButtonIsClicked(object obj)
{
if (_gpsIsActive == false)
{
_gpsReader.StartReading();
}
else
{
_gpsReader.StopReading();
}
}
public class GPSReader
{
private Thread _readGpsThread;
private Thread _parseGpsThread;
private System.Threading.Timer _timerReader;
private System.Threading.Timer _timerParser;
private BlockingCollection<GpsRawData> _gpsDataBuffer;
}
public void StartReading()
{
_readGpsThread = new Thread(StartGpsReaderLoop);
_parseGpsThread = new Thread(StartGpsParserLoop);
_readGpsThread.Start();
_parseGpsThread.Start();
}
public void StopReading()
{
ClosePort();
_timerReader.Change(Timeout.Infinite, Timeout.Infinite);
_timerParser.Change(Timeout.Infinite, Timeout.Infinite);
_readGpsThread.Abort();
_parseGpsThread.Abort();
}
private void StartGpsReaderLoop()
{
_timerReader = new System.Threading.Timer((e) =>
{
GetData(); // GET data from GPS and store in _gpsDataBuffer
}, null, 0, 1000);
}
private void StartGpsParserLoop()
{
_timerParser = new System.Threading.Timer((e) =>
{
ParseData(); // Get raw data from _gpsDataBuffer and raise event when finished
}, null, 0, 500);
}
The main question for me is, where does the probem occur? Is it because I`m running two additional threads in my GPS-Reader?
Any help would be very appreciated.
Best regards
Richard
Solved! Go to Solution.
By default, buttons on the ribbon automatically become disabled whenever something is running on the main worker thread (the thread associated with QueuedTask.Run). Buttons can opt out of this by setting their DAML disableIfBusy attribute to false. However, if your task is using ArcObjects it should use QueuedTask and it should probably keep disableIfBusy = true. If your task isn't running ArcObjects (maybe you'll pulling something down from the web or using your own objects to do something), you might want to consider using a thread from the .NET thread pool (Task.Run) and disableIfBusy=false.
<button id="acme_ShowWiz" caption="Show Wizard" className="ShowWizardCMD" loadOnClick="true" disableIfBusy="false"
I didn't try your sample, however, Buttons on the ArcGIS Pro ribbon are usually tied to a 'Condition' (which in turn is tied to a State in ArcGIS Pro). You can read more about that here: https://github.com/Esri/arcgis-pro-sdk/wiki/ProGuide-Buttons.
You can try to override your GPS button's condition by addition the following attribute to your 'button' tag in the config.daml:
condition="esri_mapping_openProjectCondition"
This condition should always be true as long as a Project is open, meaning your button should be enabled when a project is open. Here is an example with the condition attribute in the last line:
<button id="WorkwithProjects_WorkWithProjects_ShowButton"
caption="Show Work With Projects" className="WorkWithProjects_ShowButton"
loadOnClick="true" keytip="W1"
smallImage="Images\Dino16.png" largeImage="Images\Dino32.png"
condition="esri_mapping_openProjectCondition" >
Conditions are defined here: https://github.com/Esri/arcgis-pro-sdk/wiki/Condition-DAML-ID-Reference
By default, buttons on the ribbon automatically become disabled whenever something is running on the main worker thread (the thread associated with QueuedTask.Run). Buttons can opt out of this by setting their DAML disableIfBusy attribute to false. However, if your task is using ArcObjects it should use QueuedTask and it should probably keep disableIfBusy = true. If your task isn't running ArcObjects (maybe you'll pulling something down from the web or using your own objects to do something), you might want to consider using a thread from the .NET thread pool (Task.Run) and disableIfBusy=false.
<button id="acme_ShowWiz" caption="Show Wizard" className="ShowWizardCMD" loadOnClick="true" disableIfBusy="false"
Thank you for your ideas.
Steve Van Esch
You`re right, the effect occurs in the last part of my process in HandleNewGPSValuesReceivedEvent(). Inside QueuedTask.Run I
1. Create a mapPoint and add it as overlay to show GPS position
var mapPoint = MapPointBuilder.CreateMapPoint(e.GPSData.GPGGA.Longitude, e.GPSData.GPGGA.Latitude, _wgs84);
MapView.Active.AddOverlay(mapPoint, _gpsPositionSymbol);
2. Conditionally shift map extent if the GPS position moves out
if (MapView.Active.Extent.XMax - mapPoint.X <= 0.003 || mapPoint.X - MapView.Active.Extent.XMin <= 0.003
|| mapPoint.Y - MapView.Active.Extent.YMin <= 0.003 || MapView.Active.Extent.YMax - mapPoint.Y <= 0.003)
{
newMapExtent = mapPoint.Extent.Expand(0.01, 0.01, false);
MapView.Active.ZoomToAsync(newMapExtent);
}
I commented this code out and the disabling disappeared. So I`m glad it`s not because of my GpsReader Stuff. Anyway these actions are needed. I need to show an updated position on the map without blocking. Of course I can set these conditions to the AddIn ribbon button to make it always active but will it have a negative impact? What if someone would click it exact at the same time my ArcObject code is running?
Additionally I also have UI button elements on my DockingPane. These UI-Elements are not part of my Config.daml? I just added them to my GpsPanel.xaml so they are basic WPF but they are also affected which is more complicate because my user should be able to start/stop GPS-Reading no matter if worker thread is adding 1 or 2 last points.
How can I prevent these buttons or other elements on my pane from being disbaled?
Hi. By default RelayCommands (ArcGIS.Desktop.Framework.RelayCommand) also automatically disable whenever the main worker thread is busy. RelayCommands can also opt out of this by setting the displayWhenBusy constructor parameter to false.
new RelayCommand(DoSomethingWhenGPSButtonIsClicked, CanStartGPS, true, false);
Thank you very much for your help. So I got rid of this flickering buttons. I`m still not 100% sure how the app will behave when I click the button while app is in busy state but we will see.