使用Xamarin開發移動應用示例——數獨遊戲(六)使用資料庫

尋找無名的特質發表於2022-02-04

專案程式碼可以從Github下載:https://github.com/zhenl/ZL.Shudu 。程式碼隨專案進度更新。

現在我們希望為應用增加更多的功能,比如記錄每個完成的遊戲,可以讓使用者自己新增新的數獨遊戲等等,這些功能需要資料庫的支援。我們使用Sqlite資料庫儲存遊戲的資料。Sqlite是基於檔案的單機關係型資料庫,使用起來非常方便,首先安裝程式包sqlite-net-pcl,可以在Visual Studio 2022中使用Nuget管理器安裝最新版本,然後,新增POCO類的定義和資料庫訪問的方法,這裡我們需要記錄完成的遊戲和輸入的遊戲。在使用資料庫時,首先根據指定資料庫的路徑建立資料庫連線,如果資料庫檔案不存在,則建立新的資料庫檔案:

 readonly SQLiteAsyncConnection _database;
 _database = new SQLiteAsyncConnection(dbPath);

然後根據定義的POCO建立資料庫表(如果不存在):

 _database.CreateTableAsync<InputGameInfo>().Wait();
 _database.CreateTableAsync<FinishGame>().Wait();

接下來就可以使用相應的CRUD函式進行操作了。

下面是記錄完成遊戲的POCO。數獨的初始狀態和完成狀態是9X9矩陣,由於每個格子只是1-9的數字,採用字串儲存,長度是固定的,恢復為矩陣也比較方便,所以這裡採用string型別。完成的步驟是一個列表,每個步驟儲存行、列和輸入值,這裡也用字串儲存,每個步驟之間用指定分隔符隔開。

using SQLite;
using System;

namespace ZL.Shudu.Services
{
    public class FinishGame
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }
        /// <summary>
        /// 遊戲的初始狀態
        /// </summary>
        public string Sudoku { get; set; }
        /// <summary>
        /// 遊戲的結果
        /// </summary>
        public string Result { get; set; }
        /// <summary>
        /// 遊戲過程
        /// </summary>
        public string Steps { get; set; }
        /// <summary>
        /// 遊戲完成日期
        /// </summary>
        public DateTime PlayDate { get; set; }
        /// <summary>
        /// 使用時間
        /// </summary>
        public long TotalTime { get; set; }
    }
}

輸入遊戲的POCO:

using SQLite;
using System;

namespace ZL.Shudu.Services
{
    public class InputGameInfo
    {
        [PrimaryKey, AutoIncrement]
        public int ID { get; set; }
        /// <summary>
        /// 遊戲的初始狀態
        /// </summary>
        public string Sudoku { get; set; }
        /// <summary>
        /// 輸入日期
        /// </summary>
        public DateTime InputDate { get; set; }
        /// <summary>
        /// 是否在遊戲中使用
        /// </summary>
        public bool UsedInGame { get; set; }
    }
}

運算元據庫的類:

using SQLite;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace ShuduApp.Db
{
    public class Database
    {
        readonly SQLiteAsyncConnection _database;

        public Database(string dbPath)
        {
            _database = new SQLiteAsyncConnection(dbPath);
            _database.CreateTableAsync<InputGameInfo>().Wait();
            _database.CreateTableAsync<FinishGame>().Wait();
        }

        #region InputGameInfo
        public Task<List<InputGameInfo>> GetGamesAsync()
        {
            return _database.Table<InputGameInfo>().ToListAsync();
        }

        public Task<int> SaveGameAsync(InputGameInfo game)
        {
            return _database.InsertAsync(game);
        }

        public Task<int> UpdateGameAsync(InputGameInfo game)
        {
            return _database.UpdateAsync(game);
        }

        public Task<int> DeleteGameAsync(InputGameInfo game)
        {
            return _database.DeleteAsync(game);
        }

        #endregion

        #region FinishGame
        public Task<List<FinishGame>> GetFinishGamesAsync()
        {
            return _database.Table<FinishGame>().ToListAsync();
        }

        public Task<int> SaveFinishGameAsync(FinishGame game)
        {
            return _database.InsertAsync(game);
        }

        public Task<int> UpdateFinishGameAsync(FinishGame game)
        {
            return _database.UpdateAsync(game);
        }

        public Task<int> DeleteFinishGameAsync(FinishGame game)
        {
            return _database.DeleteAsync(game);
        }
        #endregion
    }
}

