我的《上海轨道交通》应用里有个允许用户报告错误的页面,会把当前视图(xaml页面名称)、页面摘要、用户反馈的内容以及设备的软硬件信息生成邮件发给应用作者,界面如下:

生成的邮件如下:

问题描述:测试  (程序版本:3.2.0.0, 所在页面:StationDetail, 页面摘要:宜山路, 设备名:ISAAC, 操作系统:WINDOWS, SKU:Surface_Pro_3, 产品名称:Surface Pro 3, 制造商:Microsoft Corporation, 固件版本:, 硬件版本:)

要实现这样的报错页面非常容易

1.获取软硬件信息

我们要用的类是:

Windows.Security.ExchangeActiveSyncProvisioning.EasClientDeviceInformation

比如获得设备名称,就可以直接:

var deviceInfo = new EasClientDeviceInformation();
string deviceName = deviceInfo.FriendlyName;

于是。获取全部的软硬件信息字符串就可以这样写:

$", 设备名:{deviceInfo.FriendlyName}, " +
$"操作系统:{deviceInfo.OperatingSystem}, " +
$"SKU:{deviceInfo.SystemSku}, " +
$"产品名称:{deviceInfo.SystemProductName}, " +
$"制造商:{deviceInfo.SystemManufacturer}, " +
$"固件版本:{deviceInfo.SystemFirmwareVersion}, " +
$"硬件版本:{deviceInfo.SystemHardwareVersion})";

2.发送邮件

如果安装了我的Edi.UWP.Helpers助手类库(GitHub https://github.com/EdiWang/UWP-Helpers),那么在UWP中发送邮件非常简单:

await Tasks.OpenEmailComposeAsync(to, subject, body);

3.获得程序版本号

还是一样,如果安装了我的Edi.UWP.Helpers,那么就可以:

Utils.GetAppVersion();

综上所述,就可以写一个报告错误的方法:

public static async Task ReportError(string viewName, string msg = null, string pageSummary = "N/A", bool includeDeviceInfo =
true)
{
    var deviceInfo = new EasClientDeviceInformation();

    string subject = "《上海轨道交通》Windows Universal应用错误报告";
    string body = $"问题描述:{msg}  " +
                  $"(程序版本:{Utils.GetAppVersion()}, " +
                  $"所在页面:{viewName}, " +
                  $"页面摘要:{pageSummary}";

    if (includeDeviceInfo)
    {
        body +=   $", 设备名:{deviceInfo.FriendlyName}, " +
                  $"操作系统:{deviceInfo.OperatingSystem}, " +
                  $"SKU:{deviceInfo.SystemSku}, " +
                  $"产品名称:{deviceInfo.SystemProductName}, " +
                  $"制造商:{deviceInfo.SystemManufacturer}, " +
                  $"固件版本:{deviceInfo.SystemFirmwareVersion}, " +
                  $"硬件版本:{deviceInfo.SystemHardwareVersion})";
    }
    else
    {
        body += ")";
    }
                  
    string to = "Edi.Wang@outlook.com";
    await Tasks.OpenEmailComposeAsync(to, subject, body);
}

考虑到隐私协议,建议给用户提供一个是否发送设备信息的选项。即“includeDeviceInfo”

4. XAML

视图名称和页面摘要设置为只读,只给用户填写错误描述。

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Style="{StaticResource LayoutGridStyle}">
    <Grid.Resources>
        <Style TargetType="TextBox">
            <Setter Property="Margin" Value="0,0,0,10" />
        </Style>
    </Grid.Resources>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" Style="{StaticResource HeaderStackPanelStyle}">
        <TextBlock Text="上海轨道交通" Style="{StaticResource SmallTitleTextStyle}"/>
        <TextBlock Text="错误报告" Style="{StaticResource MainTitleTextStyle}" />
    </StackPanel>

    <StackPanel Grid.Row="1">
        <TextBox Header="视图名称" IsReadOnly="True" Text="{Binding ViewName}" Foreground="Gray" />
        <TextBox Header="页面摘要" IsReadOnly="True" Text="{Binding PageSummary}" Foreground="Gray" />
        <TextBox Header="错误描述" PlaceholderText="输入你遇到的问题" Text="{Binding Message, Mode=TwoWay}" AcceptsReturn="True" TextWrapping="Wrap" Height="200" />
        <CheckBox Content="发送我的设备信息" IsChecked="{Binding IncludeDeviceInfo, Mode=TwoWay}" />
        <TextBlock Text="* 错误报告将用你的个人邮箱直接发送给APP作者,发送前请检查是否包含敏感信息" TextWrapping="Wrap" Foreground="Gray" />
    </StackPanel>
</Grid>

还有个按钮

<Page.BottomAppBar>
    <CommandBar Style="{StaticResource DefaultCommandBarStyle}">
        <AppBarButton Icon="Accept" Label="发送反馈"
Command="{Binding CommandReportError}" />
    </CommandBar>
</Page.BottomAppBar>

5. ViewModel

public class ReportErrorViewModel : ViewModelBase
{
    private bool _includeDeviceInfo;
    private string _viewName;
    private string _pageSummary;
    private string _message;

    public bool IncludeDeviceInfo
    {
        get { return _includeDeviceInfo; }
        set { _includeDeviceInfo = value; RaisePropertyChanged(); }
    }

    public string ViewName
    {
        get { return _viewName; }
        set { _viewName = value; RaisePropertyChanged(); }
    }

    public string PageSummary
    {
        get { return _pageSummary; }
        set { _pageSummary = value; RaisePropertyChanged(); }
    }

    public string Message
    {
        get { return _message; }
        set { _message = value; RaisePropertyChanged(); }
    }

    public RelayCommand CommandReportError { get; set; }

    public ReportErrorViewModel()
    {
        IncludeDeviceInfo = true;
        CommandReportError = new RelayCommand(async ()=> await ReportError());
    }

    public void InitData(ReportErrorModel model)
    {
        ViewName = model.ViewName;
        PageSummary = model.PageSummary;
    }

    private async Task ReportError()
    {
        await Helper.ReportError(ViewName, Message, PageSummary, IncludeDeviceInfo);
    }
}

6. 现在,就可以在任何一个xaml页面里调用报错页面了

<AppBarButton x:Name="BtnReportError" Label="报告错误" Click="BtnReportError_OnClick" />
private async void BtnReportError_OnClick(object sender, RoutedEventArgs e)
{
    var vm = DataContext as StationFloorMapViewModel;

    if (null != vm)
    {
        await Helper.ReportError($"StationFloorMapView - {vm.CurrentStation.StationName}");
    }
}