/// <summary>
        /// 打开文件选取器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void OpenFileButtonAsync_Click(object sender, RoutedEventArgs e)
        {
            FileOpenPicker openPicker = new FileOpenPicker {
                ViewMode = PickerViewMode.Thumbnail,
                SuggestedStartLocation = PickerLocationId.ComputerFolder
            };

            openPicker.FileTypeFilter.Add(".txt");
            openPicker.FileTypeFilter.Add(".mast");

            _file = await openPicker.PickSingleFileAsync();

            if (_file != null)
            {
                ProgressBoard progressBoard = new ProgressBoard();
                ProgressBoard.SlideOn(CurrentRectanglesCanvas, progressBoard);
                ResetRectangle();  // 每次选择文件之后都要重置方块颜色

                Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(_file);
                string text = await FileIO.ReadTextAsync(_file);

                try {
                    IEnumerable <string> lines = DatetimeParser.SplitByLine(text);
                    _model = new StatistTotalByDateTimeModel(lines);
                    ExtendStackCanvasByFilterOldRecorders(EarlierThanEarliestRectangle(_model.ToStatistTotalByDateTimeArray().ToList(), _earliestRectangle), _earliestRectangle);
                    ProgressBoard.CancelOn(CurrentRectanglesCanvas, progressBoard);
                    foreach (Canvas canvas in StackCanvas.Children)
                    {
                        ProgressBoard.SlideOn(canvas, new ProgressBoard());
                    }
                    TioSalamanca[] res = _model.GroupDateTimesByTotal();
#if DEBUG
                    for (int level = 0; level < res.Length; level++)
                    {
                        Debug.WriteLine($"level: {level + 1}");
                        Debug.WriteLine($"  List res[{level}]:");
                        foreach (var group in res[level])
                        {
                            Debug.WriteLine($"    Total: {group.Key}");
                            foreach (var item in group)
                            {
                                Debug.WriteLine($"      {item}");
                            }
                        }
                    }
#endif
                    DrawRectangleColor(res, false);
                }
                catch (ArgumentException err) {
                    PopErrorDialogAsync(err.Message);
                }
                _saveMode = SaveMode.OrginalFile; // 表示当前的操作基于磁盘上已有的文件
            }
        }
 /// <summary>
 /// 清空所有记录,重置所有状态
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void ClearButton_Click(object sender, RoutedEventArgs e)
 {
     /*
      * _rectangleRegisteTable 已在 ResetRectangleColor() 内部重新初始化,
      * 这里无需再次执行 _rectangleRegisteTable = new HashSet<Rectangle>()
      */
     ResetRectangle();
     _model    = null;
     _file     = null;
     _saveMode = SaveMode.NewFile;
     Blink.BlinkedRectangles.Clear();
     RefreshButton.Visibility  = Visibility.Collapsed;
     SaveFileButton.Visibility = Visibility.Collapsed;
     ClearButton.Visibility    = Visibility.Collapsed;
 }
        /// <summary>
        /// 每个方块都要响应鼠标点击
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Rect_PointerReleased(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
        {
            Rectangle rectangle = sender as Rectangle;

            Bubble.CreateBubbleRectangle(
                canvas: (sender as Rectangle).Parent as Canvas,
                hostRect: rectangle,
                bubbleName: VisualEventFrequencyRecorder.Resources["OhhohoRect"] as string,
                zoom: (minWidth: (double)VisualEventFrequencyRecorder.Resources["MinWidth"],
                       minHeight: (double)VisualEventFrequencyRecorder.Resources["MinHeight"],
                       maxWidth: (double)VisualEventFrequencyRecorder.Resources["MaxWidth"],
                       maxHeight: (double)VisualEventFrequencyRecorder.Resources["MaxHeight"])
                );

            /*
             * 气泡动画结束后从 Canvas 移除气泡方块。
             * 把 RectangleBubbleAnimation_Completed 作为局部方法的原因是:
             * 能够让 handler 在事件触发时捕获外部的 sender,让触发动画播放的
             * UI 对象能进入此 handler 的作用域,达到动画播放结束后移除该 UI 对象的效果。
             */
            void RectangleBubbleAnimation_Completed(object animation, object animationEventArg)
            {
                ((sender as Rectangle).Parent as Canvas).Children.Remove(Bubble._bubble);
            }

            Bubble.CreateBubbleStoryboard(
                hostPosition: (left: Canvas.GetLeft(rectangle), top: Canvas.GetTop(rectangle)),
                storyboard_Completed: RectangleBubbleAnimation_Completed
                );
            // 闪烁动画,提示用户该方块有未保存的变更;
            Blink.PlayBlink(rectangle);
            if (_model != null)
            {
                // 检测用户点击的方块对应的日期在之前打开的记录表中是否存在。
                // 如果 x.Count() > 0 为 true 证明存在,否则添加新条目。
                // 注意:x.Count() 和 x.First() 可能会导致两次查询,具体详情参见 MSDN
                var x = from entry in _model.Entries
                        where entry.Value.DateTime.ToShortDateString() == rectangle.Name
                        select entry;
                if (x.Count() > 0)   // 点击绿色方块
                {
                    x.First().Value.Total += 1;
#if DEBUG
                    ToolTip toolTip = new ToolTip {
                        Content = rectangle.Name + $"  Level:0  Total:{x.First().Value.Total}  Color:{(rectangle.Fill as SolidColorBrush).Color}"
                    };
                    ToolTipService.SetToolTip(rectangle, toolTip);
#endif
                }
                else    // 点击灰色方块
                {
                    _model.AddEntry(rectangle.Name);
#if DEBUG
                    ToolTip toolTip = new ToolTip {
                        Content = rectangle.Name + $"  Level:0  Total:1  Color:{(rectangle.Fill as SolidColorBrush).Color}"
                    };
                    ToolTipService.SetToolTip(rectangle, toolTip);
#endif
                }
            }
            else
            {
                // _model 为 null 证明用户在空白的状态下添加新条目
                _model = new StatistTotalByDateTimeModel(new string[] { rectangle.Name }, DateMode.DateWithSlash);
#if DEBUG
                ToolTip toolTip = new ToolTip {
                    Content = rectangle.Name + $"  Level:0  Total:1  Color:{(rectangle.Fill as SolidColorBrush).Color}"
                };
                ToolTipService.SetToolTip(rectangle, toolTip);
#endif
            }
            // 显示保存按钮,将变更添加到指定文件中
            if (SaveFileButton.Visibility == Visibility.Collapsed)
            {
                SaveFileButton.Visibility = Visibility.Visible;
            }
            // 显示刷新按钮,根据变更的时间频率对方块重新着色
            if (RefreshButton.Visibility == Visibility.Collapsed)
            {
                RefreshButton.Visibility = Visibility.Visible;
            }
            // 显示清空按钮
            if (ClearButton.Visibility == Visibility.Collapsed)
            {
                ClearButton.Visibility = Visibility.Visible;
            }
        }