Bill Blogs in C# -- WPF

Bill Blogs in C# -- WPF

Created: 2/10/2011 2:54:55 PM

This is one of those small tips that took me way too long to find, and I figure if I needed to search that long to find the right answer, I might be able to help others by posting it here.

One practice I usually use for WPF and Silverlight applications is to create a 1:1 correspondence between my Views (Windows, User Controls, and so one) and my ViewModel classes. I find that practice makes it easier to ensure that I’ve tested the behavior of the different visual elements in the program.

In general, that works great. Each view has its own DataContext, and that DataContext points to a ViewModel that holds the properties for that view. But, there’s one place where that falls down: having a parent view control the visibility of the child control.

As an example, one windows has two child controls that occupy the same location. The associated view model assures that exactly of the two child controls is visible at one time:

          <
          c:FirstControl
        
          Grid.ColumnSpan="3"
          Visibility
          ="{Binding FirstControlVisible}"
          />
        
          <
          c:SecondControl
        
          Grid.ColumnSpan="3"
          Visibility
          ="{Binding SecondControlVisible}"
          />
        

So far, so good.  This ran great in prototypes, but once I created the specific ViewModel classes for FirstControl and SecondControl, it stopped working. No matter what I did, both controls were visible. I ran it through the debugger, and I was getting errors saying that the DataContext did not have the correct property.

Hmm. I knew my ViewModel had the right property (it worked before, all tests were still green), so what was going on? Well, the problem was that the binding was using the DataContext for each user control, not for the parent window.

I now had a different ViewModel for each View. That means each view has a different object for its DataContext. In order to get the parent DataContext, you have to specify the source in the binding.  It looks like the following:

          <
          c:FirstControl
        
          Grid.ColumnSpan="3"
          Visibility
          ="{Binding Path=DataContext.FirstControlVisible,    RelativeSource={RelativeSource AncestorType=Window}}"
          />
        
          <
          c:SecondControl
        
          Grid.ColumnSpan="3"
          Visibility
          ="{Binding Path=DataContext.SecondControlVisible,RelativeSource={RelativeSource AncestorType=Window}}"
          />
        

Note the extra information on the binding to specify the parent.

You may be thinking why you don’t need this more often. My UI was working in early prototypes before I flushed out more functionality. WPF has some binding features that helps you out. If a View’s DataContext is null, the binding infrastructure will use the parent DataContext. Before I built the child ViewModels, everything worked.

I don’t recommend binding to properties in another DataContext as a regular practice, but for the visibility of child controls, it does feel very natural. Just rememeber to set the DataContext to the right relative source.

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.