protected override void OnSaveData()
        {
            if (!IsValidate())
            {
                MessageBox.Show("請先修正錯誤再儲存。", "錯誤");
                return;
            }
            K12.Data.StudentRecord stud = K12.Data.Student.SelectByID(this.PrimaryKey);
            StringBuilder sb = new StringBuilder();
            sb.Append(string.Format("學生『{0}』的課程成績變動如下:\n", stud.Name));

            //Deleted Records
            List<ActiveRecord> recs = new List<ActiveRecord>();
            foreach (UDT.SubjectSemesterScore scr in this.deletedScores)
            {
                sb.Append(string.Format("刪除:{0}  \n",this.makeScoreMsg(scr)));
                scr.Deleted = true;
                recs.Add(scr);
            }

            //Inserted Or Updated Records
            foreach (DataGridViewRow row in this.dataGridViewX1.Rows)
            {
                if (!row.IsNewRow)
                {
                    UDT.SubjectSemesterScore scr = (UDT.SubjectSemesterScore)row.Tag;
                    if (row.Tag == null)
                    {
                        scr = new UDT.SubjectSemesterScore();
                        scr.StudentID = int.Parse(this.PrimaryKey);
                    }
                    else
                    {
                        //如果不存在 updatedScores ,則跳下一筆
                        if (!this.updatedScores.ContainsKey(scr.UID))
                            continue;
                    }

                    if (row.Cells[0].Value == null)
                        scr.SchoolYear = null;
                    else
                        scr.SchoolYear =  int.Parse(row.Cells[0].Value.ToString());

                    if (row.Cells[1].Value == null)
                        scr.Semester = null;
                    else
                        scr.Semester =  this.dicSemester[row.Cells[1].Value.ToString()];

                    scr.NewSubjectCode = row.Cells[2].Value.ToString();
                    scr.SubjectID = int.Parse(this.dicSubjects[scr.NewSubjectCode].UID);
                    scr.SubjectName = row.Cells[3].Value.ToString();
                    scr.IsRequired = bool.Parse(row.Cells[5].Value.ToString());
                    scr.Credit = int.Parse(row.Cells[6].Value.ToString());
                    scr.Score = (row.Cells[8].Value == null ? "" : row.Cells[8].Value.ToString());
                    scr.IsPass = (row.Cells[9].Value==null) ? false : bool.Parse(row.Cells[9].Value.ToString());
                    scr.OffsetCourse = (row.Cells[10].Value == null ? "" : row.Cells[10].Value.ToString());
                    scr.Remark = (row.Cells[11].Value == null ? "" : row.Cells[11].Value.ToString());

                    if (string.IsNullOrWhiteSpace(scr.UID))
                        sb.Append(string.Format("新增:{0}  \n", this.makeScoreMsg(scr)));
                    else
                        sb.Append(string.Format("修改:{0}  \n", this.makeScoreMsg(scr)));

                    recs.Add(scr);
                }
            }

            AccessHelper ah = new AccessHelper();
            //delete
            //if (this.deletedList.Count > 0)
            ah.SaveAll(recs);

            FISCA.LogAgent.ApplicationLog.Log("課程學期成績.學生", "修改", "student", this.PrimaryKey, sb.ToString());

            this.OnPrimaryKeyChanged(EventArgs.Empty);

            ResetDirtyStatus();
        }
        private bool IsValidate()
        {
            //確定使用者修改的值都更新到控制項裡了(預防點選checkbox 後直接點選儲存,這時抓到的值仍是前一個值)。
            this.dataGridViewX1.EndEdit();
            Dictionary<string, List<DataGridViewRow>> dicRows = new Dictionary<string, List<DataGridViewRow>>();
            bool is_valid = true;
            foreach (DataGridViewRow row in this.dataGridViewX1.Rows)
            {
                //0:學年度
                //1:學期
                //2:課號
                //3:課程名稱
                //4:班次
                //5:必修
                //6:學分數
                //7:成績輸入進度
                //8:成績
                //9:取得學分
                //10:抵免課程
                //11:備註
                //12:流水號
                if (!row.IsNewRow)
                {
                    string school_year = row.Cells[0].Value + "";
                    string semester = row.Cells[1].Value + "";
                    string subject_code = row.Cells[2].Value + "";
                    string subject_name = row.Cells[3].Value + "";
                    string class_name = row.Cells[4].Value + "";
                    string is_required = row.Cells[5].Value + "";
                    string credit = row.Cells[6].Value + "";
                    string scored_progress = row.Cells[7].Value + "";
                    string score = row.Cells[8].Value + "";
                    string is_pass = row.Cells[9].Value + "";
                    string offset_course = row.Cells[10].Value + "";
                    string memo = row.Cells[11].Value + "";
                    string serial_no = row.Cells[12].Value + "";

                    bool changed = false;
                    bool bool_value = false;

                    UDT.SubjectSemesterScore sss = new UDT.SubjectSemesterScore();
                    if (row.Tag != null)
                    {
                        sss = row.Tag as UDT.SubjectSemesterScore;

                        if ((sss.SchoolYear + "") != school_year.Trim())
                            changed = true;
                        if (this.dicSemester[semester] != sss.Semester)
                            changed = true;
                        if (subject_code != sss.NewSubjectCode)
                            changed = true;
                        if (subject_name != sss.SubjectName)
                            changed = true;
                        if (bool.TryParse(is_required, out bool_value) && bool_value && !sss.IsRequired)
                            changed = true;
                        if ((!bool.TryParse(is_required, out bool_value) && sss.IsRequired) || (bool.TryParse(is_required, out bool_value) && !bool_value && sss.IsRequired))
                            changed = true;
                        if (credit != sss.Credit.ToString())
                            changed = true;
                        if (score != sss.Score)
                            changed = true;
                        if (bool.TryParse(is_pass, out bool_value) && bool_value && !sss.IsPass)
                            changed = true;
                        if ((!bool.TryParse(is_pass, out bool_value) && sss.IsPass) || (bool.TryParse(is_pass, out bool_value) && !bool_value && sss.IsPass))
                            changed = true;

                        if (changed && string.IsNullOrWhiteSpace(offset_course))
                        {
                            row.Cells[0].ErrorText = "非抵免課程僅可查詢,不可修改!";
                            is_valid = false;
                            continue;
                        }
                    }
                    else
                    {
                        if (string.IsNullOrWhiteSpace(offset_course))
                        {
                            row.Cells[0].ErrorText = "非抵免課程僅可查詢,不可新增!";
                            is_valid = false;
                            continue;
                        }
                    }

                    if (!dicRows.ContainsKey(school_year + "-" + subject_code))
                        dicRows.Add(school_year + "-" + subject_code, new List<DataGridViewRow>());

                    dicRows[school_year + "-" + subject_code].Add(row);
                    foreach(DataGridViewColumn column in this.dataGridViewX1.Columns)
                        row.Cells[column.Index].ErrorText = string.Empty;

                    //1. 如果學年度或學期為空,則視為抵免課程,必須輸入 "抵免課程" 欄位
                    //if (string.IsNullOrWhiteSpace(school_year) || string.IsNullOrWhiteSpace(semester))
                    //{
                    //    //必須是必修課程才能抵免
                    //    if (row.Cells[5].Value != null && bool.Parse(row.Cells[5].Value.ToString()))
                    //    {
                    //        DataGridViewCell cell = row.Cells[10];
                    //        if (cell.Value == null || string.IsNullOrWhiteSpace(cell.Value.ToString()))
                    //        {
                    //            cell.ErrorText = "請填入抵免課程資訊!";
                    //            is_valid = false;
                    //        }
                    //    }
                    //    else  //如果是選修,則依定不會是抵免課程,所以依定要有學年度學期
                    //    {
                    //        is_valid = this.cellCanNotBeNull(row.Cells[0], "請選擇學年度!");
                    //        is_valid = this.cellCanNotBeNull(row.Cells[1], "請選擇學期!");
                    //    }
                    //}
                    if (!string.IsNullOrWhiteSpace(offset_course))
                    {
                        //必須是必修課程才能抵免
                        bool required_course = false;
                        if (!(bool.TryParse(row.Cells[5].Value + "", out required_course) && required_course))
                        {
                            DataGridViewCell cell = row.Cells[5];
                            cell.ErrorText = "必修課程才能抵免!";
                            is_valid = false;
                        }
                        if (!string.IsNullOrWhiteSpace(school_year))
                        {
                            DataGridViewCell cell = row.Cells[0];
                            cell.ErrorText = "抵免課程之學年度應為空值!";
                            is_valid = false;
                        }
                        if (!string.IsNullOrWhiteSpace(semester))
                        {
                            DataGridViewCell cell = row.Cells[1];
                            cell.ErrorText = "抵免課程之學期應為空值!";
                            is_valid = false;
                        }
                        if (!string.IsNullOrWhiteSpace(score))
                        {
                            DataGridViewCell cell = row.Cells[8];
                            cell.ErrorText = "抵免課程之成績應為空值!";
                            is_valid = false;
                        }
                    }
                    //else    //如果學年度不為空值
                    //{
                    //    //檢查必填欄位
                    //    is_valid = this.cellCanNotBeNull(row.Cells[1], "請選擇學期!");
                    //}
                    if (!this.cellCanNotBeNull(row.Cells[2], "請選擇課程!"))
                        is_valid = false;
                    if (!this.cellCanNotBeNull(row.Cells[3], "請填入課程名稱!"))
                        is_valid = false;
                    if (!this.cellCanNotBeNull(row.Cells[6], "請填入學分數!"))
                        is_valid = false;
                    //this.cellCanNotBeNull(row.Cells[4], "請填入必選修!", errors);

                    //this.cellCanNotBeNull(row.Cells[6], "請填入成績!", errors);

                    //學年度和學分數必須為整數
                    if (!this.cellMustBeInteger(row.Cells[0]))
                        is_valid = false;
                    if (!this.cellMustBeInteger(row.Cells[6]))
                        is_valid = false;

                    //成績必須是有效的成績
                    //if (string.IsNullOrWhiteSpace(offset_course))
                    //    is_valid = this.cellMustBeValidScore(row.Cells[8]);

                    //如果不是必修,則不能輸入抵免課程資訊
                    //if (! bool.Parse(row.Cells[5].Value.ToString()))
                    //{
                    //    DataGridViewCell cell = row.Cells[9];
                    //    if (cell.Value != null && !string.IsNullOrWhiteSpace(cell.Value.ToString()))
                    //    {
                    //        cell.ErrorText = "非必修課程不能抵免";
                    //        is_valid = false;
                    //    }
                    //}
                }
            }
            foreach (string key in dicRows.Keys)
            {
                if (dicRows[key].Count < 2)
                    continue;

                foreach (DataGridViewRow row in dicRows[key])
                {
                    row.Cells[0].ErrorText = "「學年度」與「課號」的組合重複。";
                    row.Cells[2].ErrorText = "「學年度」與「課號」的組合重複。";
                    is_valid = false;
                }
            }
            return is_valid;
        }
            public UDT.SubjectSemesterScore GetScoreObject()
            {
                UDT.SubjectSemesterScore result = null;
                if (this.dicScores.ContainsKey(this.StudentID))
                    result = this.dicScores[this.StudentID];
                else
                {
                    result = new UDT.SubjectSemesterScore();
                    result.StudentID = int.Parse(this.StudentID);
                    result.CourseID = int.Parse(this.CourseID);
                    result.SchoolYear = int.Parse(this.SchoolYear);
                    result.Semester = int.Parse(this.Semester);
                }

                return result;
            }