7

我正在使用 UWP 和模板 10 按照 MVVM 模式构建 GUI 应用程序。作为应用程序的一部分,我需要通过按下主页上的按钮来调用内容对话框。因此,为此目的在独立的 .xaml 文件中创建了单独的 ContentDialog:

<ContentDialog
    x:Class="UWP1.Views.Speech"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UWP1.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="Dictate"
    PrimaryButtonText="Accept"
    SecondaryButtonText="Cancel"
    PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
    SecondaryButtonClick="ContentDialog_SecondaryButtonClick"
    >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="150" />
        </Grid.ColumnDefinitions>
            <Button Margin="15" Content="Dictate" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch"/>
        <Button  Margin="15" Content="Clear Text" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch"/>
        <TextBlock Grid.Row="1" Grid.ColumnSpan="2" Text="Tap 'Dictate', and speak" FontSize="12" />
            <TextBlock Margin="0 10 0 0" Grid.Row="2" Grid.ColumnSpan="2" Text="Message Dication" HorizontalAlignment="Center" FontSize="24"  />
        <ScrollViewer Grid.Row="3" Grid.ColumnSpan="2" Height="300">
            <TextBox Margin="5 5 5 10"  AcceptsReturn="True"  />
        </ScrollViewer>
    </Grid>
</ContentDialog>

通过按下按钮在我的主页中打开/调用它的正确方法是什么(因为我需要为视图和视图模型保持逻辑分离)?

我现在怎么做:

从主页我调用 DictateCommand,它反过来创建 ContentDialog 的实例并显示它:

 <AppBarButton Grid.Column="1" Icon="Microphone" IsCompact="True" HorizontalAlignment="Right" Command="{Binding DictateCommand}"/>

        public ICommand DictateCommand { get; set; }

        public async void Dictate(object obj)
        {
            var contentDialog = new Speech();
            await contentDialog.ShowAsync();
        }

对我来说,这看起来像是违反了 MVVM 模式。你能帮我以正确的方式做吗?

编辑:

我已经实现了对话服务并将其注入到主视图模型中。然而,我遇到了另一个障碍。对于这个对话框,我创建了单独的视图模型和封装对话框文本框值的属性。当我按下对话框上的“接受”按钮时 - 我需要将此值反映在我的主视图上。所以我需要将对话框的文本框值从对话框的视图模型传递到主视图模型。我应该执行另一个依赖注入来处理它吗?

4

2 回答 2

13

你有四个选择。

一个第一个是服务,就像@Ask-too-much 解释的那样。事实上,如果你喜欢,这是一个很好的解决方案。

第一个解决方案的好处是它是可重复使用的。如果您不重用此 UI,老实说,专用服务可能有点矫枉过正。

第二个是视图模型事件。也就是说,您的 Page 可以订阅您的视图模型的事件(我们称之为 ShowContentDialog),当它被视图模型引发时,您的 Page 处理它的呈现。

这种方法的好处是,就像第一种方法一样,您将工作量转移到另一个类。在这种情况下,您创建的可能是一次性解决方案,而不需要服务、服务接口或以某种方式注入该服务。如果您不等待事件的结果,那么我认为这是解决 99% 的问题的想法。

第三种方法是使用可以绑定到属性的不同控件。例如,由于您已经在使用模板 10,您可以使用具有 IsModal 属性的 ModalDialog 控件。您的视图模型中的一个属性(我们称之为 IsModalVisible)可用于控制对话框而不与它耦合。

这样做的好处是您可以从视图模型的逻辑中调用对话框,就像前两种方法一样。但与第一个不同的是,您不需要服务。与第二个不同,您不需要处理程序。这是最“数据绑定”的方式,并且可能是我会做的。

第四种方法是使用消息传递。消息传递是一个视图模型用来与另一个视图模型进行通信的机制。在这种情况下,您可以使用来自您的视图模型的消息(带有我们可能称为 ShowDialog 的消息),而不是在另一个视图模型中,而是在您的页面中。这也可以。

不利的一面是您需要一个消息传递解决方案,但您可能已经拥有它。好处是处理视觉的逻辑可以随时重新定位,因为消息被多播给任何收听的人。

如果我是你,我可能会首先考虑 3 号。如果对您的应用场景没有更多了解,我无法确定。不过,您是开发人员。这四个选项都很好。请确保不要试图将 UIElement 传递到您的视图模型中。那是不必要的肮脏:)

祝你好运!

于 2016-01-29T19:55:54.133 回答
6

MVVM 中建议的解决方案是不要Speech Dialog直接在 ViewModel 中创建实例,而是创建SpeechDialogService.

public interface ISpeechDialogService
{
    Task ShowAsync();
}

public class SpeechDialogService : ISpeechDialogService
{
    public async Task ShowAsync()
    {
        var contentDialog = new Speech();
        await contentDialog.ShowAsync();

    }
}

ViewModel并在你的构造函数中注入这个服务

public class AbcViewModel
{
    readonly ISpeechDialogService _dialog;

    public AbcViewModel(ISpeechDialogService dialog)
    {
        _dialog = dialog;
    }

    public async void Dictate(object obj)
    {
        await _dialog.ShowAsync();
    }
}
于 2016-01-06T00:28:08.167 回答