完成這些以後,我們改造Game頁面,增加儲存結果到資料和從資料庫產生新遊戲的程式碼。
首先是儲存結果到資料庫:

       private async Task SaveToDb()
        {
            try
            {
                var finobj = new FinishGame();

                for (var i = 0; i < 9; i++)
                {
                    for (var j = 0; j < 9; j++)
                    {
                        finobj.Sudoku += chess[i, j].ToString();
                    }
                }

                for (var i = 0; i < 9; i++)
                {
                    for (var j = 0; j < 9; j++)
                    {
                        finobj.Result += string.IsNullOrEmpty(buttons[i, j].Text) ? "0" : buttons[i, j].Text;
                    }
                }

                foreach (var str in steps)
                {
                    finobj.Steps += str + ";";
                }

                finobj.TotalTime = (DateTime.Now - dtBegin).Ticks;

                finobj.PlayDate = DateTime.Now;

                var id=await App.Database.SaveFinishGameAsync(finobj);

            }
            catch (Exception ex)
            {

                lbMessage.Text = ex.Message;
            }
        }

在判斷結束的程式碼中加入上述方法。然後是在初始化時從資料庫中獲取遊戲:

      private async Task<List<InputGameInfo>> LoadFromDb()
        {
            var leng = chesses.GetLength(0);
            var task = App.Database.GetGamesAsync();
            var lst = task.Result;

            for (int k = 0; k < leng; k++)
            {
                var sudoku = "";
                for (var i = 0; i < 9; i++)
                {
                    for (var j = 0; j < 9; j++)
                    {
                        sudoku += chesses[k, i, j].ToString();
                    }
                }
                var obj = lst.FirstOrDefault(o => o.Sudoku == sudoku);
                if (obj == null)
                {
                    await App.Database.SaveGameAsync(new InputGameInfo
                    {
                        Sudoku = sudoku,
                        InputDate = DateTime.Now,
                        UsedInGame = true
                    });
                }
            }

            var l= await App.Database.GetGamesAsync();
            lst = l.Where(o => o.UsedInGame).ToList();
            return lst;
        }

在這裡我們判斷資料庫中是否有遊戲,如果沒有,將預製的遊戲儲存到資料庫,然後從資料庫中生成新的遊戲。
我們增加一個新的頁面,用來檢視現有的遊戲:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms" xmlns:d1="http://xamarin.com/schemas/2014/forms/design"
             x:Class="ZL.Shudu.Views.GameList">
    <ContentPage.Content>
        <StackLayout>
            <ListView x:Name="MyListView"
            d1:ItemsSource="{Binding Items}"
            CachingStrategy="RecycleElement"
                   IsVisible="True">
                <d:ListView.ItemsSource>
                    <x:Array Type="{x:Type x:String}">
                        <x:String>Item 1</x:String>
                        <x:String>Item 2</x:String>
                        <x:String>Item 3</x:String>
                        <x:String>Item 4</x:String>
                        <x:String>Item 5</x:String>
                    </x:Array>
                </d:ListView.ItemsSource>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

後臺程式碼:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ZL.Shudu.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class GameList : ContentPage
    {
        public ObservableCollection<string> Items { get; set; }

        public GameList()
        {
            InitializeComponent();
            RefreshList();
        }

        public async Task RefreshList()
        {
            Items = await GetItems();
            MyListView.ItemsSource = Items;
            MyListView.IsVisible = true;
        }

        public async Task<ObservableCollection<string>> GetItems()
        {
            var items = new ObservableCollection<string>();

            var lst = await App.Database.GetGamesAsync();

            foreach (var obj in lst)
            {
                items.Add(obj.ID.ToString());
            }
            return items;
        }

    }
}

在導航頁面中增加對這個頁面的導航:


    <TabBar>
        <ShellContent Title="遊戲" Icon="icon_about.png" Route="Game"  ContentTemplate="{DataTemplate local:Game}" />
        <ShellContent Title="遊戲列表" Icon="icon_about.png" Route="GameList"  ContentTemplate="{DataTemplate local:GameList}" />
    </TabBar>

使用這個頁面,可以檢視現有的遊戲。
下一步,我們編寫檢視完成遊戲的列表和增加新遊戲的頁面。

相關文章