WPF/MVVM Tabbed Design & Avoiding Binding Errors

143 views Asked by At

This is a "best-practice design" question for how to structure an MVVM/WPF application.

Envision something like Photoshop, where you have an editor that can open multiple (tabbed) documents & a toolbox. Each tool simply sets a property of the currently-active doc (like turning on filters & setting filter parameters). I'm using Actipro's Docking & MDI framework for the tabbed interface.

There are two types of documents supported: call them DocSimple & DocComplex, where DocComplex offers all the controllable properties of DocSimple, plus some additional ones.

What I've done right now:

  • MainWindow.xaml & MainWindowViewModel for the overall application (contains the docking container & the toolbox/tool controls)
  • MainWindowViewModel has an ActiveDocViewModel property, which points to the DocSimpleViewModel or DocComplexViewModel instance of the currently-active document tab (or "null" when there's no document tab opened).
  • The various controls in the MainWindow's toolbox are bound to ActiveDocViewModel's various properties (i.e. {Binding Path=ActiveDocViewModel.SomeProperty}).

Question 1: Generally, does this seem like a reasonable approach? This is my first MVVM application (coming from WinForms), & although everything is working properly, I had some doubt about whether it's advisable to be binding to ActiveDocViewModel.Prop...but it also seemed like the most logical approach, based on the fact that there's "one" MainWindow that needs to bind to "one of many possible" doc tabs.

Question 2: As mentioned, DocComplex offers more properties/options than DocSimple. I've handled this by inheriting DocComplexViewModel from DocSimpleViewModel, & creating a property in MainWindowViewModel like:

public bool IsComplexToolsVisible
{
    get
    {
        return(ActiveDocViewModel != null && ActiveDocViewModel is DocComplexViewModel);
    }
}

This is bound to the "Visibility" of that section of the toolbox. Again, it works as intended - but it also yields "BindingExpression path error: 'xxx' property not found" errors for every property in the Complex Tools section, whenever we open a DocSimple. It'd be easy enough to fix these by adding 'stub' properties to DocSimple, but that felt obviously wrong (what would be the point of using inheritance to add "complex" functionality to a "simple" base type when I just end up adding stubs for everything in the base class anyway?). So the presence of these binding errors causes me to question if this approach is flawed - and how I could handle the Complex vs Simple scenario while avoiding all the binding errors.

Any insights would be greatly appreciated :)

1

There are 1 answers

0
Frerk Morrin On BEST ANSWER

At first, the general structure looks very good in my opinion, but I'm not an expert and don't want to rate something like that.


Then for you problem with the binding errors: You could use a TemplateSelector, which gives a template with the "simple toolbox" for the DocSimple type and a different template, which contains the simple toolbox plus the additional parts for the DocComplex.