I am using the Attribute Table from the Community Examples: arcgis-pro-sdk-community-samples .
I am loading a selected FeatureLayer and it display's in the table fine.
I want to sort the table by a Field called "SortField".
I looked at the Answer from this question: Sort Table based on a given field , and decided to try it.
Firstly, the custom Attribute Table does not extend TableView, it extends UserControl, but I found a Way to get the TableControl, which has the same properties, methods and even handy Events.
So using this answer:
var tv = TableView.Active;
if (tv == null)
return;
// sort the active field descending
if (tv.CanSortDescending)
tv.SortDescending();
// sort the active field ascending
if (tv.CanSortAscending)
tv.SortAscending();
// peform a custom sort programmatically
if (tv.CanCustomSort)
{
// sort fields
var dict = new Dictionary<string, FieldSortInfo>();
dict.Add("STATE_NAME", FieldSortInfo.Asc);
dict.Add("CITY_NAME", FieldSortInfo.Desc);
await tv.SortAsync(dict);
}
// perform a custom sort via the UI
if (tv.CanCustomSort)
tv.CustomSort();
I wrote this:
private void CatalogueTableContentLoaded(object? sender, EventArgs e)
{
//Inside an Event handler that is invoked when the FeatureLayer has been
//loaded into the table's grid.
QueuedTask.Run(async () =>
{
TableControl tc = null;
await FrameworkApplication.Current.Dispatcher.Invoke(async () =>
{
tc = sender as TableControl;
//The event is invoked before the grid draw is complete, so if we
//wait a shorter time than this, tc.CanCustomSort will be false.
await Task.Delay(1500);
});
if (tc.CanCustomSort)
{
//I tried this....
var dict = new Dictionary<string, FieldSortInfo>();
dict.Add($"{SortField}", FieldSortInfo.Desc);
await tc.SortAsync(dict);
//And I tried this...
SelectedFeatureLayerWrapper.SortDecending([SortField]);
}
}).ConfigureAwait(true).GetAwaiter();
}
//Here is FeatureLayerWrapper.SortDecending
public void SortDecending(string[] fieldList)
{
QueuedTask.Run(() =>
{
var cimLayer = GetDefinition() as CIMFeatureLayer;
var sortInfoList = new List<CIMFeatureSortInfo>();
// Create a new sort info object
foreach (var fieldName in fieldList)
{
var sortInfo = new CIMFeatureSortInfo();
sortInfo.FieldName = fieldName; // Name of the field to sort by
sortInfo.SortDirection = SortOrderType.Descending;
sortInfoList.Add(sortInfo);
}
// Assign to FeatureSortInfos (accepts an array)
cimLayer.FeatureSortInfos = sortInfoList.ToArray();
// Apply the definition back to the layer
SetDefinition(cimLayer);
}).ConfigureAwait(false).GetAwaiter();
}
When this code is completed, using either the TableSort or the FeatureLayer sort, the table remains unsorted.
Goggle AI suspects the TableControl in the custom Attribute Table, is not truely linked to the grid, but I can get the Events just fine.
Using the FeatureLayer sort, the table flashes, as though responding to the sort, but the sort doesn't take. I think this is because the sort isn't permanent using the FeatureLayer sort.
I even tried sorting the FeatureClass and using a Select , but the table freezes then.
Any insight or better ideas would be appreciated.
Thanks in advance.
Solved! Go to Solution.
Hi,
TableControl SortAsync method doesn't need QueuedTask.Run. Checking of CanCustomSort has different meaning. It means if the table control can display the custom sort dialog. You need to check IsReady
I have attached TableControl in dockpane add-in project. After compilation open ArcGIS Pro. Go to AddIns tab. Select "Show TableControlDockpane" button. Go to catalog pane and select feature class or table. Press Sort button in opened dockpane with TableControl
Hi,
TableControl SortAsync method doesn't need QueuedTask.Run. Checking of CanCustomSort has different meaning. It means if the table control can display the custom sort dialog. You need to check IsReady
I have attached TableControl in dockpane add-in project. After compilation open ArcGIS Pro. Go to AddIns tab. Select "Show TableControlDockpane" button. Go to catalog pane and select feature class or table. Press Sort button in opened dockpane with TableControl
I ended up launching the main Attribute Table and using the TablePane. I removed the custom rig.
Thanks @GKmieliauskas ,
After playing around with the actual ESRI attribute table for two days, I decided to tackle this custom one.
So, I have a few things going one.
I have FeatureClasses in my custom catalog window and when I select that FeatureClass, it is set as Selected FeatureClass. When the SelectedFeatureClass is available, my table button activates. If I want to view that item, I click it and the FeatureClass becomes a FeatureLayer and in placed in the map container.
These FeatureClasses only hold rows that describe more important gdb items like ships, buoys, and icebergs, also pdf, bmps, etc, so instead of creating a layer, I should, probably turn the FeatureClass into a Table that can be viewed in the table. (Thats for later).
But since I have to turn the FeatureClass into a layer, there is a slight passage of time before I physically have a layer to place into the table, and when I place it into the table, a couple of milliseconds pass before the table IsReady!!!, this is why I launch delays in mini async tasks. It waits for the UI thread to do its thing without delaying or stopping the UI thread's momentum.
Here is the new code:
private void LoadAttributesWindow()
{
try
{
//If the table is already open then bug out.
if (BapsAttributeTableViewModel.IsVisible())
{
return;
}
//This is the first time opening the table. Make sure we can get the TableControl when it is ready,
//and we can get the selection count of features from the attribute table.
PropertyChangedEventHandler subscriber = (object sender, PropertyChangedEventArgs e) => {
var vm = (BapsAttributeTableViewModel)sender;
if (e.PropertyName.Equals("TableControl"))
{
//When the TableControl is available, sort it.
TableControl? tc = vm.TableControl;
if (tc != null)
{
SortTableView(tc);
}
} else if (e.PropertyName.Equals("SelectedRowIds")){
//If we have a FeatureClass selected and we have some features selected in
//that feature class, we can activate the Catalog load button.
//This button opens an asc file, view a bitmap, views a pdf file or load a featurelayer
//into the map from the descriptions within the selected rows.
SelectedFeatureCount = vm.SelectedRowIds.Count;
}
};
//Subscribe and respond
BapsAttributeTableViewModel.SubscribeToPropertyChangeEvent(subscriber);
//Get out of the way start the loading gif
//Show the layer and turn off the loading gif.
Task.Run(async () =>
{
IsDataLoading = true;
await Task.Delay(150);
FrameworkApplication.Current.Dispatcher.Invoke(() =>
{
BapsAttributeTableViewModel.Show(SelectedFeatureLayer.FeatureLayer);
IsDataLoading = false;
});
}).ConfigureAwait(true).GetAwaiter();
}
catch (Exception ex)
{
MessageHandling.ErrorMessage(ex);
}
}
Next is the sort function:
/// <summary>
/// Sorts the specified table control asynchronously by the configured sort fields.
/// </summary>
/// <param name="tableView">The table view to sort.</param>
/// <returns>A task representing the asynchronous sort operation.</returns>
private async Task SortTableView(TableControl tableView)
{
var cansort = false;
await Task.Run(async () =>
{
do
{
//It has taken nearly a second for the a feature layer to
//be ready in this custom table...
await Task.Delay(750);
FrameworkApplication.Current.Dispatcher.Invoke(() =>
{
//I can't touch the tableview in the previous task, so I step down
//into the UI thread to ask this question.
cansort = tableView.IsReady;
if (cansort)
{
var sortFields = new Dictionary<string, FieldSortInfo>();
foreach (var sortName in SortField)
{
sortFields.Add(sortName, FieldSortInfo.Desc);
}
tableView.SortAsync(sortFields);
}
});
} while (cansort == false);
});
}This is where the newly selected FeatureClass gets turned into a FeatureLayer(table in the future).
/// <summary>
/// Handles property changes in the view model, particularly for catalog layer selection.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The event data containing the property name that changed.</param>
private void CatalogBrowserViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals(nameof(CatalogLayerSelected)))
{
if (SelectedCatalogLayer != null)
{
//Grab the selected FeatureClass...
var featureClass = ((CatalogObjectLayerInfo)SelectedCatalogLayer.Tag).FeatureClass;
if (featureClass == null)
{
return;
}
if (SelectedFeatureLayer == null || !SelectedFeatureLayer.Name.Equals(featureClass.Name))
{
//Start the loading gif...
IsDataLoading = true;
//Either create the FeatureLayer or grab the existing one from the map.
//Takes about 100 ms.
var featureLayer = featureClass.CreateCatalogFeatureLayer();
Task.Delay(75).ConfigureAwait(true).GetAwaiter();
SelectedFeatureLayer = featureLayer;
}
}
}
}