Exemplo n.º 1
0
        /// <inheritdoc/>
        protected override void OnClosing(CancelEventArgs e)
        {
            // Ask when worth.
            if (Settings.AskWhileQuitting && Messagings.AskWhileQuitting() == MessageBoxResult.No)
            {
                e.Cancel = true;
                return;
            }

            // Save configuration.
            SaveConfig();

#if SUDOKU_RECOGNIZING
            // Dispose the instance.
            // If the service provider is not initialized, this value will be null.
            _recognition?.Dispose();
#endif

            GC.Collect();

            base.OnClosing(e);

#if SUDOKU_RECOGNIZING
            if (_recognition?.ToolIsInitialized ?? false)
            {
                // If you don't use this feature, the program will not need to use
                // this method to KILL itself... KILL... sounds terrible and dangerous, isn't it?
                // To be honest, I don't know why the program fails to exit... The background
                // threads still running after base close method executed completely. If you
                // know the detail of Emgu.CV, please tell me, thx!
                Process.GetCurrentProcess().Kill();
            }
#endif
        }
Exemplo n.º 2
0
        private void MenuItemFileOpenDatabase_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog
            {
                DefaultExt  = ".sudokus",
                Filter      = "Text file|*.txt|Sudoku database file|*.sudokus|All files|*.*",
                Multiselect = false,
                Title       = "Open sudoku file from..."
            };

            if (dialog.ShowDialog() is true)
            {
                using var sr = new StreamReader(Settings.CurrentPuzzleDatabase = _database = dialog.FileName);
                _puzzlesText = sr.ReadToEnd().Split(Splitter, StringSplitOptions.RemoveEmptyEntries);

                Messagings.LoadDatabase(_puzzlesText.Length);

                if (_puzzlesText.Length != 0)
                {
                    LoadPuzzle(_puzzlesText[Settings.CurrentPuzzleNumber = 0].TrimEnd(Splitter));
                    UpdateDatabaseControls(false, false, true, true);

                    _textBoxJumpTo.IsEnabled   = true;
                    _labelPuzzleNumber.Content = $"1/{_puzzlesText.Length}";
                }
            }
        }
Exemplo n.º 3
0
        private void MenuItemGenerateWithTechniqueFiltering_Click(object sender, RoutedEventArgs e)
#endif
        {
#if DEBUG
            await internalOperation();

            async Task internalOperation()
            {
                DisableGeneratingControls();

                Puzzle = new UndoableGrid(
                    await Task.Run(() =>
                                   new TechniqueFilteringPuzzleGenerator().Generate(
                                       new TechniqueCodeFilter {
                    TechniqueCode.AlmostLockedPair
                })));

                EnableGeneratingControls();
                SwitchOnGeneratingComboBoxesDisplaying();
                ClearItemSourcesWhenGeneratedOrSolving();
                UpdateImageGrid();
            }
#else
            Messagings.NotSupportedWhileGeneratingWithFilter();
#endif
        }
Exemplo n.º 4
0
        private void MenuItemEditRecomputeCandidates_Click(object sender, RoutedEventArgs e)
        {
            int[] z = new int[81];
            for (int cell = 0; cell < 81; cell++)
            {
                z[cell] = _puzzle[cell] + 1;
            }

            var grid = SudokuGrid.CreateInstance(z);

            if (new BitwiseSolver().Solve(grid.ToString(), null, 2) == 0)
            {
                Messagings.SukakuCannotUseThisFunction();

                e.Handled = true;
                return;
            }

            _puzzle = new UndoableGrid(grid);
            _puzzle.Unfix();
            _puzzle.ClearStack();

            UpdateImageGrid();
            UpdateUndoRedoControls();
        }
