MVVM / PRISM:对话框字符串应该存储在哪里?
MVVM / PRISM: Where should dialog box strings be stored?
我正在使用 PRISM 和 MVVM 构建应用程序。我有一个视图模型需要向用户显示一个 non-modal 对话框,指示操作正在进行中。我本质上使用的是抽象的 IDialogService。
我的问题是:我应该在哪里存储标题和此对话框中显示的消息的字符串?视图模型的逻辑导致显示对话框并确定何时关闭它。因此,我的视图模型中的代码如下所示:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
"Opening File",
"Please wait while your selected file is opened.") |> Async.AwaitTask
我在考虑本地化方案。 WPF 有自己的机制来通过资源字典等提供本地化。这些字符串似乎属于资源字典,但视图模型不应该依赖于 WPF 资源目录——尤其是因为相同的视图模型将被稍后用于 Xamarin Forms 应用程序。
想到的最佳解决方案是使用将资源库抽象出来的服务(例如 IDialogStringService),但我想知道是否有更好或更优选的方法?
您不应使用资源字典 (xaml) 来存储文本。相反,您必须使用资源 (*.resx)。在对比中:
- 右键单击项目
- 添加 -> 新项目...
- 找到“资源文件”模板,键入名称,然后单击添加
- 选择。打开此文件(将打开特殊编辑器)并在顶部栏将访问修饰符切换为 Public,如果你想从另一个项目或 XAML 访问文本。添加一些 key\value 个字符串。
- 右键单击资源文件并单击运行自定义工具。新 class 将生成静态属性,其名称基于步骤 4 中的密钥。
如何使用(如果文件有名称 Localizations.resx 并且有带键“AppTitle”的字符串)
来自代码:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
Localizations.AppTitle,
"Please wait while your selected file is opened.") |> Async.AwaitTask
来自xaml:
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{x:Static Localizations.AppTitle}"/>
*.resx 文件和生成的 *.cs 文件都不依赖于任何 WPF 程序集,因此您可以在不同的程序集中使用它们:共享视图模型、wpf 视图和 xamarin 视图。只需将 *.resx 文件放在单独的 netstandard 程序集中,然后在需要的地方引用它
这种方式的缺点:
- resx 使用字符串生成 class,每个字符串是 public 属性,因此静态代码分析有效
- 您没有添加新的抽象级别
- 您可以从代码文件或 XAML
中引用字符串
我喜欢 Vadim 的回答,我以前也用过这种方法。如果我的视图模型与 WPF 项目位于同一个项目中,那将是最好的解决方案。
但是,我的视图模型位于不同的库(和不同的语言)中,并将在 Prism MVVM WPF 项目和 Prism MVVM Xamarin Forms 项目之间共享。我仍然可以使用视图模型库中的资源,但是本地化问题将分别存在于 WPF 项目(对于视图)和视图模型库中。 IMO 本地化问题应该集中。
因此,我决定将服务背后的资源抽象化。结果证明,实施资源服务比我想象的要简单。为了直观地使用索引器,我定义了一个由 IResourceService return 编辑的“资源容器对象”,如下所示:
public struct ResourceContainer
{
private readonly Func<string, string> _resourceGetter;
public string this[string resourceId] => _resourceGetter(resourceId);
public ResourceContainer(Func<string, string> resourceGetter) => _resourceGetter = resourceGetter;
}
public interface IResourceService
{
ResourceContainer Resources { get; }
}
而WPF库中的服务实现如下:
public class ResourceService : IResourceService
{
public ResourceService()
{
Resources = new ResourceContainer((s) => Application.Current.Resources[s] as string);
}
public ResourceContainer Resources { get; }
}
在WPF层的XAML资源目录中:
<s:String x:Key="FileOpenDialogTitle">Opening File</s:String>
<s:String x:Key="FileOpenDialogMessage">Please wait while your selected file is opened.</s:String>
最后,视图模型通过在其构造函数上请求 IResourceService 来使用此服务,使用方式如下:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
resourceSvc.Resources.["FileOpenDialogTitle"],
resourceSvc.Resources.["FileOpenDialogMessage"]) |> Async.AwaitTask
这种方法最终将需要实施两次资源 - 一次用于 WPF 项目,一次用于 XF 项目,但无论如何我必须实施两次视图。至少本地化问题在这两种情况下都是集中的(或者也许可以在两个项目之间使用共享资源库)。
编辑:该技术还可以通过将本地化资源 (.resx) 也放入 WPF 项目中来利用 Vadim 的建议,并且让 XAML 资源目录引用静态资源,或者让 ResourceService return直接资源。拥有 .resx 格式的资源可能会更直接地在多个项目之间共享它们。
我正在使用 PRISM 和 MVVM 构建应用程序。我有一个视图模型需要向用户显示一个 non-modal 对话框,指示操作正在进行中。我本质上使用的是抽象的 IDialogService。
我的问题是:我应该在哪里存储标题和此对话框中显示的消息的字符串?视图模型的逻辑导致显示对话框并确定何时关闭它。因此,我的视图模型中的代码如下所示:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
"Opening File",
"Please wait while your selected file is opened.") |> Async.AwaitTask
我在考虑本地化方案。 WPF 有自己的机制来通过资源字典等提供本地化。这些字符串似乎属于资源字典,但视图模型不应该依赖于 WPF 资源目录——尤其是因为相同的视图模型将被稍后用于 Xamarin Forms 应用程序。
想到的最佳解决方案是使用将资源库抽象出来的服务(例如 IDialogStringService),但我想知道是否有更好或更优选的方法?
您不应使用资源字典 (xaml) 来存储文本。相反,您必须使用资源 (*.resx)。在对比中:
- 右键单击项目
- 添加 -> 新项目...
- 找到“资源文件”模板,键入名称,然后单击添加
- 选择。打开此文件(将打开特殊编辑器)并在顶部栏将访问修饰符切换为 Public,如果你想从另一个项目或 XAML 访问文本。添加一些 key\value 个字符串。
- 右键单击资源文件并单击运行自定义工具。新 class 将生成静态属性,其名称基于步骤 4 中的密钥。
如何使用(如果文件有名称 Localizations.resx 并且有带键“AppTitle”的字符串)
来自代码:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
Localizations.AppTitle,
"Please wait while your selected file is opened.") |> Async.AwaitTask
来自xaml:
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{x:Static Localizations.AppTitle}"/>
*.resx 文件和生成的 *.cs 文件都不依赖于任何 WPF 程序集,因此您可以在不同的程序集中使用它们:共享视图模型、wpf 视图和 xamarin 视图。只需将 *.resx 文件放在单独的 netstandard 程序集中,然后在需要的地方引用它
这种方式的缺点:
- resx 使用字符串生成 class,每个字符串是 public 属性,因此静态代码分析有效
- 您没有添加新的抽象级别
- 您可以从代码文件或 XAML 中引用字符串
我喜欢 Vadim 的回答,我以前也用过这种方法。如果我的视图模型与 WPF 项目位于同一个项目中,那将是最好的解决方案。
但是,我的视图模型位于不同的库(和不同的语言)中,并将在 Prism MVVM WPF 项目和 Prism MVVM Xamarin Forms 项目之间共享。我仍然可以使用视图模型库中的资源,但是本地化问题将分别存在于 WPF 项目(对于视图)和视图模型库中。 IMO 本地化问题应该集中。
因此,我决定将服务背后的资源抽象化。结果证明,实施资源服务比我想象的要简单。为了直观地使用索引器,我定义了一个由 IResourceService return 编辑的“资源容器对象”,如下所示:
public struct ResourceContainer
{
private readonly Func<string, string> _resourceGetter;
public string this[string resourceId] => _resourceGetter(resourceId);
public ResourceContainer(Func<string, string> resourceGetter) => _resourceGetter = resourceGetter;
}
public interface IResourceService
{
ResourceContainer Resources { get; }
}
而WPF库中的服务实现如下:
public class ResourceService : IResourceService
{
public ResourceService()
{
Resources = new ResourceContainer((s) => Application.Current.Resources[s] as string);
}
public ResourceContainer Resources { get; }
}
在WPF层的XAML资源目录中:
<s:String x:Key="FileOpenDialogTitle">Opening File</s:String>
<s:String x:Key="FileOpenDialogMessage">Please wait while your selected file is opened.</s:String>
最后,视图模型通过在其构造函数上请求 IResourceService 来使用此服务,使用方式如下:
let! closeDlgAction =
dialogSvc.ShowDialogModeless (
resourceSvc.Resources.["FileOpenDialogTitle"],
resourceSvc.Resources.["FileOpenDialogMessage"]) |> Async.AwaitTask
这种方法最终将需要实施两次资源 - 一次用于 WPF 项目,一次用于 XF 项目,但无论如何我必须实施两次视图。至少本地化问题在这两种情况下都是集中的(或者也许可以在两个项目之间使用共享资源库)。
编辑:该技术还可以通过将本地化资源 (.resx) 也放入 WPF 项目中来利用 Vadim 的建议,并且让 XAML 资源目录引用静态资源,或者让 ResourceService return直接资源。拥有 .resx 格式的资源可能会更直接地在多个项目之间共享它们。