Yeah there's a few ways you could do this yourself with a ViewModel. A group layer is really just visual grouping to the user - it doesn't really have any effect on the visual appearance on the actual map, so the trick is to "lie" about the layer hierarchy. And since the toolkit ToC control is centered around the ILayerContent interface, you can actually create a VM that works with that too. Below is an example that just groups all feature layers into a group-layer and puts all the basemap layers in its own single-item entry.
You can tweak this sample to group on something else. It could definitely be optimized here and there, but it's a good starting point for changing how a collection of layers gets exposed to the user.
public class MapTocVM : IEnumerable<ILayerContent>, INotifyCollectionChanged
{
private Map _map;
public event NotifyCollectionChangedEventHandler CollectionChanged;
public MapTocVM(Map map)
{
_map = scene;
_map.OperationalLayers.CollectionChanged += OperationalLayers_CollectionChanged;
_map.PropertyChanged += _map_PropertyChanged;
}
private void _map_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(Map.Basemap))
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OperationalLayers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public IEnumerator<ILayerContent> GetEnumerator()
{
foreach(var layer in _map.OperationalLayers)
{
if(!(layer is FeatureLayer))
yield return layer;
}
yield return new GroupLayerContent(_map.OperationalLayers.OfType<FeatureLayer>(), "Feature Layers");
yield return new BasemapLayerContent(_map.Basemap);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private class GroupLayerContent : ILayerContent
{
IEnumerable<ILayerContent> _sublayers;
public GroupLayerContent(IEnumerable<ILayerContent> sublayers, string name)
{
_sublayers = sublayers;
Name = name;
}
public bool CanChangeVisibility => false;
private bool m_isVisible;
public bool IsVisible
{
get { return m_isVisible; }
set { m_isVisible = value; }
}
public string Name { get; }
public bool ShowInLegend { get; set; }
public IReadOnlyList<ILayerContent> SublayerContents => new ReadOnlyCollection<ILayerContent>(_sublayers.ToList());
public Task<IReadOnlyList<LegendInfo>> GetLegendInfosAsync() => Task.FromResult<IReadOnlyList<LegendInfo>>(null);
public bool IsVisibleAtScale(double scale) => true;
}
private class BasemapLayerContent : ILayerContent
{
private Basemap _basemap;
public BasemapLayerContent(Basemap basemap)
{
_basemap = basemap;
}
public bool CanChangeVisibility => false;
public bool IsVisible { get => true; set => throw new NotSupportedException(); }
public string Name => "Basemap: " + _basemap.Name;
public bool ShowInLegend { get => true; set => throw new NotImplementedException(); }
public IReadOnlyList<ILayerContent> SublayerContents => null;
public Task<IReadOnlyList<LegendInfo>> GetLegendInfosAsync() => Task.FromResult<IReadOnlyList<LegendInfo>>(null);
public bool IsVisibleAtScale(double scale) => true;
}
}