Exemplo n.º 5
0
        private async void MenuItemGenerateHardPattern_Click(object sender, RoutedEventArgs e)
        {
            await internalOperation();

            async Task internalOperation()
            {
                if (_database is null || Messagings.AskWhileGeneratingWithDatabase() == MessageBoxResult.Yes)
                {
                    // Disable relative database controls.
                    Settings.CurrentPuzzleDatabase = _database = null;
                    Settings.CurrentPuzzleNumber   = -1;
                    UpdateDatabaseControls(false, false, false, false);
                    _labelPuzzleNumber.ClearValue(ContentProperty);

                    DisableGeneratingControls();

                    int depth = _comboBoxBackdoorFilteringDepth.SelectedIndex;
                    Puzzle = new UndoableGrid(await Task.Run(() => new HardPatternPuzzleGenerator().Generate(depth - 1)));

                    EnableGeneratingControls();
                    SwitchOnGeneratingComboBoxesDisplaying();
                    ClearItemSourcesWhenGeneratedOrSolving();
                    UpdateImageGrid();
                }
            }
        }
Exemplo n.º 6
0
        private void MenuItemViewsGspView_Click(object sender, RoutedEventArgs e)
        {
            if (Enumerable.Range(0, 81).All(i => _puzzle.GetStatus(i) != CellStatus.Given))
            {
                Messagings.SukakuCannotUseGspChecking();
                e.Handled = true;
                return;
            }

            if (!(new GspTechniqueSearcher().GetOne(_puzzle) is GspTechniqueInfo info))
            {
                Messagings.DoesNotContainGsp();
                e.Handled = true;
                return;
            }

            bool[] series      = new bool[9];
            int?[] mapping     = info.MappingTable;
            var    cellOffsets = new List <(int, int)>();

            for (int i = 0, p = 0; i < 9; i++)
            {
                if (series[i])
                {
                    continue;
                }

                int?value = mapping[i];
                if (value is null)
                {
                    continue;
                }

                int j = value.Value;
                (series[i], series[j]) = (true, true);
                for (int cell = 0; cell < 81; cell++)
                {
                    int cellValue = _puzzle[cell];
                    if (cellValue == i || cellValue == j)
                    {
                        cellOffsets.Add((p, cell));
                    }
                }

                p++;
            }

            _textBoxInfo.Text = info.ToString();

            _layerCollection.Add(
                new ViewLayer(
                    _pointConverter, new View(cellOffsets, null, null, null), info.Conclusions, Settings.PaletteColors,
                    Settings.EliminationColor, Settings.CannibalismColor, Settings.ChainColor));

            UpdateImageGrid();
        }
Exemplo n.º 7
0
        private void MenuItemClearStack_Click(object sender, RoutedEventArgs e)
        {
            if ((_puzzle.HasUndoSteps || _puzzle.HasRedoSteps) &&
                Messagings.AskWhileClearingStack() == MessageBoxResult.Yes)
            {
                _puzzle.ClearStack();

                UpdateUndoRedoControls();
            }
        }
Exemplo n.º 8
0
        private void MenuItemExport_Click(object sender, RoutedEventArgs e)
        {
            if (_analyisResult is null)
            {
                Messagings.YouShouldSolveFirst();
                e.Handled = true;
                return;
            }

            new ExportAnalysisResultWindow(_analyisResult).Show();
        }
