Update Visibility of Controls in Dockpane

1523
3
05-01-2020 11:37 AM
AbelPerez
Occasional Contributor III

I have a DockPane with several controls and I need to change the visibility based on the active Map

For example I have a button "Import From Map". If there is no active view that is a map then I want to hide these controls, otherwise i want to display the controls.

I looked at the community samples. ProceduralSymbolLayersWithRulePackages and ProceduralSymbolLayersWithRulePackages. Ive also watched the ESRI video on MVVM ArcGIS Pro SDK for .NET: UI Design and MVVM - YouTube  and searched on geonet. I'm still not getting it.

I know I have to setup my control XAML with the binding Visibility="{Binding ControlVisible}" and then that property has to be developed. How do i subscribe to the Pro events so that it will tell my property that the active view has changed and thus the controls need updating? I just cant seem to connect the dots.

0 Kudos
3 Replies
Wolf
by Esri Regular Contributor
Esri Regular Contributor

Hi Abel,

 Since this seems to be a common question, let me elaborate a bit.  Maybe this will also help others looking at this post.  MVVM is an architectural programming pattern that is used in most WPF based large scale or enterprise systems.  I think in the long run a good understanding on what it does will make programming using MVVM a lot easier.  I used WinForms for many years (if not decades) and it took me a while to get used to MVVM but after I got my head around it, I actually prefer it now.  If you have some time maybe you can look at this YouTube:  WPF: why MVVM? The video explains the "why?", but also looks a bit behind the curtain of MVVM. 

 Specifically to answer your question: controlling the visibility of controls on your dockpane:

 Since you already know that you need to bind the Visibility attribute of your UI control, I want to mention the approach that I often use when looking for a reference sample implementation:

On the top left of the community samples github repo page I enter the text string for which I need a code snippet for (as specific as possible to reduce the number of candidates).  So for your requirements I searched for 'Visibility="{Binding' (note: only search for this repo not all of Github):

In similar fashion you can also search https://github.com/Esri/arcgis-pro-sdk because it contains the source for all ProSnippets.  The search above yields all controls that are bound to some kind of 'visibility' property in my viewmodel.  I open the "Samples" solution (from community samples) in Visual Studio and look at the located solutions there.  In this case I search in Visual Studio for '<Grid Visibility="{Binding DockpaneVisibility}">' which yields the 'SymbolLookup' project.  I open the ViewModel file: SymbolLookupDockpaneViewModel.cs and the View file: SymbolLookupDockpane.xaml.  

Databinding links the 'DockpaneVisibility' property:

private Visibility _dockpaneVisibility;
/// <summary>
/// Controls the visible state of the controls on the dockpane
/// </summary>
public Visibility DockpaneVisibility
{
    get { return _dockpaneVisibility; }
    set { SetProperty(ref _dockpaneVisibility, value, () => DockpaneVisibility); }
}

To this UI element:

<Grid Visibility="{Binding DockpaneVisibility}">

Note that 'SetProperty(ref...' the DockpaneVisibility setter is required because this method will 'notify' the UI (via data binding) when the code changes the DockpaneVisibility property.

To change your control's visibility you can use the following code snippets:

// make the control bound to DockpaneVisibility invisible:
DockpaneVisibility = Visibility.Collapsed;

// make the control bound to DockpaneVisibility visible:
DockpaneVisibility = Visibility.Visible;

Finally, and you see the code for this also in the ViewModel file: SymbolLookupDockpaneViewModel.cs: you can subscribe to the ActiveMapViewChangedEvent either in the constructor on in InitializeAsync:

protected SymbolLookupDockpaneViewModel()
{
  //either Subscribe to ActiveMapViewChangedEvent here:
  ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChangedEvent);
}

protected override async Task InitializeAsync()
{
  //or Subscribe to ActiveMapViewChangedEvent here:
  ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChangedEvent);
}

And your subscription logic could look like this (similar to the sample I found):

/// <summary>
/// Event handler when the active MapView changes
/// </summary>
/// <param name="obj"></param>
private void OnActiveMapViewChangedEvent(ActiveMapViewChangedEventArgs obj)
{
  if (obj.IncomingView == null)
  {
    // there is no active map view - disable the UI
    DockpaneVisibility = Visibility.Collapsed;
    return;
  }
  // we have an active map view - enable the UI
  DockpaneVisibility = Visibility.Visible;
}

Since this code will only kick in after your dockpane is instantiated (it's constructor is called), you need to set up the initial visibility state in the constructor:

protected SymbolLookupDockpaneViewModel()
{
  //setup the initial visibility state
  DockpaneVisibility = MapView.Active != null ? Visibility.Visible : Visibility.Collapsed;
  //either Subscribe to ActiveMapViewChangedEvent here:
  ActiveMapViewChangedEvent.Subscribe(OnActiveMapViewChangedEvent);
}

I hope this helps.

AbelPerez
Occasional Contributor III

Wow just one brief look at this and i already understand it. Let me take the path you have laid out and I will educate myself a bit more. Every day that i wok more and more with WPF I "hate it less" and am starting to see the light. I will post my progress.

AbelPerez
Occasional Contributor III

Stepping through your snippets I think I get it now.  It was being lost on me in the community samples because they had so much going on. Coming from WinForms I'm not used to so many hooks. A suggestion...a very simple article or just maybe this post would do wonders for most developers.

As for your suggestions on where to Subscribe I prefer the first method. Only for the simple fact that on the second one you have to add the Await Task.Run so it doesn't throw an exception. The community sample has  await UpdateSymbolList(); which its way to do async.

I will continue with the video you have posted and I will also try the search method you described.

Many, MANY Thanks!!!

0 Kudos