Chinh sua UI

This commit is contained in:
Nguyễn Thái Phong 2025-10-15 14:35:02 +07:00
parent 8f144c1a8b
commit 8cca84ceec
24 changed files with 698 additions and 458 deletions

Binary file not shown.

View File

@ -6,29 +6,45 @@
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}, },
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form1.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form1.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{ {
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form2.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form2.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form2.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form2.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}, },
{ {
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form1.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\response\\quiz.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form1.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:response\\quiz.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\response\\questionoption.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:response\\questionoption.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\service\\executeexcelservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:service\\executeexcelservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\response\\question.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:response\\question.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\dbhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:dbhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}, },
{ {
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\service\\datautil.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\service\\datautil.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:service\\datautil.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:service\\datautil.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}, },
{ {
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|E:\\QuizMaster\\QuizMaster\\form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\service\\excelvalidator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form1.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:service\\excelvalidator.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}, },
{ {
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form2.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\form2.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form2.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" "RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:form2.designer.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|e:\\quizmaster\\quizmaster\\dbhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{582DBFA6-720D-44B4-B48C-909F4DD98783}|QuizMaster.csproj|solutionrelative:dbhelper.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
} }
], ],
"DocumentGroupContainers": [ "DocumentGroupContainers": [
@ -48,7 +64,7 @@
"RelativeDocumentMoniker": "Form1.cs", "RelativeDocumentMoniker": "Form1.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Form1.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\Form1.cs",
"RelativeToolTip": "Form1.cs", "RelativeToolTip": "Form1.cs",
"ViewState": "AQIAAAYEAAAAAAAAAAAcwBEEAAApAAAA", "ViewState": "AQIAAE8AAAAAAAAAAAAuwGUAAABVAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-26T01:54:54.164Z", "WhenOpened": "2025-05-26T01:54:54.164Z",
"IsPinned": true, "IsPinned": true,
@ -56,28 +72,16 @@
}, },
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 1, "DocumentIndex": 2,
"Title": "Form2.cs", "Title": "Form2.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form2.cs", "DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form2.cs",
"RelativeDocumentMoniker": "Form2.cs", "RelativeDocumentMoniker": "Form2.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Form2.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\Form2.cs",
"RelativeToolTip": "Form2.cs", "RelativeToolTip": "Form2.cs",
"ViewState": "AQIAAGMAAAAAAAAAAAAAAGwAAAAxAAAA", "ViewState": "AQIAAF0AAAAAAAAAAAAIwG8AAAAUAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T06:28:48.666Z", "WhenOpened": "2025-10-02T06:28:48.666Z",
"IsPinned": true, "IsPinned": true
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "Form1.cs [Design]",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form1.cs",
"RelativeDocumentMoniker": "Form1.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Form1.cs [Design]",
"RelativeToolTip": "Form1.cs [Design]",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-08T02:47:37.753Z"
}, },
{ {
"$type": "Bookmark", "$type": "Bookmark",
@ -95,52 +99,111 @@
"$type": "Bookmark", "$type": "Bookmark",
"Name": "ST:17:0:{e8034f19-ab72-4f06-83fd-f6832b41aa63}" "Name": "ST:17:0:{e8034f19-ab72-4f06-83fd-f6832b41aa63}"
}, },
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "Question.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Response\\Question.cs",
"RelativeDocumentMoniker": "Response\\Question.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Response\\Question.cs",
"RelativeToolTip": "Response\\Question.cs",
"ViewState": "AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-14T06:24:38.262Z"
},
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 3, "DocumentIndex": 3,
"Title": "Quiz.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Response\\Quiz.cs",
"RelativeDocumentMoniker": "Response\\Quiz.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Response\\Quiz.cs",
"RelativeToolTip": "Response\\Quiz.cs",
"ViewState": "AQIAAAAAAAAAAAAAAAAAAA0AAAAWAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-13T08:59:40.254Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "ExecuteExcelService.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Service\\ExecuteExcelService.cs",
"RelativeDocumentMoniker": "Service\\ExecuteExcelService.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Service\\ExecuteExcelService.cs",
"RelativeToolTip": "Service\\ExecuteExcelService.cs",
"ViewState": "AQIAAA8AAAAAAAAAAAAAADAAAAAAAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-13T03:41:10.651Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "QuestionOption.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Response\\QuestionOption.cs",
"RelativeDocumentMoniker": "Response\\QuestionOption.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Response\\QuestionOption.cs",
"RelativeToolTip": "Response\\QuestionOption.cs",
"ViewState": "AQIAAAAAAAAAAAAAAAAAAAYAAAAWAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-10T06:59:55.277Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "ExcelValidator.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Service\\ExcelValidator.cs",
"RelativeDocumentMoniker": "Service\\ExcelValidator.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Service\\ExcelValidator.cs",
"RelativeToolTip": "Service\\ExcelValidator.cs",
"ViewState": "AQIAAFcAAAAAAAAAAAAQwEwAAAAJAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-10T03:12:07.301Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "DataUtil.cs", "Title": "DataUtil.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Service\\DataUtil.cs", "DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Service\\DataUtil.cs",
"RelativeDocumentMoniker": "Service\\DataUtil.cs", "RelativeDocumentMoniker": "Service\\DataUtil.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Service\\DataUtil.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\Service\\DataUtil.cs",
"RelativeToolTip": "Service\\DataUtil.cs", "RelativeToolTip": "Service\\DataUtil.cs",
"ViewState": "AQIAABsBAAAAAAAAAAAwwEABAAAAAAAA", "ViewState": "AQIAAAAAAAAAAAAAAAAAABEAAAAJAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-29T01:45:12.308Z" "WhenOpened": "2025-05-29T01:45:12.308Z"
}, },
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 2, "DocumentIndex": 1,
"Title": "Form1.Designer.cs", "Title": "Form1.Designer.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form1.Designer.cs", "DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form1.Designer.cs",
"RelativeDocumentMoniker": "Form1.Designer.cs", "RelativeDocumentMoniker": "Form1.Designer.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Form1.Designer.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\Form1.Designer.cs",
"RelativeToolTip": "Form1.Designer.cs", "RelativeToolTip": "Form1.Designer.cs",
"ViewState": "AQIAABoBAAAAAAAAAAAYwCcBAAAwAAAA", "ViewState": "AQIAAE8AAAAAAAAAAAAuwGUAAAAxAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-09-30T04:14:48.709Z", "WhenOpened": "2025-09-30T04:14:48.709Z"
"EditorCaption": ""
}, },
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 5, "DocumentIndex": 10,
"Title": "Form2.Designer.cs", "Title": "Form2.Designer.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form2.Designer.cs", "DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\Form2.Designer.cs",
"RelativeDocumentMoniker": "Form2.Designer.cs", "RelativeDocumentMoniker": "Form2.Designer.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\Form2.Designer.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\Form2.Designer.cs",
"RelativeToolTip": "Form2.Designer.cs", "RelativeToolTip": "Form2.Designer.cs",
"ViewState": "AQIAAAAAAAAAAAAAAAAAACcAAAAbAAAA", "ViewState": "AQIAAAAAAAAAAAAAAAAAADYAAAAgAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-10-02T06:23:49.946Z" "WhenOpened": "2025-10-02T06:23:49.946Z"
}, },
{ {
"$type": "Document", "$type": "Document",
"DocumentIndex": 6, "DocumentIndex": 7,
"Title": "DbHelper.cs", "Title": "DbHelper.cs",
"DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\DbHelper.cs", "DocumentMoniker": "E:\\QuizMaster\\QuizMaster\\DbHelper.cs",
"RelativeDocumentMoniker": "DbHelper.cs", "RelativeDocumentMoniker": "DbHelper.cs",
"ToolTip": "E:\\QuizMaster\\QuizMaster\\DbHelper.cs", "ToolTip": "E:\\QuizMaster\\QuizMaster\\DbHelper.cs",
"RelativeToolTip": "DbHelper.cs", "RelativeToolTip": "DbHelper.cs",
"ViewState": "AQIAADAAAAAAAAAAAAAYwBIAAAAPAAAA", "ViewState": "AQIAADYAAAAAAAAAAAAYwBIAAAAPAAAA",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2025-05-26T01:35:21.294Z" "WhenOpened": "2025-05-26T01:35:21.294Z"
} }