Exemplo n.º 9
0
        /// <inheritdoc/>
        protected override void OnInitialized(EventArgs e)
        {
            // Call the base method.
            base.OnInitialized(e);

            var mutex = new Mutex(true, SolutionName, out bool mutexIsNew);

            if (mutexIsNew)
            {
                mutex.ReleaseMutex();
            }
            else
            {
                Messagings.OnlyOpenOneProgram();
                Environment.Exit(0);
            }

#if SUDOKU_RECOGNIZING
            // Then initialize for recognizer.
            try
            {
                _recognition = new RecognitionServiceProvider();
            }
            catch (Exception ex)
            {
                Messagings.FailedToLoadRecognitionTool(ex);
            }
#endif

            // Define shortcuts.
            AddShortCut(Key.C, ModifierKeys.Control, null, MenuItemEditCopy_Click);
            AddShortCut(Key.H, ModifierKeys.Control, _menuItemGenerateHardPattern, MenuItemGenerateHardPattern_Click);
            AddShortCut(Key.O, ModifierKeys.Control, _menuItemFileOpen, MenuItemFileOpen_Click);
            AddShortCut(Key.P, ModifierKeys.Control, null, MenuItemFileGetSnapshot_Click);
            AddShortCut(Key.S, ModifierKeys.Control, null, MenuItemFileSave_Click);
            AddShortCut(Key.V, ModifierKeys.Control, _menuItemEditPaste, MenuItemEditPaste_Click);
            AddShortCut(Key.Y, ModifierKeys.Control, _menuItemEditRedo, MenuItemEditRedo_Click);
            AddShortCut(Key.Z, ModifierKeys.Control, _menuItemEditUndo, MenuItemEditUndo_Click);
            AddShortCut(Key.F5, ModifierKeys.Control, _menuItemEditRecomputeCandidates, MenuItemEditRecomputeCandidates_Click);
            AddShortCut(Key.OemTilde, ModifierKeys.Control, _menuItemEditFix, MenuItemEditFix_Click);
            AddShortCut(Key.F9, ModifierKeys.None, _menuItemAnalyzeAnalyze, MenuItemAnalyzeAnalyze_Click);
            AddShortCut(Key.F10, ModifierKeys.None, _menuItemAnalyzeSolve, MenuItemAnalyzeSolve_Click);
            AddShortCut(Key.F4, ModifierKeys.Alt, null, MenuItemFileQuit_Click);
            AddShortCut(Key.N, ModifierKeys.Control | ModifierKeys.Shift, _menuItemEditClear, MenuItemEditClear_Click);
            AddShortCut(Key.C, ModifierKeys.Control | ModifierKeys.Shift, null, MenuItemEditCopyCurrentGrid_Click);
            AddShortCut(Key.OemTilde, ModifierKeys.Control | ModifierKeys.Shift, _menuItemEditUnfix, MenuItemEditUnfix_Click);

            Title = $"{SolutionName} Ver {SolutionVersion}";

            LoadConfigIfWorth();
            InitializePointConverterAndLayers();
            LoadDatabaseIfWorth();
            UpdateControls();
        }
Exemplo n.º 10
0
 private void MenuItemFileGetSnapshot_Click(object sender, RoutedEventArgs e)
 {
     try
     {
         Clipboard.SetImage((BitmapSource)_imageGrid.Source);
     }
     catch (Exception ex)
     {
         Messagings.ShowExceptionMessage(ex);
     }
 }
Exemplo n.º 11
0
        private void MenuItemAnalyzeBugN_Click(object sender, RoutedEventArgs e)
        {
            if (!_puzzle.IsValid(out _))
            {
                Messagings.FailedToCheckDueToInvaildPuzzle();
                e.Handled = true;
                return;
            }

            new BugNSearchWindow(_puzzle).ShowDialog();
        }
Exemplo n.º 12
0
 private void MenuItemEditCopyHodokuLibrary_Click(object sender, RoutedEventArgs e)
 {
     try
     {
         string z = _puzzle.ToString(Settings.TextFormatPlaceholdersAreZero ? "#0" : "#.");
         Clipboard.SetText($":0000:x:{z}{(z.Contains(':') ? "::" : ":::")}");
     }
     catch (Exception ex)
     {
         Messagings.ShowExceptionMessage(ex);
     }
 }
