欢迎投稿

今日深度:

为自己做一个简单记账簿(1)(2)

代码分析

1. 程序启动

当程序启动时,需要做一下检查和初始化工作。我把这些工作都放在启动框中完成。

Program.cs:

  1. [STAThread]    
  2.  
  3. static void Main()    
  4.  
  5. {    
  6.  
  7.     Application.EnableVisualStyles();    
  8.  
  9.     Application.SetCompatibleTextRenderingDefault(false);    
  10.  
  11.     if (Splash.Instance.ShowDialog() == DialogResult.OK)    
  12.  
  13.      {    
  14.  
  15.          Application.Run(new MainFrame());    
  16.  
  17.      }    
  18.  
  19. }   

以上代码中的Splash就是启动对话框。只有当返回DialogResult.OK时,才会启动主程序。

Splash对话框是一个简单单例模式的实现。

Splash.cs:

  1. private static Splash _instance;    
  2.  
  3. public static Splash Instance    
  4.  
  5. {    
  6.  
  7.     get   
  8.  
  9.     {    
  10.  
  11.         if (_instance == null)    
  12.  
  13.         {    
  14.  
  15.             _instance = new Splash();    
  16.  
  17.         }    
  18.  
  19.         return _instance;    
  20.  
  21.     }    
  22.  
  23. }   

在Splash的构造过程中,会启动一个定时器,再会启动一个工作线程运行初始化程序。

Splash.cs:

  1. private Splash()    
  2.  
  3. {    
  4.  
  5.     InitializeComponent();    
  6.  
  7.     SetDialogInfo();    
  8.  
  9.     Ticker.Start();    
  10.  
  11.     Worker.RunWorkerAsync();    
  12.  
  13. }  

工作线程与定时器之间由标志DBState联系起来的,工作线程置标志,定时器轮询标志。

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.     }    
  46. }  

2. 标签选择框的绘制

图3下半部分中有一系列动态标签,这些标签的显示逻辑为:

从本地SQLite数据库中,查询出指定消费类别‘生活必需’或‘奢侈享受’)近一个月中不重复的标签,按出现频率倒序排列,并取出前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;";   

界面上的绘制标签区域其实是一个Panel,每一个标签是一个Label。

每次添加Label时,需检查当前将绘制的Label是否会超出Panel的边界,并相应的进行换行处理或退出循环。

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.         }  

总结与思考

1. 我对WinForm的开发远没有对数据库开发熟悉,大家若发现纰漏之处,请温柔指出。

2. 最近用户体验是一个热门词汇,做软件除了考虑技术问题之外,更要站在用户的角度去考虑他们的使用习惯。

3. 我自己非常想把这个记账工具做成手机版的,但对于移动开发知之甚少,大家可以进行尝试与讨论,欢迎和我邮件交流。


www.htsjk.Com true http://www.htsjk.com/shujukujc/18934.html NewsArticle 代码分析 1. 程序启动 当程序启动时,需要做一下检查和初始化工作。我把这些工作都放在启动框中完成。 Program.cs: [STAThread] static voidMain() { Application.Ena...
评论暂时关闭