View File

@ -98,8 +98,8 @@
panelcb.BorderStyle = BorderStyle.FixedSingle; panelcb.BorderStyle = BorderStyle.FixedSingle;
panelcb.Location = new Point(50, 50); panelcb.Location = new Point(50, 50);
panelcb.Name = "panelcb"; panelcb.Name = "panelcb";
panelcb.Size = new Size(600, 300); panelcb.Size = new Size(770, 200);
panelcb.MaximumSize = new Size(600, 300); panelcb.MaximumSize = new Size(770, 400);
panelcb.TabIndex = 1; panelcb.TabIndex = 1;
panelcb.AutoScroll = true; panelcb.AutoScroll = true;
panelcb.AutoSize = true; panelcb.AutoSize = true;
@ -113,8 +113,8 @@
panelop.Controls.Add(lblNumberOfCopies); panelop.Controls.Add(lblNumberOfCopies);
panelop.Controls.Add(txtNumberOfCopies); panelop.Controls.Add(txtNumberOfCopies);
panelop.Name = "panelop"; panelop.Name = "panelop";
panelop.Size = new Size(600, 100); panelop.Size = new Size(770, 100);
panelop.MaximumSize = new Size(600, 300); panelop.MaximumSize = new Size(770, 300);
panelop.TabIndex = 2; panelop.TabIndex = 2;
panelop.AutoScroll = true; panelop.AutoScroll = true;
panelop.AutoSize = true; panelop.AutoSize = true;

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@
this.txtResult.Location = new System.Drawing.Point(0, 0); this.txtResult.Location = new System.Drawing.Point(0, 0);
this.txtResult.Name = "txtResult"; this.txtResult.Name = "txtResult";
this.txtResult.Size = new System.Drawing.Size(800, 450); this.txtResult.Size = new System.Drawing.Size(800, 450);
this.txtResult.WordWrap = true;
this.txtResult.TabIndex = 0; this.txtResult.TabIndex = 0;
// //
//btnExoortWord //btnExoortWord
@ -49,8 +50,8 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450); this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.txtResult);
this.Controls.Add(this.btnExportWord); this.Controls.Add(this.btnExportWord);
this.Controls.Add(this.txtResult);
this.Name = "Form2"; this.Name = "Form2";
this.Text = "Kết quả Generate"; this.Text = "Kết quả Generate";
this.ResumeLayout(false); this.ResumeLayout(false);

View File

@ -4,7 +4,6 @@ using System.Windows.Forms;
using Xceed.Words.NET; using Xceed.Words.NET;
using Xceed.Document.NET; using Xceed.Document.NET;
namespace QuizMaster namespace QuizMaster
{ {
public partial class Form2 : Form public partial class Form2 : Form
@ -16,7 +15,7 @@ namespace QuizMaster
{ {
InitializeComponent(); InitializeComponent();
_content = content; _content = content;
txtResult.Text = content; txtResult.Text = _content.Replace("\n", Environment.NewLine);
} }
private void btnExportWord_Click(object sender, EventArgs e) private void btnExportWord_Click(object sender, EventArgs e)

View File

@ -6,5 +6,7 @@
public string Question { get; set; } public string Question { get; set; }
public string Option { get; set; } public string Option { get; set; }
public int IsCorrect { get; set; } // 1 = đúng, 0 = sai public int IsCorrect { get; set; } // 1 = đúng, 0 = sai
public string DoKho { get; set; }
} }
} }

View File

@ -10,6 +10,7 @@ namespace QuizMaster.Response
public string? B { get; set; } public string? B { get; set; }
public string? C { get; set; } public string? C { get; set; }
public string? D { get; set; } public string? D { get; set; }
public required string DapAn { get; set; } public required List<string> DapAn { get; set; } = new List<string>();
public string DoKho { get; set; }
} }
} }

View File

@ -21,7 +21,7 @@ namespace QuizMaster.Service
// 1. Kiểm tra xem câu hỏi đã tồn tại chưa // 1. Kiểm tra xem câu hỏi đã tồn tại chưa
var questionIdCmd = new NpgsqlCommand(@" var questionIdCmd = new NpgsqlCommand(@"
SELECT id FROM question WHERE content = @content AND category_id = @category_id; SELECT id FROM question WHERE content = @content AND category_id = @category_id;
", conn); ", conn);
questionIdCmd.Parameters.AddWithValue("content", questionContent); questionIdCmd.Parameters.AddWithValue("content", questionContent);
@ -42,9 +42,9 @@ namespace QuizMaster.Service
try try
{ {
var cmd = new NpgsqlCommand(@" var cmd = new NpgsqlCommand(@"
SELECT name SELECT name
FROM category FROM category
ORDER BY name; ORDER BY name;
", conn); ", conn);
await using var reader = await cmd.ExecuteReaderAsync(); await using var reader = await cmd.ExecuteReaderAsync();
@ -75,11 +75,11 @@ namespace QuizMaster.Service
try try
{ {
var cmd = new NpgsqlCommand(@" var cmd = new NpgsqlCommand(@"
SELECT c.name SELECT c.name
FROM category c FROM category c
INNER JOIN department d ON c.department_id = d.id INNER JOIN department d ON c.department_id = d.id
WHERE d.name = @departmentName WHERE d.name = @departmentName
ORDER BY c.name; ORDER BY c.name;
", conn); ", conn);
cmd.Parameters.AddWithValue("departmentName", departmentName); cmd.Parameters.AddWithValue("departmentName", departmentName);
@ -103,6 +103,48 @@ namespace QuizMaster.Service
return categories; return categories;
} }
public static async Task<List<CategoryInfo>> GetTotalOptionQuestionsAsync(List<string> lstCategory, string DoKho)
{
var categories = new List<CategoryInfo>();
if (lstCategory == null || lstCategory.Count == 0)
{
return categories;
}
string whereCate = string.Join(",", lstCategory.Select(c => $"'{c.Replace("'", "''")}'"));
await using var conn = new NpgsqlConnection(DbHelper.connectionString);
await conn.OpenAsync();
try
{
var cmd = new NpgsqlCommand($@"
SELECT c.name, COUNT(q.id) AS Total
FROM question q
INNER JOIN category c ON q.category_id = c.id
WHERE c.name IN ({whereCate})
AND q.level = '{DoKho}'
GROUP BY c.name
ORDER BY c.name;
", conn);
await using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
categories.Add(new CategoryInfo
{
Category = reader.GetString(0),
Total = reader.GetInt32(1)
});
}
}
catch (Exception ex)
{
MessageBox.Show($"Lỗi khi lấy danh sách số câu khó: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
throw;
}
return categories;
}
public static async Task<List<CategoryInfo>> GetTotalCategoriesAsync(List<string> lstCategory) public static async Task<List<CategoryInfo>> GetTotalCategoriesAsync(List<string> lstCategory)
{ {
var categories = new List<CategoryInfo>(); var categories = new List<CategoryInfo>();
@ -117,12 +159,12 @@ namespace QuizMaster.Service
try try
{ {
var cmd = new NpgsqlCommand($@" var cmd = new NpgsqlCommand($@"
SELECT c.name, COUNT(q.id) AS Total SELECT c.name, COUNT(q.id) AS Total
FROM question q FROM question q
INNER JOIN category c ON q.category_id = c.id INNER JOIN category c ON q.category_id = c.id
WHERE c.name IN ({whereCate}) WHERE c.name IN ({whereCate})
GROUP BY c.name GROUP BY c.name
ORDER BY c.name; ORDER BY c.name;
", conn); ", conn);
await using var reader = await cmd.ExecuteReaderAsync(); await using var reader = await cmd.ExecuteReaderAsync();
@ -144,7 +186,6 @@ namespace QuizMaster.Service
return categories; return categories;
} }
public static async Task<List<QuestionOption>> getAllQA(string Category) public static async Task<List<QuestionOption>> getAllQA(string Category)
{ {
var questionOption = new List<QuestionOption>(); var questionOption = new List<QuestionOption>();
@ -154,13 +195,12 @@ namespace QuizMaster.Service
try try
{ {
var cmd = new NpgsqlCommand(@" var cmd = new NpgsqlCommand(@"SELECT q.id, q.content, q.level, o.option_text, o.is_correct
SELECT q.id, q.content, o.option_text, o.is_correct FROM question q
FROM question q INNER JOIN question_option o ON o.question_id = q.id
INNER JOIN question_option o ON o.question_id = q.id INNER JOIN category c ON q.category_id = c.id
INNER JOIN category c ON q.category_id = c.id WHERE c.name = @category
WHERE c.name = @category ORDER BY q.id, o.id
ORDER BY q.id, o.id;
", conn); ", conn);
cmd.Parameters.AddWithValue("category", Category); cmd.Parameters.AddWithValue("category", Category);
@ -170,10 +210,11 @@ namespace QuizMaster.Service
{ {
questionOption.Add(new QuestionOption questionOption.Add(new QuestionOption
{ {
QuestionId = reader.IsDBNull(0) ? 0 : reader.GetInt32(0), QuestionId = reader.GetInt32(0),
Question = reader.IsDBNull(1) ? string.Empty : reader.GetString(1), Question = reader.GetString(1),
Option = reader.IsDBNull(2) ? string.Empty : reader.GetString(2), DoKho = reader.IsDBNull(2) ? "" : reader.GetString(2),
IsCorrect = reader.IsDBNull(3) ? 0 : reader.GetInt32(3) Option = reader.IsDBNull(3) ? string.Empty : reader.GetString(3),
IsCorrect = reader.IsDBNull(4) ? 0 : reader.GetInt32(4)
}); });
} }
} }
@ -186,37 +227,78 @@ namespace QuizMaster.Service
return questionOption; return questionOption;
} }
public static async Task<bool> InsertQuestionAndOptionsAsync(List<Quiz> questions) public static async Task<bool> InsertQuestionAndOptionsAsync(List<Quiz> questions)
{ {
if (questions == null || questions.Count == 0)
return false;
await using var conn = new NpgsqlConnection(DbHelper.connectionString); await using var conn = new NpgsqlConnection(DbHelper.connectionString);
await conn.OpenAsync(); await conn.OpenAsync();
await using var transaction = await conn.BeginTransactionAsync(); await using var transaction = await conn.BeginTransactionAsync();
try try
{ {
// --- 1. Cache categoryId ---
var categoryCache = new Dictionary<string, int>();
foreach (var q in questions) foreach (var q in questions)
{ {
// Lấy category_id if (!categoryCache.ContainsKey(q.LinhVuc))
var cmdCategory = new NpgsqlCommand("SELECT id FROM category WHERE name = @name", conn, transaction); {
cmdCategory.Parameters.AddWithValue("name", q.LinhVuc); var cmdCategory = new NpgsqlCommand(
var categoryIdObj = await cmdCategory.ExecuteScalarAsync(); "SELECT id FROM category WHERE name = @name", conn, transaction);
if (categoryIdObj == null) cmdCategory.Parameters.AddWithValue("name", q.LinhVuc);
throw new Exception($"Category '{q.LinhVuc}' không tồn tại."); var categoryIdObj = await cmdCategory.ExecuteScalarAsync();
if (categoryIdObj == null)
throw new Exception($"Category '{q.LinhVuc}' không tồn tại.");
categoryCache[q.LinhVuc] = (int)categoryIdObj;
}
}
int categoryId = (int)categoryIdObj; // --- 2. Batch insert questions ---
var batchQuestions = new NpgsqlBatch(conn, transaction);
foreach (var q in questions)
{
var cmd = new NpgsqlBatchCommand(@"
INSERT INTO question (content, category_id, level)
VALUES (@content, @categoryId, @level)
RETURNING id;
");
cmd.Parameters.AddWithValue("content", q.CauHoi);
cmd.Parameters.AddWithValue("categoryId", categoryCache[q.LinhVuc]);
cmd.Parameters.AddWithValue("level", q.DoKho);
batchQuestions.BatchCommands.Add(cmd);
}
// Insert Question // --- 3. Execute batch question và lấy questionId ---
var cmdQuestion = new NpgsqlCommand(@" var questionIds = new List<int>();
INSERT INTO question (content, category_id) await using (var reader = await batchQuestions.ExecuteReaderAsync())
VALUES (@content, @categoryId) {
RETURNING id; foreach (var _ in questions)
", conn, transaction); {
cmdQuestion.Parameters.AddWithValue("content", q.CauHoi); if (!await reader.ReadAsync())
cmdQuestion.Parameters.AddWithValue("categoryId", categoryId); throw new Exception("Không lấy đủ questionId từ batch.");
questionIds.Add(reader.GetInt32(0));
// Chuyển sang result set tiếp theo (mỗi INSERT RETURNING tạo 1 result set)
await reader.NextResultAsync();
}
}
// --- 4. Batch insert options ---
var batchOptions = new NpgsqlBatch(conn, transaction);
for (int i = 0; i < questions.Count; i++)
{
var q = questions[i];
var qid = questionIds[i];
if (q.DapAn == null || q.DapAn.Count == 0 || q.DapAn.Any(a => !"ABCD".Contains(a)))
{
var dapAnStr = q.DapAn != null ? string.Join(",", q.DapAn) : "(null)";
throw new Exception($"Câu hỏi '{q.CauHoi}' có đáp án không hợp lệ: {dapAnStr}");
}
int questionId = (int)await cmdQuestion.ExecuteScalarAsync();
// Insert QuestionOption
var options = new Dictionary<string, string?> var options = new Dictionary<string, string?>
{ {
{ "A", q.A }, { "A", q.A },
@ -227,19 +309,20 @@ namespace QuizMaster.Service
foreach (var opt in options) foreach (var opt in options)
{ {
var cmdOption = new NpgsqlCommand(@" var cmdOption = new NpgsqlBatchCommand(@"
INSERT INTO question_option (question_id, option_text, is_correct) INSERT INTO question_option (question_id, option_text, is_correct)
VALUES (@questionId, @optionText, @isCorrect); VALUES (@questionId, @optionText, @isCorrect);
", conn, transaction); ");
cmdOption.Parameters.AddWithValue("questionId", qid);
cmdOption.Parameters.AddWithValue("questionId", questionId);
cmdOption.Parameters.AddWithValue("optionText", opt.Value ?? (object)DBNull.Value); cmdOption.Parameters.AddWithValue("optionText", opt.Value ?? (object)DBNull.Value);
cmdOption.Parameters.AddWithValue("isCorrect", opt.Key == q.DapAn ? 1 : 0); cmdOption.Parameters.AddWithValue("isCorrect", q.DapAn.Contains(opt.Key) ? 1 : 0);
batchOptions.BatchCommands.Add(cmdOption);
await cmdOption.ExecuteNonQueryAsync();
} }
} }
// --- 5. Execute batch options ---
await batchOptions.ExecuteNonQueryAsync();
await transaction.CommitAsync(); await transaction.CommitAsync();
return true; return true;
} }
@ -250,6 +333,7 @@ namespace QuizMaster.Service
} }
} }
public static async Task<int> GetTotalQuestionCount() public static async Task<int> GetTotalQuestionCount()
{ {
string query = "SELECT COUNT(*) FROM Question"; string query = "SELECT COUNT(*) FROM Question";
@ -267,26 +351,6 @@ namespace QuizMaster.Service
return Convert.ToInt32(result); return Convert.ToInt32(result);
} }
//public static async Task<List<Question>> GetRandomQuestions(int requestedCount)
//{
// int totalCount = await GetTotalQuestionCount();
// if (requestedCount <= 0)
// {
// MessageBox.Show("Vui lòng nhập số lượng lớn hơn 0.", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Warning);
// return new List<Question>();
// }
// if (requestedCount > totalCount)
// {
// MessageBox.Show($"Chỉ có {totalCount} câu hỏi trong cơ sở dữ liệu.", "Thông báo", MessageBoxButtons.OK, MessageBoxIcon.Warning);
// return new List<Question>();
// }
// string query = $"SELECT * FROM Question ORDER BY RANDOM() LIMIT {requestedCount}";
// return await DbHelper.ExecuteSelectQuery<Question>(query);
//}
public static async Task<List<string>> GetDepartmentsAsync() public static async Task<List<string>> GetDepartmentsAsync()
{ {
List<string> deplist = new List<string>(); List<string> deplist = new List<string>();
@ -316,5 +380,6 @@ namespace QuizMaster.Service
return deplist; return deplist;
} }
} }
} }

View File

@ -2,8 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using OfficeOpenXml; using OfficeOpenXml;
using Xceed.Document.NET;
namespace QuizMaster.Service namespace QuizMaster.Service
{ {
@ -11,7 +13,7 @@ namespace QuizMaster.Service
{ {
private static readonly List<string> ExpectedHeaders = new() private static readonly List<string> ExpectedHeaders = new()
{ {
"Lĩnh vực", "Câu hỏi", "A", "B", "C", "D", "Đáp án" "Lĩnh vực", "Câu hỏi", "A", "B", "C", "D", "Đáp án", "Độ khó"
}; };
public sealed class RowData public sealed class RowData
@ -23,11 +25,14 @@ namespace QuizMaster.Service
public string B { get; init; } = string.Empty; public string B { get; init; } = string.Empty;
public string C { get; init; } = string.Empty; public string C { get; init; } = string.Empty;
public string D { get; init; } = string.Empty; public string D { get; init; } = string.Empty;
public string Answer { get; init; } = string.Empty; public List<string> Answers { get; init; } = new List<string>();
public string Level { get; init; } = string.Empty;
} }
public static List<string> ValidateHeaders(OfficeOpenXml.ExcelWorksheet worksheet) public static List<string> ValidateHeaders(OfficeOpenXml.ExcelWorksheet worksheet)
{ {
var errors = new List<string>(); var errors = new List<string>();
for (int i = 0; i < ExpectedHeaders.Count; i++) for (int i = 0; i < ExpectedHeaders.Count; i++)
@ -50,6 +55,9 @@ namespace QuizMaster.Service
if (string.IsNullOrWhiteSpace(data.Question)) if (string.IsNullOrWhiteSpace(data.Question))
errors.Add($"Dòng {data.Row}: 'Câu hỏi' không được trống"); errors.Add($"Dòng {data.Row}: 'Câu hỏi' không được trống");
if (string.IsNullOrWhiteSpace(data.Level))
errors.Add($"Dòng {data.Row}: 'Độ khó' không được trống");
return errors; return errors;
} }
@ -71,31 +79,40 @@ namespace QuizMaster.Service
public static List<string> ValidateRowAnswer(RowData data) public static List<string> ValidateRowAnswer(RowData data)
{ {
var errors = new List<string>(); var errors = new List<string>();
if (string.IsNullOrWhiteSpace(data.Answer))
if (data.Answers == null || data.Answers.Count == 0)
{ {
errors.Add($"Dòng {data.Row}: 'Đáp án' không được trống"); errors.Add($"Dòng {data.Row}: 'Đáp án' không được trống");
return errors; return errors;
} }
var valid = new[] { "A", "B", "C", "D" }; var validOptions = new HashSet<string> { "A", "B", "C", "D" };
if (!valid.Contains(data.Answer))
{
errors.Add($"Dòng {data.Row}: 'Đáp án' phải là A, B, C hoặc D (hiện tại là '{data.Answer}')");
return errors;
}
bool answerPointsToEmpty = (data.Answer == "A" && string.IsNullOrWhiteSpace(data.A)) || foreach (var ans in data.Answers)
(data.Answer == "B" && string.IsNullOrWhiteSpace(data.B)) ||
(data.Answer == "C" && string.IsNullOrWhiteSpace(data.C)) ||
(data.Answer == "D" && string.IsNullOrWhiteSpace(data.D));
if (answerPointsToEmpty)
{ {
errors.Add($"Dòng {data.Row}: 'Đáp án' {data.Answer} không hợp lệ vì phương án {data.Answer} đang trống"); if (!validOptions.Contains(ans))
{
errors.Add($"Dòng {data.Row}: 'Đáp án' phải là A, B, C hoặc D (phát hiện '{ans}')");
continue;
}
bool isEmptyOption =
(ans == "A" && string.IsNullOrWhiteSpace(data.A)) ||
(ans == "B" && string.IsNullOrWhiteSpace(data.B)) ||
(ans == "C" && string.IsNullOrWhiteSpace(data.C)) ||
(ans == "D" && string.IsNullOrWhiteSpace(data.D));
if (isEmptyOption)
{
errors.Add($"Dòng {data.Row}: 'Đáp án' {ans} không hợp lệ vì phương án {ans} đang trống");
}
} }
return errors; return errors;
} }
//________________________________________________________________________________________________
public static List<string> ValidateRowDuplicate(RowData data, Dictionary<string, int> questionOptionDict) public static List<string> ValidateRowDuplicate(RowData data, Dictionary<string, int> questionOptionDict)
{ {
var errors = new List<string>(); var errors = new List<string>();
@ -134,6 +151,12 @@ namespace QuizMaster.Service
var firstCell = worksheet.Cells[row, 1]; var firstCell = worksheet.Cells[row, 1];
if (string.IsNullOrWhiteSpace(firstCell.Text)) break; // Dòng trống → kết thúc if (string.IsNullOrWhiteSpace(firstCell.Text)) break; // Dòng trống → kết thúc
var answerText = worksheet.Cells[row, 7].Text.Trim().ToUpper();
var answers = Regex.Matches(answerText.ToUpper(), "[ABCD]")
.Select(m => m.Value.Trim())
.Distinct()
.ToList();
var data = new RowData var data = new RowData
{ {
Row = row, Row = row,
@ -143,7 +166,8 @@ namespace QuizMaster.Service
B = worksheet.Cells[row, 4].Text.Trim(), B = worksheet.Cells[row, 4].Text.Trim(),
C = worksheet.Cells[row, 5].Text.Trim(), C = worksheet.Cells[row, 5].Text.Trim(),
D = worksheet.Cells[row, 6].Text.Trim(), D = worksheet.Cells[row, 6].Text.Trim(),
Answer = worksheet.Cells[row, 7].Text.Trim().ToUpper() Answers = answers,
Level = worksheet.Cells[row, 8].Text.Trim()
}; };
errors.AddRange(ValidateRowRequired(data)); errors.AddRange(ValidateRowRequired(data));

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using OfficeOpenXml; using OfficeOpenXml;
using QuizMaster.Response; using QuizMaster.Response;
@ -21,6 +22,8 @@ namespace QuizMaster.Service
for (int row = 2; row <= rowCount; row++) // Bắt đầu từ dòng 2, bỏ dòng tiêu đề for (int row = 2; row <= rowCount; row++) // Bắt đầu từ dòng 2, bỏ dòng tiêu đề
{ {
var answerText = worksheet.Cells[row, 7].Text.Trim().ToUpper();
var ch = new Quiz var ch = new Quiz
{ {
LinhVuc = worksheet.Cells[row, 1].Text, LinhVuc = worksheet.Cells[row, 1].Text,
@ -29,13 +32,16 @@ namespace QuizMaster.Service
B = worksheet.Cells[row, 4].Text, B = worksheet.Cells[row, 4].Text,
C = worksheet.Cells[row, 5].Text, C = worksheet.Cells[row, 5].Text,
D = worksheet.Cells[row, 6].Text, D = worksheet.Cells[row, 6].Text,
DapAn = worksheet.Cells[row, 7].Text
};
DapAn = Regex.Matches(answerText.ToUpper(), "[ABCD]")
.Select(m => m.Value.Trim())
.Distinct()
.ToList(),
DoKho = worksheet.Cells[row, 8].Text
};
result.Add(ch); result.Add(ch);
} }
} }
return result; return result;
} }
} }