Exemplo n.º 13
0
        private async void MenuItemGenerateWithSymmetry_Click(object sender, RoutedEventArgs e)
        {
            await internalOperation();

            async Task internalOperation()
            {
                #region Obsolete code
                //if (_comboBoxDifficulty.SelectedIndex == 0)
                //{
                //	MessageBox.Show(
                //		"We may not allow you to generate the puzzle whose difficulty is unknown.", "Warning");
                //
                //	e.Handled = true;
                //	return;
                //}
                //
                //if (_comboBoxDifficulty.SelectedIndex >= 5
                //	&& MessageBox.Show(
                //		"You selected a difficulty that generate a puzzle will be too slow " +
                //		"and you may not cancel the operation when generating. " +
                //		"Would you like to generate anyway?", "Info", MessageBoxButton.YesNo
                //	) != MessageBoxResult.Yes)
                //{
                //	e.Handled = true;
                //	return;
                //}
                #endregion

                if (_database is null || Messagings.AskWhileGeneratingWithDatabase() == MessageBoxResult.Yes)
                {
                    // Disable relative database controls.
                    Settings.CurrentPuzzleDatabase = _database = null;
                    Settings.CurrentPuzzleNumber   = -1;
                    UpdateDatabaseControls(false, false, false, false);

                    DisableGeneratingControls();

                    // These two value should be assigned first, rather than
                    // inlining in the asynchronized environment.
                    var symmetry = ((PrimaryElementTuple <string, SymmetryType>)_comboBoxSymmetry.SelectedItem).Value2;
                    //var diff = (DifficultyLevel)_comboBoxDifficulty.SelectedItem;
                    Puzzle = new UndoableGrid(await Task.Run(() => new BasicPuzzleGenerator().Generate(33, symmetry)));

                    EnableGeneratingControls();
                    SwitchOnGeneratingComboBoxesDisplaying();
                    ClearItemSourcesWhenGeneratedOrSolving();
                    UpdateImageGrid();
                }
            }
        }
Exemplo n.º 14
0
 private void ContextListBoxPathsCopyCurrentStep_Click(object sender, RoutedEventArgs e)
 {
     if (sender is MenuItem)
     {
         try
         {
             Clipboard.SetText(_listBoxPaths.SelectedItem.ToString());
         }
         catch
         {
             Messagings.CannotCopyStep();
         }
     }
 }
Exemplo n.º 15
0
        private void MenuItemAnalyzeSolve_Click(object sender, RoutedEventArgs e)
        {
            if (!applyNormal() && !applySukaku())
            {
                Messagings.FailedToApplyPuzzle();
                e.Handled = true;
                return;
            }

            bool applySukaku()
            {
                var sb = new StringBuilder(SudokuGrid.EmptyString);

                if (new SukakuBitwiseSolver().Solve(
                        _puzzle.ToString($"~{(Settings.TextFormatPlaceholdersAreZero ? "0" : ".")}"), sb, 2) != 1)
                {
                    return(!(e.Handled = true));
                }

                Puzzle = new UndoableGrid(SudokuGrid.Parse(sb.ToString()));
                UpdateImageGrid();
                return(true);
            }

            bool applyNormal()
            {
                var sb = new StringBuilder(SudokuGrid.EmptyString);

                for (int cell = 0; cell < 81; cell++)
                {
                    sb[cell] += (char)(_puzzle[cell] + 1);
                }

                if (new BitwiseSolver().Solve(sb.ToString(), sb, 2) != 1)
                {
                    return(!(e.Handled = true));
                }

                Puzzle = new UndoableGrid(SudokuGrid.Parse(sb.ToString()));
                UpdateImageGrid();
                return(true);
            }
        }
Exemplo n.º 16
0
        private async void MenuItemViewsShowBugN_Click(object sender, RoutedEventArgs e)
        {
            await internalOperation();

            async ValueTask internalOperation()
            {
                if (!_puzzle.IsValid(out _))
                {
                    Messagings.FailedToCheckDueToInvaildPuzzle();
                    e.Handled = true;
                    return;
                }

                _textBoxInfo.Text =
                    "The program is running as quickly as possible to search " +
                    "all non-BUG candidates (true candidates). Please wait.";

                var trueCandidates = await Task.Run(() => new BugChecker(_puzzle).GetAllTrueCandidates(64));

                _textBoxInfo.ClearValue(TextBox.TextProperty);
                if (trueCandidates.Count == 0)
                {
                    Messagings.DoesNotContainBugMultiple();
                    e.Handled = true;
                    return;
                }

                _layerCollection.Add(
                    new ViewLayer(
                        _pointConverter,
                        new View(
                            null,
                            new List <(int, int)>(from candidate in trueCandidates select(0, candidate)),
                            null,
                            null),
                        null,
                        Settings.PaletteColors, DColor.Empty, DColor.Empty, DColor.Empty));

                UpdateImageGrid();

                _textBoxInfo.Text = $"All true candidate(s): {new CandidateCollection(trueCandidates).ToString()}";
            }
        }
Exemplo n.º 17
0
        private async void ButtonFindAllSteps_Click(object sender, RoutedEventArgs e)
        {
            await internalOperation();

            async Task internalOperation()
            {
                if (!_puzzle.IsValid(out _))
                {
                    Messagings.FunctionIsUnavailable();

                    e.Handled = true;
                    return;
                }

                _listBoxTechniques.ClearValue(ItemsControl.ItemsSourceProperty);
                _textBoxInfo.Text             = "The solver is running slowly, please wait...";
                _buttonFindAllSteps.IsEnabled = false;
                DisableSolvingControls();

                var techniqueGroups = await new StepFinder(Settings).SearchAsync(_puzzle);

                EnableSolvingControls();
                SwitchOnGeneratingComboBoxesDisplaying();
                _buttonFindAllSteps.IsEnabled = true;
                _textBoxInfo.ClearValue(TextBox.TextProperty);

                // The boolean value stands for whether the technique is enabled.
                var list = new List <ListBoxItem>();

                foreach (var techniqueGroup in techniqueGroups)
                {
                    string name = techniqueGroup.Key;
                    foreach (var info in techniqueGroup)
                    {
                        var(fore, back) = Settings.DiffColors[info.DifficultyLevel];
                        list.Add(
                            new ListBoxItem
                        {
                            Content =
                                new PrimaryElementTuple <string, TechniqueInfo, bool>(
                                    info.ToSimpleString(), info, true),
                            BorderThickness = default,
Exemplo n.º 18
0
        private void ContextListBoxPathsCopyAllSteps_Click(object sender, RoutedEventArgs e)
        {
            if (sender is MenuItem)
            {
                var sb = new StringBuilder();
                foreach (string step in from object item in _listBoxPaths.Items select item.ToString())
                {
                    sb.AppendLine(step);
                }

                try
                {
                    Clipboard.SetText(sb.ToString());
                }
                catch
                {
                    Messagings.CannotCopyStep();
                }
            }
        }
Exemplo n.º 19
0
        private void MenuItemBackupConfig_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new SaveFileDialog
            {
                AddExtension    = true,
                CheckPathExists = true,
                DefaultExt      = "scfg",
                Filter          = "Configuration file|*.scfg",
                Title           = "Save configuration file to..."
            };

            if (dialog.ShowDialog() is true)
            {
                try
                {
                    SaveConfig(dialog.FileName);
                }
                catch
                {
                    Messagings.FailedToBackupConfig();
                }
            }
        }
Exemplo n.º 20
0
        private void MenuItemEditPasteAsSukaku_Click(object sender, RoutedEventArgs e)
        {
            string puzzleStr = Clipboard.GetText();

            if (!(puzzleStr is null))
            {
                try
                {
                    Puzzle = new UndoableGrid(SudokuGrid.Parse(puzzleStr, GridParsingOption.Sukaku));

                    _menuItemEditUndo.IsEnabled = _menuItemEditRedo.IsEnabled = false;
                    UpdateImageGrid();
                }
                catch (ArgumentException)
                {
                    Messagings.FailedToPasteText();
                }

                _listBoxPaths.ClearValue(ItemsControl.ItemsSourceProperty);
                _listViewSummary.ClearValue(ItemsControl.ItemsSourceProperty);
                _listBoxTechniques.ClearValue(ItemsControl.ItemsSourceProperty);
            }
        }
Exemplo n.º 21
0
        private void MenuItemFileLoadPicture_Click(object sender, RoutedEventArgs e)
#endif
        {
#if SUDOKU_RECOGNIZING
            await internalOperation();

            async Task internalOperation()
            {
                if (_recognition is null)
                {
                    Messagings.FailedToLoadPicture();

                    e.Handled = true;
                    return;
                }

                var dialog = new OpenFileDialog
                {
                    AddExtension = true,
                    DefaultExt   = "png",
                    Filter       = "PNG file|*.png|JPEG file|*.jpg;*.jpeg|BMP file|*.bmp|GIF file|*.gif",
                    Multiselect  = false,
                    Title        = "Load picture from..."
                };

                if (dialog.ShowDialog() is true)
                {
                    try
                    {
                        if (_recognition.ToolIsInitialized)
                        {
                            if (Messagings.AskWhileLoadingPicture() == MessageBoxResult.Yes)
                            {
                                _textBoxInfo.Text =
                                    "Load picture successfully, now grab all digits in the picture, please wait...";
                                using (var bitmap = new Bitmap(dialog.FileName))
                                {
                                    var grid = (await Task.Run(() => _recognition.Recorgnize(bitmap))).ToMutable();
                                    grid.Fix();
                                    Puzzle = new UndoableGrid(grid);
                                }

                                UpdateUndoRedoControls();
                                UpdateImageGrid();
                            }
                        }
                        else
                        {
                            Messagings.FailedToLoadPictureDueToNotHavingInitialized();
                        }
                    }
                    catch (Exception ex)
                    {
                        Messagings.ShowExceptionMessage(ex);
                    }

                    _textBoxInfo.ClearValue(TextBox.TextProperty);
                }
            }
#else
            Messagings.NotSupportedForLoadingPicture();
#endif
        }
Exemplo n.º 22
0
        private async void MenuItemViewsBackdoorView_Click(object sender, RoutedEventArgs e)
        {
            await internalOperation();

            async ValueTask internalOperation()
            {
                if (!_puzzle.IsValid(out _))
                {
                    Messagings.FailedToCheckDueToInvaildPuzzle();
                    e.Handled = true;
                    return;
                }

                _textBoxInfo.Text =
                    "The program is running as quickly as possible to search " +
                    "all backdoors (at level 0 or 1). Please wait.";

                var backdoors = new List <Conclusion>();

                for (int level = 0; level <= 1; level++)
                {
                    foreach (var backdoor in
                             await Task.Run(() => new BackdoorSearcher().SearchForBackdoorsExact(_puzzle, level)))
                    {
                        backdoors.AddRange(backdoor);
                    }
                }

                _textBoxInfo.ClearValue(TextBox.TextProperty);
                if (backdoors.None())
                {
                    Messagings.DoesNotContainBackdoor();
                    e.Handled = true;
                    return;
                }

                var highlightCandidates = new List <(int, int)>();
                int currentLevel        = 0;

                foreach (var(_, candidate) in backdoors)
                {
                    highlightCandidates.Add((currentLevel, candidate));

                    currentLevel++;
                }

                _layerCollection.Add(
                    new ViewLayer(
                        _pointConverter,
                        new View(
                            null,
                            new List <(int, int)>(
                                from conclusion in backdoors
                                where conclusion.ConclusionType == ConclusionType.Assignment
                                select(0, conclusion.CellOffset * 9 + conclusion.Digit)),
                            null,
                            null),
                        backdoors,
                        Settings.PaletteColors, Settings.EliminationColor, DColor.Empty, DColor.Empty));

                UpdateImageGrid();

                _textBoxInfo.Text = $"All backdoor(s) at level 0 or 1: {new ConclusionCollection(backdoors).ToString()}";
            }
        }
Exemplo n.º 23
0
        private async void MenuItemAnalyzeAnalyze_Click(object sender, RoutedEventArgs e)
        {
            if (!await internalOperation(false) && !await internalOperation(true))
            {
                Messagings.FailedToApplyPuzzle();
                e.Handled = true;
                return;
            }

            async Task <bool> internalOperation(bool sukakuMode)
            {
                if (_puzzle.HasSolved)
                {
                    Messagings.PuzzleAlreadySolved();
                    return(!(e.Handled = true));
                }

                var sb = new StringBuilder(SudokuGrid.EmptyString);

                if (sukakuMode)
                {
                    string puzzleString = _puzzle.ToString("~");
                    if (new SukakuBitwiseSolver().Solve(puzzleString, sb, 2) != 1)
                    {
                        return(!(e.Handled = true));
                    }
                }
                else
                {
                    for (int cell = 0; cell < 81; cell++)
                    {
                        sb[cell] += (char)(_puzzle[cell] + 1);
                    }

                    if (new BitwiseSolver().Solve(sb.ToString(), null, 2) != 1)
                    {
                        return(!(e.Handled = true));
                    }
                }

                // Update status.
                ClearItemSourcesWhenGeneratedOrSolving();
                _textBoxInfo.Text = "Solving, please wait. During solving you can do some other work...";
                DisableSolvingControls();

                // Run the solver asynchronizedly, during solving you can do other work.
                _analyisResult =
                    await Task.Run(() =>
                {
                    if (!Settings.SolveFromCurrent)
                    {
                        _puzzle.Reset();
                        _puzzle.ClearStack();
                    }

                    return(_manualSolver.Solve(_puzzle));
                });

                // Solved. Now update the technique summary.
                EnableSolvingControls();
                SwitchOnGeneratingComboBoxesDisplaying();

                if (_analyisResult.HasSolved)
                {
                    _textBoxInfo.Text =
                        $"{_analyisResult.SolvingStepsCount} " +
                        $"{(_analyisResult.SolvingStepsCount == 1 ? "step" : "steps")}, " +
                        $"time elapsed: {_analyisResult.ElapsedTime:hh':'mm'.'ss'.'fff}.";

                    int i        = 0;
                    var pathList = new List <ListBoxItem>();
                    foreach (var step in _analyisResult.SolvingSteps !)
                    {
                        var(fore, back) = Settings.DiffColors[step.DifficultyLevel];
                        var item = new ListBoxItem
                        {
                            Foreground      = new SolidColorBrush(fore.ToWColor()),
                            Background      = new SolidColorBrush(back.ToWColor()),
                            Content         = new PrimaryElementTuple <int, TechniqueInfo>(i++, step, 2),
                            BorderThickness = new Thickness()
                        };
                        pathList.Add(item);
                    }
                    _listBoxPaths.ItemsSource = pathList;

                    // Gather the information.
                    // GridView should list the instance with each property, not fields,
                    // even if fields are public.
                    // Therefore, here may use anonymous type is okay, but using value tuples
                    // is bad.
                    var     collection = new List <AnonymousType>();
                    decimal summary = 0, summaryMax = 0;
                    int     summaryCount = 0;
                    foreach (var techniqueGroup in
                             from solvingStep in _analyisResult.SolvingSteps !
                             orderby solvingStep.Difficulty
                             group solvingStep by solvingStep.Name)
                    {
                        string  name = techniqueGroup.Key;
                        int     count = techniqueGroup.Count();
                        decimal total = 0, maximum = 0;
                        foreach (var step in techniqueGroup)
                        {
                            summary += step.Difficulty;
                            summaryCount++;
                            total     += step.Difficulty;
                            maximum    = Math.Max(step.Difficulty, maximum);
                            summaryMax = Math.Max(step.Difficulty, maximum);
                        }

                        collection.Add(new { Technique = name, Count = count, Total = total, Max = maximum });
                    }

                    collection.Add(new
                    {
                        Technique = default(string?),
                        Count     = summaryCount,
                        Total     = summary,
                        Max       = summaryMax
                    });

                    GridView view;
                    _listViewSummary.ItemsSource = collection;
                    _listViewSummary.View        = view = new GridView();
                    view.Columns.AddRange(new[]
                    {
                        createGridViewColumn("Technique", .6),
                        createGridViewColumn("Count", .1),
                        createGridViewColumn("Total", .2),
                        createGridViewColumn("Max", .1)
                    });
                    view.AllowsColumnReorder = false;
                }
                else
                {
                    Messagings.FailedToSolveWithMessage(_analyisResult);
                }

                return(true);
            }

            GridViewColumn createGridViewColumn(string name, double widthScale) =>
            new GridViewColumn
            {
                Header = name,
                DisplayMemberBinding = new Binding(name),
                Width = _tabControlInfo.ActualWidth * widthScale - 4,
            };
        }