日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
為自己做一個簡單記賬簿

每個月收到信用卡賬單時,我總會又驚又惑。上個月怎么又花了那么多錢?看著每一筆出帳流水,猛抓頭皮卻怎么也記不起來這錢是用在了哪兒。痛定思痛,采取行動,我要記賬。作為一個信奉技術(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:

 
 
 
 
  1. [STAThread]  
  2. static void Main()  
  3. {  
  4.     Application.EnableVisualStyles();  
  5.     Application.SetCompatibleTextRenderingDefault(false);  
  6.     if (Splash.Instance.ShowDialog() == DialogResult.OK)  
  7.      {  
  8.          Application.Run(new MainFrame());  
  9.      }  

以上代碼中的Splash就是啟動對話框。只有當(dāng)返回DialogResult.OK時,才會啟動主程序。

Splash對話框是一個簡單單例模式的實現(xiàn)。

Splash.cs:

 
 
 
 
  1. private static Splash _instance;  
  2. public static Splash Instance  
  3. {  
  4.     get 
  5.     {  
  6.         if (_instance == null)  
  7.         {  
  8.             _instance = new Splash();  
  9.         }  
  10.         return _instance;  
  11.     }  

在Splash的構(gòu)造過程中,會啟動一個定時器,再會啟動一個工作線程運行初始化程序。

Splash.cs:

 
 
 
 
  1. private Splash()  
  2. {  
  3.     InitializeComponent();  
  4.     SetDialogInfo();  
  5.     Ticker.Start();  
  6.     Worker.RunWorkerAsync();  

工作線程與定時器之間由標(biāo)志DBState聯(lián)系起來的,工作線程置標(biāo)志,定時器輪詢標(biāo)志。

Splash.cs:

 
 
 
 
  1. private Timer _ticker;  
  2. public Timer Ticker  
  3. {  
  4.     get 
  5.     {  
  6.         if (_ticker == null)  
  7.         {  
  8.             _ticker = new Timer(this.components);  
  9.             _ticker.Interval = 2000;  
  10.             _ticker.Tick += new System.EventHandler(_ticker_Tick);  
  11.         }  
  12.         return _ticker;  
  13.     }  
  14. }  
  15. private enum DBStateEnum  
  16. {  
  17.     Undefined,  
  18.     Ready,  
  19.     Failed  
  20. }  
  21. private DBStateEnum _dbState = DBStateEnum.Undefined;  
  22. private DBStateEnum DBState  
  23. {  
  24.     get { return _dbState; }  
  25.     set { _dbState = value; }  
  26. }  
  27. private void _ticker_Tick(object sender, System.EventArgs e)  
  28. {  
  29.     if (DBState == DBStateEnum.Ready)  
  30.     {  
  31.         this.DialogResult = DialogResult.OK;  
  32.         this.Close();  
  33.     }  
  34.     else if (DBState == DBStateEnum.Failed)  
  35.     {  
  36.         if (string.IsNullOrEmpty(this.lblMessage.Text))  
  37.         {  
  38.             this.lblMessage.Text = ErrorMessage;  
  39.         }  
  40.         else 
  41.         {  
  42.             this.DialogResult = DialogResult.Cancel;  
  43.             this.Close();  
  44.         }  
  45.     }  

2. 標(biāo)簽選擇框的繪制

圖3下半部分中有一系列動態(tài)標(biāo)簽,這些標(biāo)簽的顯示邏輯為:

從本地SQLite數(shù)據(jù)庫中,查詢出指定消費類別(‘生活必需’或‘奢侈享受’)近一個月中不重復(fù)的標(biāo)簽,按出現(xiàn)頻率倒序排列,并取出前10個

FeeRecorderControl.cs:

 
 
 
 
  1. private static readonly string getRecentMonthTop10SubCategorySql =  
  2.              @"select  
  3.                SubCategory  
  4.              from  
  5.                AccountRecord  
  6.              where  
  7.                Category = '{0}'  
  8.                and  
  9.                ConsumeDate >= date('now', 'localtime', '-1 month')  
  10.                and  
  11.                ConsumeDate <= datetime('now', 'localtime')  
  12.                and  
  13.                ifnull(SubCategory, '') <> ''  
  14.              group by  
  15.                SubCategory  
  16.              order by  
  17.                count(*) desc  
  18.              limit 0,10;"; 

界面上的繪制標(biāo)簽區(qū)域其實是一個Panel,每一個標(biāo)簽是一個Label。

每次添加Label時,需檢查當(dāng)前將繪制的Label是否會超出Panel的邊界,并相應(yīng)的進(jìn)行換行處理或退出循環(huán)。

FeeRecorderControl.cs:

 
 
 
 
  1. private void InitalizeSubCategoryPanel(string strCategory, Color backColor)  
  2.         {  
  3.             using (SQLiteConnection conn = new SQLiteConnection(SqliteConnString))  
  4.             {  
  5.                 conn.Open();  
  6.                 using (SQLiteCommand cmd = new SQLiteCommand(string.Format(getRecentMonthTop10SubCategorySql, strCategory), conn))  
  7.                 {  
  8.                     using (SQLiteDataReader reader = cmd.ExecuteReader())  
  9.                     {  
  10.                         Point subCategoryLocation = new Point(0, 0);  
  11.                         SubCategoryList.Clear();  
  12.                         plSubCategory.Controls.Clear();  
  13.                         while (reader.Read())  
  14.                         {  
  15.                             string strSubCategory = reader["SubCategory"].ToString();  
  16.                             Label lblSubCategory = new Label();  
  17.                             lblSubCategory.Text = strSubCategory;  
  18.                             lblSubCategory.Font = new Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold,  
  19.                                                            System.Drawing.GraphicsUnit.Point, ((byte) (0)));  
  20.                             lblSubCategory.Width = lblSubCategory.Text.Length*25 + 10;  
  21.                             lblSubCategory.Height = 35;  
  22.                             lblSubCategory.TextAlign = ContentAlignment.MiddleCenter;  
  23.                             lblSubCategory.BackColor = backColor;  
  24.                             lblSubCategory.Click += new EventHandler(lblSubCategory_Click);  
  25.                             if (subCategoryLocation.X + lblSubCategory.Width <= plSubCategory.Width  
  26.                                 && subCategoryLocation.Y + lblSubCategory.Height <= plSubCategory.Height)  
  27.                             {  
  28.                                 lblSubCategory.Location = subCategoryLocation;  
  29.                             }  
  30.                             else if (subCategoryLocation.X + lblSubCategory.Width > plSubCategory.Width   
  31.                                 && subCategoryLocation.Y + lblSubCategory.Height + 5 + lblSubCategory.Height <= plSubCategory.Height)  
  32.                             {  
  33.                                 subCategoryLocation.X = 0;  
  34.                                 subCategoryLocation.Y = subCategoryLocation.Y + lblSubCategory.Height + 5;  
  35.                                 lblSubCategory.Location = subCategoryLocation;  
  36.                             }  
  37.                             else 
  38.                             {  
  39.                                 break;  
  40.                             }  
  41.                             subCategoryLocation.X = subCategoryLocation.X + lblSubCategory.Width + 5;  
  42.                             SubCategoryList.Add(lblSubCategory);  
  43.                         }  
  44.                         plSubCategory.Controls.AddRange(SubCategoryList.ToArray());  
  45.                     }  
  46.                 }  
  47.                 conn.Close();  
  48.             }  
  49.         } 

總結(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