无需魔术弦即可解耦屏幕

发布于 2024-08-20 14:34:07 字数 1129 浏览 6 评论 0原文

我的 WPF 项目将这样组织:

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

要从 Screen2 显示 Screen1,我将使用如下内容:ScreenManager.Show("Group1.Screen1" ) 这会在 Screens.Group1.Screen1 命名空间中查找(使用反射)View 和 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 仍然比魔术字符串更好。我可以自动测试(通过反射)所有调用是否有效。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

场罚期间 2024-08-27 14:34:07

您不需要 WPF 中的所有 ScreenManager 内容,因为 DataTemplate 引擎可以通过纯标记为您处理这些事情。

您可以简单地使用 ContentPresenter 和一堆 DataTemplates 对应用程序的特定区域进行数据绑定。将该区域绑定到“根”ViewModel 的属性,并让“根”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 绑定到新的 View。

DataTemplates 可能像这样简单:

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

如果您不熟悉它,这篇关于 WPF 中的 MVVM 的文章是一个很好的介绍。

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.

蘸点软妹酱 2024-08-27 14:34:07

最后,我使用 T4 代码生成来生成我的 ScreenNames 类。我通过调整以下代码来做到这一点: 自动生成强类型ASP.NET Web 应用程序中所有用户控件的导航类

Finally I used T4 code generation to generate my ScreenNames class. I did that by adapting this code : Auto generate strong typed navigation class for all user controls in ASP.NET web application

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文