POST
|
David, in addition to Wolf's suggestion, please see the below event. The ProjectItemsChangedEventArgs will tell you what the change is (map added, removed, layout added, removed, etc,etc) via its Action and ProjectItem properties. ArcGIS.Desktop.Core.Events.ProjectItemsChangedEvent
... View more
07-27-2020
04:49 PM
|
0
|
0
|
407
|
POST
|
Simon, with the release of 2.6, (tomorrow, July 28th, PST) this event is now available: ArcGIS.Desktop.Core.Events.PortalSignOnChangedEvent
... View more
07-27-2020
04:33 PM
|
0
|
1
|
958
|
POST
|
Hi Curt, try this: The key is that the code attempts to eliminate any unnecessary selections either by checking the amount of movement of the mouse or by comparing the current mouse position with the polygon of the current parcel. Only if the mouse has moved the pre-requisite amount that would result in a new parcel being selected does the code actually do the selection... Note: one important assumption - that the map and parcel layer are in the _same_ projection...if they have different SRs then the "GeometryEngine.Instance.Intersects" statement will fail. The map point would have to be projected first and that could be an expensive operation. To ensure that they do have the same projection just change the Map CoordinateSystem to be the same as Parcels via the Map Properties dialog. internal class SelectOnMove1 : MapTool
{
private Point _lastLocation;
private FeatureLayer _parcelLayer = null;
private static readonly object _resultsLock = new object();
Dictionary<string, object> _currentResults = null;
private int _deltaPixels = 0;
public SelectOnMove1()
{
IsSketchTool = true;
SketchType = SketchGeometryType.Rectangle;
SketchOutputMode = SketchOutputMode.Map;
}
protected override Task OnToolActivateAsync(bool active)
{
_parcelLayer = MapView.Active.Map.GetLayersAsFlattenedList()
.FirstOrDefault(l => l.Name == "Parcels") as FeatureLayer;
if (_deltaPixels == 0)
_deltaPixels = SelectionEnvironment.SelectionTolerance;
return base.OnToolActivateAsync(active);
}
protected override Task OnToolDeactivateAsync(bool hasMapViewChanged)
{
_parcelLayer = null;
return base.OnToolDeactivateAsync(hasMapViewChanged);
}
protected async override void OnToolMouseMove(MapViewMouseEventArgs e)
{
if (_parcelLayer == null)
return;
//use SelectionEnvironment.SelectionTolerance but change this as needed
//....default is usually 3...
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;
_lastLocation = e.ClientPoint;
//Get the feature attributes or null
var pin = await QueuedTask.Run(() =>
{
//Make sure that the Map spatial reference is set to the
//_same_ SR as the parcels layer!
var mpt = MapView.Active.ClientToMap(e.ClientPoint);
var strap = "";
//check against the shape first - note: we are assuming that
//the mpt and "poly" SRs are the same!
if (_currentResults != null)
{
strap = (string)_currentResults["STRAP"];
//is the point in the current feature extent?
var extent = _currentResults["ENVELOPE"] as Envelope;
if (mpt.X <= extent.XMax &&
mpt.X >= extent.XMin &&
mpt.Y <= extent.YMax &&
mpt.Y >= extent.YMin)
{
//see if we are still selecting the same feature
var poly = _currentResults["SHAPE"] as Polygon;
if (poly != null && GeometryEngine.Instance.Intersects(poly, mpt))
return strap;
}
}
//This is for the selection + highlight
var sqf = new SpatialQueryFilter()
{
FilterGeometry = mpt,
SpatialRelationship = SpatialRelationship.Intersects,
SubFields = "OBJECTID"
};
//This is the slowest part...
var sel = _parcelLayer.Select(sqf);
if (sel.GetCount() == 0)
return strap;
var oid = sel.GetObjectIDs().First();
if (_currentResults != null)
{
var curr_oid = (long)_currentResults["OBJECTID"];
if (oid == curr_oid)
return strap;
}
var attrib = Module1.Current.GetParcelRecord(_parcelLayer, oid);
_currentResults = attrib;
strap = (string)_currentResults["STRAP"] ?? "null";
return strap;
});
//Do something with the results...
var info = $"Current: STRAP:{pin}";
//etc
}
protected override Task<bool> OnSketchCompleteAsync(Geometry geometry)
{
return base.OnSketchCompleteAsync(geometry);
}
}
From the Module: internal class Module1 : Module
{
private static Module1 _this = null;
private Dictionary<long, Dictionary<string, object>> _cache = new Dictionary<long, Dictionary<string, object>>();
private bool _useCache = true;
/// <summary>
/// Retrieve the singleton instance to this module here
/// </summary>
public static Module1 Current
{
get
{
return _this ?? (_this = (Module1)FrameworkApplication.FindModule("DoMouseMove_Module"));
}
}
public Dictionary<string, object> GetParcelRecord(BasicFeatureLayer parcels, long oid)
{
if (_useCache && _cache.ContainsKey(oid))
{
return _cache[oid];
}
var qf = new QueryFilter()
{
SubFields = "SHAPE,STRAP",
ObjectIDs = new List<long> { oid }
};
var rc = parcels.Search(qf);
rc.MoveNext();
var dict = new Dictionary<string, object>();
dict["OBJECTID"] = oid;
dict["SHAPE"] = rc.Current["SHAPE"] as Geometry;
dict["ENVELOPE"] = ((Geometry)rc.Current["SHAPE"]).Extent;
dict["STRAP"] = rc.Current["STRAP"] as string ?? "null";
rc.Dispose();
if (_useCache)
{
_cache[oid] = dict;
}
return dict;
}
#region Overrides
/// <summary>
/// Called by Framework when ArcGIS Pro is closing
/// </summary>
/// <returns>False to prevent Pro from closing, otherwise True</returns>
protected override bool CanUnload()
{
//TODO - add your business logic
//return false to ~cancel~ Application close
return true;
}
#endregion Overrides
}
}
... View more
07-14-2020
04:49 PM
|
2
|
1
|
365
|
POST
|
Hi Alex, Look here... ProSnippets Geodatabase Obtaining List of Defintions from Geodatabase ProSnippets Geodatabase, Obtaining related definitions from geodatabase piecing the bits together gives you what you want //From the snippets...
IReadOnlyList<FeatureDatasetDefinition> featureDatasetDefinitions = geodatabase.GetDefinitions<FeatureDatasetDefinition>();
bool electionRelatedFeatureDatasets = featureDatasetDefinitions.Any(definition => definition.GetName().Contains("Election"));
...and...
FeatureDatasetDefinition featureDatasetDefinition = geodatabase.GetDefinition<FeatureDatasetDefinition>("LocalGovernment.GDB.Address");
IReadOnlyList<Definition> datasetsInAddressDataset = geodatabase.GetRelatedDefinitions(featureDatasetDefinition, DefinitionRelationshipType.DatasetInFeatureDataset);
//put it together and you get...
IReadOnlyList<Definition> fdsList = gdb.GetDefinitions(DatasetType.FeatureDataset);
//get all the feature classes...
foreach (var fdsDef in fdsList) {
var defsInDataset = gdb.GetRelatedDefinitions(fdsDef, DefinitionRelationshipType.DatasetInFeatureDataset);
foreach (var def in defsInDataset) { //Or use LINQ .Where( d => d.DatasetType ...
if (def.DatasetType == DatasetType.FeatureClass)
... etc ...
... View more
06-26-2020
09:41 AM
|
2
|
1
|
2733
|
POST
|
Add the based on attribute to your style and you will be good to go. Is there some reason that does not work for you? <ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
....
</Style>
</ComboBox.Style>
... View more
05-26-2020
12:00 PM
|
2
|
1
|
4984
|
POST
|
Hi Sreeni, apologies for the delay but we were testing this configuration. The bottom line is that the classes referred to in the DAML must be in the add-in assembly. Therefore, to distribute functionality across multiple assemblies, in general, there are two choices (and variations thereof): Spread the functionality across multiple add-ins.The "master" add-in, for example, defines the Tabs and the "optional" add-ins update the Tab(s) with their buttons and so forth. Within the add-in use a containment pattern to load the library classes/functions that do the "work" within the buttons, etc. public class AddinButton : ... {
private SomeLibraryClass _doRealWork = null;
protected override void OnClick() {
if (_doRealWork == null) {
_doRealWork = new SomeLibraryClass(...);
}
//etc There is a third option that involves Categories but that is beyond the scope of this post. If you are interested in that please review these samples and watch this technical session: ConfigureGallery sample CustomCategories sample Advanced Pro UI Customization, Dev Summit 2019. Advance to 36:17.
... View more
04-28-2020
09:02 AM
|
0
|
1
|
704
|
POST
|
Basically, if you ignore the logic Wolf has implemented to check whether to change progress value and text or not, the pattern Wolf is using (in WPF) is the same standard pattern as with old school WinForms for updating the UI from a non-UI thread. Namely: Check if you are on the UI thread, if so, update progress directly. If not, invoke the update on the UI thread first. So the pattern resolves to: private void UpdateProgress(...) {
//whatever "update progress" means - change a counter, text, etc
NotifyPropertyChanged("ProgressValue");//Assuming something is bound to this
}
private void UpdateProgressSafely(...) {
//FrameworkApplication and System.Windows.Application are the same thing
if (FrameworkApplication.Current.Dispatcher.CheckAccess())
//On the UI - do update directly
UpdateProgress(...);
else {
//We are on a background thread, invoke on the UI
FrameworkApplication.Current.Dispatcher.BeginInvoke((Action)(() => UpdateProgress(...)));
Which is pretty much copy/paste ready. UpdateProgressSafely() can be called from any thread. The last thing probably worth some explanation is this intuitive piece of syntax: //what is all that "(Action)(() => .....) for?
FrameworkApplication.Current.Dispatcher.BeginInvoke((Action)(() => UpdateProgress(...))); Well, let's remove all that "(Action)(.....)" cast reducing the syntax to a more legible: FrameworkApplication.Current.Dispatcher.BeginInvoke(() => UpdateProgress(...)); where "() => ..." is the standard anonymous delegate usage. However, intellisense will complain that: It "Cannot convert lambda expression to type 'Delegate' because it is not a delegate type" and you will get a compiler error. BeginInvoke has numerous overloads and without the "Action" cast the compiler cannot resolve which overload we want it to use. We have to help it by providing an explicit "Action" that wraps the delegate "() => ..." bringing us back full circle to "BeginInvoke( (Action) (() => ....) )". To simplify the syntax, you can also add your own utility method like this in your Module that can be used to call any method safely on the UI: //in your module or some helper class
public void RunOnUIThread(Action action) {
if (FrameworkApplication.Current.Dispatcher.CheckAccess())
action();
else
FrameworkApplication.Current.Dispatcher.BeginInvoke(action);
}
//usage
await QueuedTask.Run(() => {
//some length process
//...
Module1.Current.RunOnUIThread(() => UpdateProgess(50, "working..."));
... View more
04-23-2020
02:37 PM
|
2
|
0
|
1858
|
POST
|
sample: ShowLicense reference: topic8900.html - ArcGIS.Core.Licensing.LicenseInformation
... View more
04-23-2020
08:02 AM
|
0
|
1
|
1364
|
POST
|
Hi Simon, Uma and I are looking into this with the development team. There is an internal event that fires when a user signs in or when a user signs out of online or portal. However, if the user toggles between active portals into which he or she is already signed in then, of course, that event does not fire (as the portal _signed on_ status did not change).....but.... the "active portal changed" event does fire because the active portal did change (and this event is already public) even though its signed on status didn't. Therefore, with the addition of this "new" event getting exposed to give you notification of a given portal's sign in/sign out status changing _combined with_ the existing "active portal changed" event giving you notification of the active portal changing - that should give you everything you need. We will get that in for 2.6.
... View more
03-27-2020
03:29 PM
|
0
|
3
|
958
|
POST
|
As Uma stated >>The Pro UI is written using WPF<< and the SDK templates automate the generation of most of the possible UI elements that are supported in Pro (buttons, tools, panes, dockpanes, property pages, property sheets, custom controls, and so on). However, you can write any of the UI elements by hand - you would just be responsible for deriving from the correct base classes, adding the requisite overloads and so forth, and adding the necessary Config.daml entries. This can be a tedious business to repeat each time you need to add, say a button or a dockpane. That is where the SDK templates come in - to help automate a lot of these repetitive tasks. However, you don't _have_ to use them. You can do it all by hand as you need (for example - copy/pasting or moving existing classes from one add-in project to another). Also, within WPF, the underlying technology used by the Pro UI, you can host a Winforms control within a WPF user control or window - though this is not a common pattern and is usually done for legacy reasons (eg an old WinForms control provides a specific set of functionality not easily duplicated in WPF or the level of effort is too great to rewrite it). You can read the Microsoft documentation on WindowsFormHost for more information if you are interested.
... View more
03-25-2020
10:16 PM
|
0
|
1
|
1296
|
POST
|
Ideally, you want the defaultAssembly and defaultNamespace attributes to _match_ the corresponding properties in your visual studio project (i.e. Application properties tab, Assembly name, Default namespace). If you change the name of your assembly or default namespace in Visual Studio then be sure to update your Config.daml to keep it in sync. Additionally, depending on how you have your project arranged and which project folders you organize your content into, you may also need to change the className attribute on your various DAML entries if you rename classes or move them around. If a given control or dockpane or whatever is _not_ in the default namespace then you must always fully qualify the className attribute. The SDK templates do this for you automatically when they first generate a given DAML entry but if you refactor your add-in content then it is your responsibility to keep your Config.daml up to date. In your example, say you were to refactor your Module cs file to a different namespace (or rename it or....) then you would need to go into the Config.daml and change the "<module className" attribute to match - fully qualifying the className if your "refactored" ModuleMain were to no longer be in the MyCompany.MyDepartment.MyAddin namespace (i.e. your current default).
... View more
03-23-2020
02:08 PM
|
0
|
1
|
965
|
POST
|
This section of the Pro Concepts Framework may help: ProConcepts-Framework#module-plug-in. This is basically what Than is describing. Some debugging tips are here: ProGuide-Diagnosing-ArcGIS-Pro-Add-ins
... View more
03-23-2020
09:16 AM
|
2
|
0
|
965
|
POST
|
I think we have two separate things going on here and please confirm: 1) You originally were asking can you specify a clipping polygon for the map via the API, is that right? The answer is no. You cannot and this will be added for 2.6 2) You are using EditOperation.Clip to "manually" clip features and you have encountered a bug, is that right? So no, my statement regarding 1) , map clipping, is not related. To submit a bug please contact tech support. This post may help: How to report a bug in ArcGIS Pro Note: just looking at your code above, I believe the result you are looking for requires ClipMode.PreserveArea not DiscardArea.
... View more
03-11-2020
10:36 AM
|
0
|
1
|
1772
|
POST
|
In general, calling a modal dialog from the queued task should be avoided. protected override void OnClick()
{
//construct on the UI
var dlg = new SaveItemDialog();
dlg.Title = "On QueuedTask";
QueuedTask.Run(() =>
{
FrameworkApplication.Current.Dispatcher.Invoke(() => {
var result = dlg.ShowDialog();
});
});
}
....
... View more
03-06-2020
02:47 PM
|
0
|
1
|
399
|
POST
|
Overlays are not persisted which is why you cannot print (or export) them. At 2.6 there will be the concept of a graphics layer. Graphics you add to a graphics layer will be persistable (and so can be printed).
... View more
03-03-2020
02:02 PM
|
2
|
0
|
376
|
Title | Kudos | Posted |
---|---|---|
2 | Wednesday | |
1 | 03-18-2024 06:28 PM | |
1 | 03-19-2024 10:15 AM | |
1 | 03-18-2024 03:46 PM | |
2 | 03-14-2024 08:37 AM |
Online Status |
Offline
|
Date Last Visited |
yesterday
|