基于ViewModel状态

时间:2017-02-06 17:46:58

标签: c# wpf mvvm localization

我有一个WPF应用程序,我希望在其中显示"友好"如果ViewModel状态指示发生错误,则显示详细错误描述。所以,作为一个非常简单的第一遍,我有:

XAML

<Label>
  <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Path=ErrorMessage}" />
</Label>

视图模型

public string ErrorMessage { get; set; }

private void DoSomething()
{
    try { /* ... */ }
    catch (Exception1) { ErrorMessage = "Long description 1"; }
    catch (Exception2) { ErrorMessage = "Long description 2"; }
    catch (Exception3) { ErrorMessage = "Long description 3"; }
}

(注意,PropertyChanged.Fody用于自动为上面的ViewModel属性实现INotifyPropertyChangedDoSomething()通过RelayCommand触发。)

这很好用,但当然我不想在ViewModel代码中使用那些长描述字符串。所以我开始阅读WPF本地化,ResourceDictionary等等,我很快就完全迷失了。

在我看来,如果我可以这样做,那将是非常理想的:

XAML(假设)

<Label>
  <TextBlock TextWrapping="WrapWithOverflow" Text="{Binding Path=Error, Converter?=???}" />
</Label>

ViewModel(假设)

public enum Errors { None, Error1, Error2, Error3 };
public Errors Error get; set; }

private void DoSomething()
{
    try { /* ... */ }
    catch (Exception1) { Error = Errors.Error1; }
    catch (Exception2) { Error = Errors.Error2; }
    catch (Exception3) { Error = Errors.Error3; }
}

某处? (完全假设)

<Strings>
    <String Key="MyViewModel.Errors.Error1">
        Long description 1
    </String>
    <!-- etc. -->
</Strings>

现在,上面的内容完全是虚构的,从我读过的内容来看,我认为ResourceDictionary 可能是存储字符串的地方。但我无法弄清楚如何将字符串与可能的错误条件的枚举相关联。当然它也不一定是enum - 我可以在Error字段中存储字符串键或其他内容 - 这对我来说似乎是最理想的

我知道我可以将字符串资源放在ResourceDictionary中,然后在ViewModel代码中使用Application.Current.Resources来查找字符串并使用它来填充ErrorMessage。但我认为这很糟糕 - ViewModel不应该知道或关心实际的消息文本,这是一个View关注点。所以在我看来,View应该找出基于ViewModel状态渲染的相应字符串。

理想情况下,字符串在未来也应该是可本地化的,虽然我目前没有本地化应用程序(我对WPF本地化知之甚少,并且此应用程序不需要它)

问题是,您是否可以指出我在WPF最佳做法方面的正确方向,以显示上述示例中的关键信息?我不介意做腿部工作以了解我需要学习的内容,但是当我在网上阅读这篇文章时,我很快就完全迷失了,只需要一些指导。谢谢!

2 个答案:

答案 0 :(得分:2)

对于视图模型来说,使用TextBlock元素公开视图绑定的字符串值是完全正常的。如果您不想将字符串值硬编码到视图模型类中,则可以将字符串存储在单独的类中:

查看型号:

private void DoSomething()
{
    try { /* ... */ }
    catch (Exception1) { ErrorMessage = StringResources.ErrorMessageA; }
    catch (Exception2) { ErrorMessage = StringResources.ErrorMessageB; }
}

<强> StringResources.cs:

public static class StringResources
{
    public const string ErrorMessageA = "...";
    public const string ErrorMessagB = "...";
}

如果由于某种原因你不喜欢这样,你仍然可以暴露诸如&#34; Error1&#34;和&#34;错误2&#34;从视图模型中,然后使用转换器或自定义标记扩展将这些转换为视图中的实际字符串值。有关更多信息,请参阅以下链接:

https://www.codeproject.com/articles/35159/wpf-localization-using-resx-files

https://wpftutorial.net/LocalizeMarkupExtension.html

Recommendation on a XAML RESX markup extension

答案 1 :(得分:1)

根据提供的示例,这是另一种选择:

可能(有很多的方法),在Style上使用TextBlock并设置其{{} 1}}根据Text上的错误,如下所示:

枚举:

ViewModel

(无在C#中不是关键字)

查看型号:

public enum Error
{
    None,
    Error1,
    Error2,
    Error3
}

XAML示例

public Error CurrentError { get; set; }

PS:如果<TextBlock> <TextBlock.Style> <Style TargetType="TextBlock"> <Style.Triggers> <DataTrigger Binding="{Binding CurrentError}" Value="{x:Static local:Error.None}"> <Setter Property="Text" Value="No error" /> </DataTrigger> <DataTrigger Binding="{Binding CurrentError}" Value="{x:Static local:Error.Error1}"> <Setter Property="Text" Value="Long description 1" /> </DataTrigger> <DataTrigger Binding="{Binding CurrentError}" Value="{x:Static local:Error.Error2}"> <Setter Property="Text" Value="Long description 2" /> </DataTrigger> <DataTrigger Binding="{Binding CurrentError}" Value="{x:Static local:Error.Error3}"> <Setter Property="Text" Value="Long description 3" /> </DataTrigger> </Style.Triggers> </Style> </TextBlock.Style> </TextBlock> enum / namespace /不在同一Window之内(并且可能不应该&#) 39; t),你必须更换&#34; Page&#34;使用适当的命名空间。

通过这种方式,您基本上可以使用local:执行所有操作(例如,在没有错误时更改其TextBlock,更改其Visibility等)。