新聞中心
每個月收到信用卡賬單時,我總會又驚又惑。上個月怎么又花了那么多錢?看著每一筆出帳流水,猛抓頭皮卻怎么也記不起來這錢是用在了哪兒。痛定思痛,采取行動,我要記賬。作為一個信奉技術(shù)能改變世界的IT人,我理所當(dāng)然的在網(wǎng)上搜索各種電子記賬本。在線的記賬功能不敢用(怕被騷擾),一些單機(jī)記賬軟件提供的功能又不是我想要的。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)建站!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、小程序開發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了薩嘎免費建站歡迎大家使用!
與此同時,最近空下來的時候,我在看SQLite方面的資料。SQLite的簡潔、小巧讓我有些愛不釋手。就此決定給自己做個記賬本,用SQLite作為本地數(shù)據(jù)引擎。
功能概述
我需要的記賬功能比較簡單:
***、記錄每一筆消費,并可以添加需要的標(biāo)簽。當(dāng)我查看明細(xì)時,能知道自己買了啥。
第二、對我來說,消費只需要分成兩種:‘生活必需消費’和‘享受消費’。每周、每月可以看到這兩種消費所占的比例、金額。
第三、能查看自己近6個月的消費走勢。
根據(jù)這3點需求,我為自己度身定制了這款記賬工具。
圖1是記賬本的啟動框。
程序?qū)右粋€工作線程來檢查記賬程序路徑下是否已存在賬本數(shù)據(jù)庫,若沒有則創(chuàng)建該數(shù)據(jù)庫和所需的表結(jié)構(gòu)。同時定時器將輪詢檢查結(jié)果。
(圖1)
圖2是記賬本的主界面。
很多其他記賬軟件把消費分成餐飲,交通,買衣服……或者更細(xì)。一筆賬到底歸為哪一類要想個半天,同時出的圖表復(fù)雜但又意義不大。
為自己做的賬本只有兩種消費類別,對應(yīng)兩個大按鈕,點擊即可進(jìn)入記賬界面。這兩種消費所占的比例和總額是我每月的關(guān)注點。
主界面的最下方還有3個按鈕,分別對應(yīng)‘返回主界面’、‘退出程序’、‘查看報表’。在任何其它界面中,這三個按鈕的圖案、功能都保持一致。
(圖2)
點擊主界面上的綠色或紅色按鈕就會進(jìn)入到記賬界面。如圖3所示
標(biāo)題、圖標(biāo)、主色調(diào)區(qū)分了不同的消費。該界面的設(shè)計也是希望最簡化,省去了消費時間選擇框,默認(rèn)為當(dāng)前記錄時間。
該界面的一個亮點是‘標(biāo)簽選擇框’??蛑械臉?biāo)簽是動態(tài)生成的。系統(tǒng)會取近一個月時間,使用最頻繁的10個標(biāo)簽來顯示。(代碼分析部分還會展開)
這里記錄的標(biāo)簽,會出現(xiàn)在后面的明細(xì)報表中,這是我用來對賬的。
(圖3)
***來看一下這個小工具能生成的圖表與報表,如圖4所示
該工具能輸出3種報表,分別是消費比例圖,近6月消費走勢圖,消費對賬明細(xì)。對于圖表,鼠標(biāo)至于色塊上方時將顯示消費金額。
這3個報表也本著減少操作,降低復(fù)雜度,簡潔好用為宗旨,所以只提供了最必要的功能。
(圖4)
程序結(jié)構(gòu)
看了工具的界面設(shè)計后,讓我們來看一下程序結(jié)構(gòu),如圖5所示
(圖5)
整個Solution最主要由3個Project組成。
1. DataAccessLayer.SQLite包裝了對SQLite訪問的方法
2. ForSingle 主程序
3. UserControls 自定義用戶控件
需要說明的是:
這個工具所有界面最下方的3個按鈕保持統(tǒng)一,所以我在UserControls中畫了一個BaseForm(圖中橙色框標(biāo)出),讓主界面繼承自BaseForm。
其他的每一個界面都做成UserControl,在主程序中控制它們的創(chuàng)建與顯示。如圖中綠色框標(biāo)出。
SQLite對于本地應(yīng)用是個不錯的選擇,我使用的是一個包裝成ADO.NET接口的SQLite引擎。以下鏈接供參考:
我使用的類庫:http://sqlite.phxsoftware.com/
SQLite官方網(wǎng)站:http://www.sqlite.org/
#p#
代碼分析
1. 程序啟動
當(dāng)程序啟動時,需要做一下檢查和初始化工作。我把這些工作都放在啟動框中完成。
Program.cs:
- [STAThread]
- static void Main()
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- if (Splash.Instance.ShowDialog() == DialogResult.OK)
- {
- Application.Run(new MainFrame());
- }
- }
以上代碼中的Splash就是啟動對話框。只有當(dāng)返回DialogResult.OK時,才會啟動主程序。
Splash對話框是一個簡單單例模式的實現(xiàn)。
Splash.cs:
- private static Splash _instance;
- public static Splash Instance
- {
- get
- {
- if (_instance == null)
- {
- _instance = new Splash();
- }
- return _instance;
- }
- }
在Splash的構(gòu)造過程中,會啟動一個定時器,再會啟動一個工作線程運行初始化程序。
Splash.cs:
- private Splash()
- {
- InitializeComponent();
- SetDialogInfo();
- Ticker.Start();
- Worker.RunWorkerAsync();
- }
工作線程與定時器之間由標(biāo)志DBState聯(lián)系起來的,工作線程置標(biāo)志,定時器輪詢標(biāo)志。
Splash.cs:
- private Timer _ticker;
- public Timer Ticker
- {
- get
- {
- if (_ticker == null)
- {
- _ticker = new Timer(this.components);
- _ticker.Interval = 2000;
- _ticker.Tick += new System.EventHandler(_ticker_Tick);
- }
- return _ticker;
- }
- }
- private enum DBStateEnum
- {
- Undefined,
- Ready,
- Failed
- }
- private DBStateEnum _dbState = DBStateEnum.Undefined;
- private DBStateEnum DBState
- {
- get { return _dbState; }
- set { _dbState = value; }
- }
- private void _ticker_Tick(object sender, System.EventArgs e)
- {
- if (DBState == DBStateEnum.Ready)
- {
- this.DialogResult = DialogResult.OK;
- this.Close();
- }
- else if (DBState == DBStateEnum.Failed)
- {
- if (string.IsNullOrEmpty(this.lblMessage.Text))
- {
- this.lblMessage.Text = ErrorMessage;
- }
- else
- {
- this.DialogResult = DialogResult.Cancel;
- this.Close();
- }
- }
- }
2. 標(biāo)簽選擇框的繪制
圖3下半部分中有一系列動態(tài)標(biāo)簽,這些標(biāo)簽的顯示邏輯為:
從本地SQLite數(shù)據(jù)庫中,查詢出指定消費類別(‘生活必需’或‘奢侈享受’)近一個月中不重復(fù)的標(biāo)簽,按出現(xiàn)頻率倒序排列,并取出前10個
FeeRecorderControl.cs:
- private static readonly string getRecentMonthTop10SubCategorySql =
- @"select
- SubCategory
- from
- AccountRecord
- where
- Category = '{0}'
- and
- ConsumeDate >= date('now', 'localtime', '-1 month')
- and
- ConsumeDate <= datetime('now', 'localtime')
- and
- ifnull(SubCategory, '') <> ''
- group by
- SubCategory
- order by
- count(*) desc
- limit 0,10;";
界面上的繪制標(biāo)簽區(qū)域其實是一個Panel,每一個標(biāo)簽是一個Label。
每次添加Label時,需檢查當(dāng)前將繪制的Label是否會超出Panel的邊界,并相應(yīng)的進(jìn)行換行處理或退出循環(huán)。
FeeRecorderControl.cs:
- private void InitalizeSubCategoryPanel(string strCategory, Color backColor)
- {
- using (SQLiteConnection conn = new SQLiteConnection(SqliteConnString))
- {
- conn.Open();
- using (SQLiteCommand cmd = new SQLiteCommand(string.Format(getRecentMonthTop10SubCategorySql, strCategory), conn))
- {
- using (SQLiteDataReader reader = cmd.ExecuteReader())
- {
- Point subCategoryLocation = new Point(0, 0);
- SubCategoryList.Clear();
- plSubCategory.Controls.Clear();
- while (reader.Read())
- {
- string strSubCategory = reader["SubCategory"].ToString();
- Label lblSubCategory = new Label();
- lblSubCategory.Text = strSubCategory;
- lblSubCategory.Font = new Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold,
- System.Drawing.GraphicsUnit.Point, ((byte) (0)));
- lblSubCategory.Width = lblSubCategory.Text.Length*25 + 10;
- lblSubCategory.Height = 35;
- lblSubCategory.TextAlign = ContentAlignment.MiddleCenter;
- lblSubCategory.BackColor = backColor;
- lblSubCategory.Click += new EventHandler(lblSubCategory_Click);
- if (subCategoryLocation.X + lblSubCategory.Width <= plSubCategory.Width
- && subCategoryLocation.Y + lblSubCategory.Height <= plSubCategory.Height)
- {
- lblSubCategory.Location = subCategoryLocation;
- }
- else if (subCategoryLocation.X + lblSubCategory.Width > plSubCategory.Width
- && subCategoryLocation.Y + lblSubCategory.Height + 5 + lblSubCategory.Height <= plSubCategory.Height)
- {
- subCategoryLocation.X = 0;
- subCategoryLocation.Y = subCategoryLocation.Y + lblSubCategory.Height + 5;
- lblSubCategory.Location = subCategoryLocation;
- }
- else
- {
- break;
- }
- subCategoryLocation.X = subCategoryLocation.X + lblSubCategory.Width + 5;
- SubCategoryList.Add(lblSubCategory);
- }
- plSubCategory.Controls.AddRange(SubCategoryList.ToArray());
- }
- }
- conn.Close();
- }
- }
總結(jié)與思考
1. 我對WinForm的開發(fā)遠(yuǎn)沒有對數(shù)據(jù)庫開發(fā)熟悉,大家若發(fā)現(xiàn)紕漏之處,請溫柔指出。
2. 最近用戶體驗是一個熱門詞匯,做軟件除了考慮技術(shù)問題之外,更要站在用戶的角度去考慮他們的使用習(xí)慣。
3. 我自己非常想把這個記賬工具做成手機(jī)版的,但對于移動開發(fā)知之甚少,大家可以進(jìn)行嘗試與討論,歡迎和我郵件交流。
分享標(biāo)題:為自己做一個簡單記賬簿
標(biāo)題路徑:http://www.5511xx.com/article/ccicede.html


咨詢
建站咨詢
