问题


Windows 10 Mobile上的文件管理器有这样一个进度指示器,它是一个模态框,不带任何按钮,只有进度条和文本:

解决方法


今天我琢磨了很久,绕了一些弯路,发现其实要做一个这样的模态进度指示器很简单。只要用ContentDialog就行。

平时我们用ContentDialog有些思维定势,就觉得这个对话框一定要提供按钮给用户操作。其实它是可以不设置任何按钮的,这样就有了初步的雏形:

XAML

<ContentDialog x:Name="ModalProgressDig">
    <ContentControl.Content>
        <StackPanel Margin="0,10,0,10">
            <ProgressBar IsIndeterminate="True" />
            <TextBlock Text="坐和放宽" TextAlignment="Center" Margin="0,10,0,0" />
        </StackPanel>
    </ContentControl.Content>
</ContentDialog>
<Button x:Name="BtnSitAndRelax" Content="坐和放宽" Click="BtnSitAndRelax_OnClick" />

C#

private async void BtnSitAndRelax_OnClick(object sender, RoutedEventArgs e)
{
    // show progress
    await ModalProgressDig.ShowAsync();
}

之后有2个问题要解决。

1. 如何在操作完成后关闭这个对话框

以前是通过用户点击按钮后主动关闭对话框的,就像这样

