Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decouple the screens without magic strings

My WPF project will be organised like this :

Screens
   Group1
      Screen1
         View.xaml
         ViewModel.cs
   Group2
      Screen2
         View.xaml
         ViewModel.cs

To show the Screen1 from the Screen2 I'll use something like this: ScreenManager.Show("Group1.Screen1") This looks (using reflection) in the Screens.Group1.Screen1 namespace for a View and a ViewModel and instantiates them.

How can I eliminate the magic string without coupling Screen1 and Screen2 (I don't want the classes in Screen2 to use the Screen1 namespace). Also I would like some kind of screen discovery (autocompletion/intellisense)

Or maybe some way (automate test) to verify that all calls to ScreenManager.Show are valid.

Update : I came up with this:

public class ScreenNames
{
    public Group1Screens Group1;

    public class Group1Screens
    {
        public ScreenName Screen1;
    }
}

public sealed class ScreenName
{
    private ScreenName() { }
}

public class ScreenManager : IScreenManager
{
    public void Show(Expression<Func<ScreenNames, ScreenName>> x) {}
}

Usage:

screenManager.Show(x=>x.Group1.Screen1);

Not ideal but I suppose violating DRY is still better than magic strings. And I can automatically test (with reflection) that all calls are valid.

like image 974
Catalin DICU Avatar asked Feb 10 '10 20:02

Catalin DICU


1 Answers

You don't need all that ScreenManager stuff in WPF, because the DataTemplate engine can take care of this for you with pure markup.

You can simply databind a particular area of your application with a ContentPresenter and a bunch of DataTemplates. Bind the area to a property of a 'root' ViewModel, and let the 'root' ViewModel implement INotifyPropertyChanged so that WPF knows if you change the ViewModel in that area.

public class RootViewModel : INotifyPropertyChanged
{
    public object Screen1ViewModel { get; }

    public object Screen2ViewModel { get; }
}

Databind one ContentPresenter control to the Screen1ViewModel property using

<ContentControl Content="{Binding Path=Screen1ViewModel}" />

and similarly for the next one. When you need to change the content of Screen1, you simply re-assign Screen1ViewModel from code, and because of the raised PropertyChanged event, WPF will pick it up and bind the new ViewModel to a new View.

The DataTemplates may be as simple as this:

<Window.Resources>
    <DataTemplate DataType="{x:Type foo:MyViewModel}">
        <self:MyControl />
    </DataTemplate>
    <DataTemplate DataType="{x:Type foo:MyOtherViewModel}">
        <self:MyOtherControl />
    </DataTemplate>
</Window.Resources>

In case you are not familiar with it, this article on MVVM in WPF is an excellent introduction.

like image 200
Mark Seemann Avatar answered Oct 29 '22 18:10

Mark Seemann