在没有魔术字符串的情况下将屏幕去耦

时间:2010-02-10 20:00:02

标签: c# wpf architecture mvvm magic-string

我的WPF项目将按如下方式组织:

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

要显示Screen1中的Screen2,我将使用以下内容:ScreenManager.Show("Group1.Screen1")这会在Screens.Group1.Screen1命名空间中查看(使用反射)以查看视图和ViewModel并实例化它们。

如何在不耦合Screen1Screen2的情况下消除魔术字符串(我不希望Screen2中的类使用Screen1命名空间)。我也想要某种屏幕发现(自动完成/智能感知)

或者某种方式(自动化测试)来验证对ScreenManager.Show的所有调用是否有效。

更新 我想出了这个:

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) {}
}

用法:

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

不理想,但我认为违反DRY仍然比魔术弦更好。我可以自动测试(通过反射)所有调用都是有效的。

2 个答案:

答案 0 :(得分:3)

您不需要WPF中的所有ScreenManager内容,因为DataTemplate引擎可以为您提供纯标记。

您可以使用ContentPresenter和一堆DataTemplates简单地对应用程序的特定区域进行数据绑定。将该区域绑定到“root”ViewModel的属性,并让“root”ViewModel实现INotifyPropertyChanged,以便WPF知道您是否更改了该区域中的ViewModel。

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

    public object Screen2ViewModel { get; }
}

使用

将一个ContentPresenter控件数据绑定到Screen1ViewModel属性
<ContentControl Content="{Binding Path=Screen1ViewModel}" />

并且类似于下一个。当您需要更改Screen1的内容时,只需从代码中重新分配Screen1ViewModel,并且由于引发的PropertyChanged事件,WPF将拾取它并将新ViewModel绑定到新视图。

DataTemplates可能就像这样简单:

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

如果您不熟悉它,this article on MVVM in WPF是一个很好的介绍。

答案 1 :(得分:0)

最后,我使用T4代码生成来生成我的ScreenNames类。我通过调整此代码来做到这一点:Auto generate strong typed navigation class for all user controls in ASP.NET web application