var result = await ModalProgressDig.ShowAsync();
if(result == ...){ // 这时候对话框已经被用户关闭了 }

现在没有了按钮,我们要从代码里关闭这个对话框。

关闭对话框的代码是:

ModalProgressDig.Hide();

但是不能这样做:

private async void BtnSitAndRelax_OnClick(object sender, RoutedEventArgs e)
{
    // show progress
    await ModalProgressDig.ShowAsync();
    
    // do some logic
    await Task.Delay(5000);
    
    // hide progress
    ModalProgressDig.Hide();
}

因为你会卡在第一个await上。得等第一个await结束(即用户主动关闭对话框)才会执行后面的逻辑。

所以要用一个trick:

private async void BtnSitAndRelax_OnClick(object sender, RoutedEventArgs e)
{
    // show progress
    var progressTask = ModalProgressDig.ShowAsync();
    
    // do some logic
    await Task.Delay(5000);
    
    // hide progress
    progressTask.Cancel();
    ModalProgressDig.Hide();
}

现在操作完成后就会自动关闭对话框了。但是我不知道这样写代码会不会有什么问题。

2. 对话框的样式修改


以前我们要修改控件的默认样式可以在Blend里右键拷出默认模板编辑,结果这个ContentDialog居然拷不出来。所以要在MSDN上找一个默认模板来改:

https://msdn.microsoft.com/en-us/library/windows/apps/mt299120.aspx

我们主要改的就是对话框的高度。从上面的截图对比发现我们自己做的对话框高度明显太高了。

所以,在这个默认模板里找到:

<Setter Property="MaxHeight" Value="{ThemeResource ContentDialogMaxHeight}" />
<Setter Property="MinHeight" Value="{ThemeResource ContentDialogMinHeight}" />

和Border上的

MaxHeight="{TemplateBinding MaxHeight}"
MinHeight="{TemplateBinding MinHeight}" 

把这对代码删了就好。

修改后的样式如下(我个人喜好,把Border改成2px了,这个改不改无所谓):

<Style TargetType="ContentDialog" x:Key="SitAndRelaxDialogStyle">
            <Setter Property="Foreground" Value="{ThemeResource SystemControlPageTextBaseHighBrush}" />
            <Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Top" />
            <Setter Property="IsTabStop" Value="False" />
            <Setter Property="MaxWidth" Value="{ThemeResource ContentDialogMaxWidth}" />
            <Setter Property="MinWidth" Value="{ThemeResource ContentDialogMinWidth}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ContentDialog">
                        <Border x:Name="Container">
                            <Grid x:Name="LayoutRoot">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Border x:Name="BackgroundElement"
                    Background="{TemplateBinding Background}"
                    FlowDirection="{TemplateBinding FlowDirection}"
                    BorderThickness="2"
                    BorderBrush="{ThemeResource SystemControlForegroundAccentBrush}"
                    MaxWidth="{TemplateBinding MaxWidth}"
                    MinWidth="{TemplateBinding MinWidth}"
                    >
                                    <Grid x:Name="DialogSpace" VerticalAlignment="Stretch">
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto" />
                                            <RowDefinition Height="*" />
                                            <RowDefinition Height="Auto" />
                                        </Grid.RowDefinitions>
                                        <ScrollViewer x:Name="ContentScrollViewer"
                        HorizontalScrollBarVisibility="Disabled"
                        VerticalScrollBarVisibility="Disabled"
                        ZoomMode="Disabled"
                        Margin="{ThemeResource ContentDialogContentScrollViewerMargin}"
                        IsTabStop="False">
                                            <Grid>
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="Auto" />
                                                    <RowDefinition Height="Auto" />
                                                </Grid.RowDefinitions>
                                                <ContentControl x:Name="Title"
                        Margin="{ThemeResource ContentDialogTitleMargin}"
                        Content="{TemplateBinding Title}"
                        ContentTemplate="{TemplateBinding TitleTemplate}"
                        FontSize="20"
                        FontFamily="XamlAutoFontFamily"
                        FontWeight="Normal"
                        Foreground="{TemplateBinding Foreground}"
                        HorizontalAlignment="Left"
                        VerticalAlignment="Top"
                        IsTabStop="False"
                        MaxHeight="{ThemeResource ContentDialogTitleMaxHeight}" >
                                                    <ContentControl.Template>
                                                        <ControlTemplate TargetType="ContentControl">
                                                            <ContentPresenter
                              Content="{TemplateBinding Content}"
                              MaxLines="2"
                              TextWrapping="Wrap"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              Margin="{TemplateBinding Padding}"
                              ContentTransitions="{TemplateBinding ContentTransitions}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                                                        </ControlTemplate>
                                                    </ContentControl.Template>
                                                </ContentControl>
                                                <ContentPresenter x:Name="Content"
                        ContentTemplate="{TemplateBinding ContentTemplate}"
                        Content="{TemplateBinding Content}"
                        FontSize="{ThemeResource ControlContentThemeFontSize}"
                        FontFamily="{ThemeResource ContentControlThemeFontFamily}"
                        Margin="{ThemeResource ContentDialogContentMargin}"
                        Foreground="{TemplateBinding Foreground}"
                        Grid.Row="1"
                        TextWrapping="Wrap" />
                                            </Grid>
                                        </ScrollViewer>
                                        <Grid x:Name="CommandSpace" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition/>
                                                <ColumnDefinition/>
                                            </Grid.ColumnDefinitions>
                                            <Border x:Name="Button1Host"
                      Margin="{ThemeResource ContentDialogButton1HostMargin}"
                      MinWidth="{ThemeResource ContentDialogButtonMinWidth}"
                      MaxWidth="{ThemeResource ContentDialogButtonMaxWidth}"
                      Height="{ThemeResource ContentDialogButtonHeight}"
                      HorizontalAlignment="Stretch" />
                                            <Border x:Name="Button2Host"
                      Margin="{ThemeResource ContentDialogButton2HostMargin}"
                      MinWidth="{ThemeResource ContentDialogButtonMinWidth}"
                      MaxWidth="{ThemeResource ContentDialogButtonMaxWidth}"
                      Height="{ThemeResource ContentDialogButtonHeight}"
                      Grid.Column="1"
                      HorizontalAlignment="Stretch" />
                                        </Grid>
                                    </Grid>
                                </Border>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

然后在我们的对话框上引用这个样式:

<ContentDialog x:Name="ModalProgressDig" Style="{StaticResource SitAndRelaxDialogStyle}">

大功告成: