代码分析
1. 程序启动
当程序启动时,需要做一下检查和初始化工作。我把这些工作都放在启动框中完成。
Program.cs:
- [STAThread]
- static void Main()
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- if (Splash.Instance.ShowDialog() == DialogResult.OK)
- {
- Application.Run(new MainFrame());
- }
- }
以上代码中的Splash就是启动对话框。只有当返回DialogResult.OK时,才会启动主程序。
Splash对话框是一个简单单例模式的实现。
Splash.cs:
- private static Splash _instance;
- public static Splash Instance
- {
- get
- {
- if (_instance == null)
- {
- _instance = new Splash();
- }
- return _instance;
- }
- }
在Splash的构造过程中,会启动一个定时器,再会启动一个工作线程运行初始化程序。
Splash.cs:
- private Splash()
- {
- InitializeComponent();
- SetDialogInfo();
- Ticker.Start();
- Worker.RunWorkerAsync();
- }
工作线程与定时器之间由标志DBState联系起来的,工作线程置标志,定时器轮询标志。
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. 标签选择框的绘制
图3下半部分中有一系列动态标签,这些标签的显示逻辑为:
从本地SQLite数据库中,查询出指定消费类别‘生活必需’或‘奢侈享受’)近一个月中不重复的标签,按出现频率倒序排列,并取出前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;";
界面上的绘制标签区域其实是一个Panel,每一个标签是一个Label。
每次添加Label时,需检查当前将绘制的Label是否会超出Panel的边界,并相应的进行换行处理或退出循环。
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();
- }
- }
总结与思考
1. 我对WinForm的开发远没有对数据库开发熟悉,大家若发现纰漏之处,请温柔指出。
2. 最近用户体验是一个热门词汇,做软件除了考虑技术问题之外,更要站在用户的角度去考虑他们的使用习惯。
3. 我自己非常想把这个记账工具做成手机版的,但对于移动开发知之甚少,大家可以进行尝试与讨论,欢迎和我邮件交流。
本站文章为和通数据库网友分享或者投稿,欢迎任何形式的转载,但请务必注明出处.
同时文章内容如有侵犯了您的权益,请联系QQ:970679559,我们会在尽快处理。