【问题标题】:Localizing dialogs in MVVM: is it good to reference the resources from the ViewModel?在 MVVM 中本地化对话框:从 ViewModel 中引用资源是否很好?
【发布时间】:2014-02-28 20:35:45
【问题描述】:

我已经到了需要向我的 WPF MVVM 应用程序添加本地化的地步(我使用 Caliburn.Micro + Autofac)。

我进行了一些研究,并找到了许多不同的方法来完成它,但没有一种方法可以提供本地化对话文本的解决方案。 作为对话框,我使用具有 Caption 和 Message 字符串属性的 DialogViewModel,并使用 CM 的 WindowManager 在 DialogView 中显示它。 我的自动取款机是这样的

this.windowManager.ShowDialog(new DialogViewModel("Hello!", "Hello everybody!!"))

还有类似的东西

this.windowManager.ShowDialog(new DialogViewModel("Hello!", "Hello " + this.Name + "!!"))

我认为我可以使用像 "Hello {0}!!" 这样的资源字符串并以这种方式使用它

this.windowManager.ShowDialog(new DialogViewModel("Hello!", string.Format(languageResources.HelloName, this.Name)))

从 ViewModel 层引用本地化资源好不好?

【问题讨论】:

  • 啊,我写的是假的——太晚了;)我认为你的方法是可行的,没有问题。我正在用 Caliburn 做类似的事情。您可能需要记住,还有可能需要翻译的标题等。
  • 好的,所以从 vm 中引用本地化资源并不是一个坏习惯?

标签: c# wpf mvvm localization resources


【解决方案1】:

Resources 是使用View 的数据,我认为不建议从ViewModel 引用资源。另一方面,如果它是一个存储特定字符串的类(可能是静态的),并且对View 一无所知,那么它将是一些可以在ViewModel 中的抽象。在任何情况下,您都应该尝试使用我将提供的技术或任何其他技术来使用View 方面的资源。

Using x:Static Member

在 WPF 中,可以像这样绑定类中的静态数据:

<x:Static Member="prefix : typeName . staticMemberName" .../>

下面是格式字符串在一个类中的示例,用于显示日期和时间的格式。

XAML

xmlns:local="clr-namespace:YourNameSpace"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

<Grid>
    <TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat={x:Static Member=local:StringFormats.DateFormat}}" 
               HorizontalAlignment="Right" />

    <TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat={x:Static Member=local:StringFormats.Time}}" />
</Grid>

Code behind

public class StringFormats 
{
    public static string DateFormat = "Date: {0:dddd}";

    public static string Time = "Time: {0:HH:mm}";
}   

在这种情况下,StringFormats 类被视为资源,尽管它实际上是一个普通类。更多信息请见x:Static Markup Extension on MSDN

Using Converter

如果你有存储在Application.Current.Resources中的资源,并且需要添加一些逻辑,这种情况下,你可以使用转换器。这个例子取自here

XAML

<Button Content="{Binding ResourceKey, Converter={StaticResource resourceConverter}}" />

Code behind

public class StaticResourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var resourceKey = (string)value;

        // Here you can add logic

        return Application.Current.Resources[resourceKey];
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }
}   

Note: 在转换器中,最好不要使用重逻辑,因为它会影响性能。更复杂的逻辑见下文。

Attached Behavior

当没有x:Static Member 并且转换器没有帮助时,应将附加行为用于具有视觉元素的复杂操作。附加行为是非常强大和方便的解决方案,完全满足 MVVM 模式,也可以在 Blend 中使用(带有预定义的接口)。您可以定义一个附加属性,其中属性处理程序可以访问元素及其资源。

实施附加行为示例,见下文:

Set focus to a usercontrol when it is made visible

Animated (Smooth) scrolling on ScrollViewer

Setting WindowStartupLocation from ResourceDictionary throws XamlParseException

Example with converter

App.xaml

我在这里存储每种文化的字符串。

<Application x:Class="MultiLangConverterHelp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             StartupUri="MainWindow.xaml">

    <Application.Resources>
        <sys:String x:Key="HelloStringEN">Hello in english!</sys:String>
        <sys:String x:Key="HelloStringRU">Привет на русском!</sys:String>
    </Application.Resources>
</Application>

MainWindow.xaml

输入是当前的文化,可以在转换器中获得,为了简单起见,我这样做了。

<Window x:Class="MultiLangConverterHelp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:local="clr-namespace:MultiLangConverterHelp"
        WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <local:StaticResourceConverter x:Key="converter" />
        <local:TestViewModel x:Key="viewModel" />
    </Window.Resources>

    <Grid DataContext="{StaticResource viewModel}">
        <TextBlock Text="{Binding Path=CurrentCulture, Converter={StaticResource converter}}" />
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

public class StaticResourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var currentCulture = (string)value;

        if (currentCulture.Equals("EN-en")) 
        {
            return Application.Current.Resources["HelloStringEN"];
        }
        else if (currentCulture.Equals("RU-ru"))
        {
            return Application.Current.Resources["HelloStringRU"];
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return DependencyProperty.UnsetValue;
    }
}

public class TestViewModel : NotificationObject
{
    private string _currentCulture = "EN-en";

    public string CurrentCulture
    {
        get
        {
            return _currentCulture;
        }

        set
        {
            _currentCulture = value;
            NotifyPropertyChanged("CurrentCulture");
        }
    }
}

另外,我建议你学习更简单的方法,这些方法已经在 WPF 技术中:

WPF Localization for Dummies

WPF Globalization and Localization Overview

【讨论】:

  • 如果你使用这个,你会完全绕过 resx 文件系统吗?
  • @Christian Sauer:我有资源,有存储的文件路径,控件样式,所有这些都存储在ResourceDicitionary,但在ViewModel,我不是指资源的内容。是的,在某种程度上它会帮助您避免引用 ViewModel 中的资源,但不会取消 resx 文件系统。我想,关于资源应该只知道View
  • 我喜欢使用转换器的想法,我在考虑解决方案时也有同样的想法,但我仍然想知道如何使用您提供的解决方案更改对话框的文本,你能请给我看一个小例子?谢谢你的时间
  • @Nikolaev:为了清楚起见,假设我们必须显示对话框“Item has been romved.”,你将如何本地化文本?
  • @Nikolaev:好的,谢谢你的例子,我明白你的意思,但不是假设我必须为每个不同的对话框消息创建一个视图吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 2017-10-24
  • 2011-04-09
  • 2014-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多