Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
600 views
in Technique[技术] by (71.8m points)

mvvm - UWP ContentDialog Invocation

I am using UWP and Template 10 to build a GUI app by following MVVM pattern. As a part of application I need to invoke a content dialog by pressing button on Main Page. So separate ContentDialog was created in standalone .xaml file for that purpose:

<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>

What is the proper way to open/invoke it in my Main Page by pressing button (as I need to keep logic separated for view and viewmodel)?

How I do it now:

From Main Page I invoke DictateCommand, which in turn creates an instance of ContentDialog and shows it:

 <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();
        }

It looks like a MVVM pattern breach for me. Could you please help me to do it in a right way?

EDIT:

I have implemented dialog service and injected it in the main view model. However, I got another obstacle. For this dialog I created separate view model and property which encapsulates dialog text box value. When I press 'accept' button on the dialog - I need this value to be reflected on my Main View. So I need to pass dialog's text box value from dialog's view model to main view model. Should I perform another dependency injection to deal with it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have four options.

ONE The first is a service, just like @Ask-too-much explains. In fact, this is a beautiful solution if you like it.

The benefits of the first solution are that it is reusable. If you are not reusing this UI, a dedicated service might be overkill, to be honest.

TWO The second is a view-model event. That is to say, your Page can subscribe to your view-model's event (let's call it ShowContentDialog) and when it is raised by the view-model, your Page handle its presentation.

The benefits this approach is that, just like the first approach, you are offloading the effort to another class. In this case, you are creating what is likely a one-off solution without needing a service, a service interface, or injection of that service somehow. If you are not waiting for the result of the event, then I think this is idea for 99% of issues like yours.

THREE The third approach is to use a different control that can be bound to a property. For example, since you are already using Template 10, you can use the ModalDialog control which has an IsModal property. A property in your view-model (let's call it IsModalVisible) can be used to control the dialog without coupling to it.

The good part of this is that you get to invoke the dialog from the logic of your view-model, just like the first two approaches. But unlike the first, you do not need a service. Unlike the second, you do not need a handler. It's the most "data binding" way to do it, and likely what I would do.

FOUR The fourth way to do it is to use messaging. Messaging is the mechanism one view-model uses to communicate with another. IN this case you could use messaging from your view-model (with a message we might call ShowDialog) that is listened for, not in another view-model, but in your Page. This would work fine, too.

The down side to this is that you need a messaging solution, but you might already have that. The up-side is that the logic for handling the visual could be relocated at any time as Messaging is multicasted to anyone listening.

If I were you, I would consider number 3 first, perhaps. Without a little more understanding of your app scenario, I can't be sure. You are a developer, though. All four of those options are good. Just be sure not to be tempted into passing the UIElement into your view-model. That's needless nastiness :)

Best of luck!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...