프로젝트

일반

사용자정보

통계
| 개정판:

hytos / ID2.Manager / ID2.Manager.Compare / Main.cs @ 4142eefa

이력 | 보기 | 이력해설 | 다운로드 (21.3 KB)

1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel;
4
using System.Data;
5
using System.Drawing;
6
using System.Linq;
7
using System.Text;
8
using System.Threading.Tasks;
9
using System.Windows.Forms;
10

    
11
using System.IO;
12
using System.Reflection;
13
using System.Diagnostics;
14

    
15
using ID2.Manager.Controls;
16
using ID2.Manager.Classes;
17

    
18
using Telerik.WinControls;
19
using Telerik.WinControls.UI;
20

    
21
namespace ID2.Manager
22
{
23
    public partial class Main : RadRibbonForm
24
    {
25
        protected override CreateParams CreateParams
26
        {
27
            get
28
            {
29
                CreateParams cp = base.CreateParams;
30
                cp.ExStyle |= 0x02000000;
31
                return cp;
32
            }
33
        }
34

    
35
//#if DEBUG
36
//        Telerik.WinControls.RadControlSpy.RadControlSpyForm radControlSpyForm = new Telerik.WinControls.RadControlSpy.RadControlSpyForm();
37
//#endif
38
        private readonly Color _SummaryColor = Color.FromArgb(255, 108, 55);
39

    
40
        public Main()
41
        {
42
            InitializeComponent();
43
            var verification = new Controls.Verification(this.radProgressBarElement) { Dock = DockStyle.Fill };
44
            this.LayoutValidation.Controls.Add(verification);
45
            this.DockMainTabStrip.Select();
46

    
47
            var appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.ProductName);
48

    
49
            if (!Directory.Exists(appDataPath))
50
            {
51
                Directory.CreateDirectory(appDataPath);
52
            }
53

    
54
            #region 테마 설정
55
            this.radMenuItemControlDefault.Click += RadMenuItemTheme_Click;
56
            this.radMenuItemOffice2013Dark.Click += RadMenuItemTheme_Click;
57
            this.radMenuItemTelerikMetro.Click += RadMenuItemTheme_Click;
58
            this.radMenuItemVisualStudio2012Dark.Click += RadMenuItemTheme_Click;
59
            this.radMenuItemVisualStudio2012Light.Click += RadMenuItemTheme_Click;
60
            this.radMenuItemWindows8.Click += RadMenuItemTheme_Click;
61
            #endregion
62

    
63
            this.Initialize();
64
        }
65

    
66
        /// <summary>
67
        /// 사용자가 선택한 테마를 적용한다.
68
        /// </summary>
69
        /// <param name="sender"></param>
70
        /// <param name="e"></param>
71
        private void RadMenuItemTheme_Click(object sender, EventArgs e)
72
        {
73
            string theme = (sender as RadMenuItem).Text;
74
            if(theme != this.radDropDownButtonElementTheme.Text)
75
            {
76
                this.radDropDownButtonElementTheme.Text = theme;
77
                Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "App", "Theme", theme);
78
                if (!string.IsNullOrEmpty(theme))
79
                {
80
                    Telerik.WinControls.ThemeResolutionService.ApplicationThemeName = theme;
81
                    Program.ThemeName = theme;
82
                }
83
            }
84
        }
85

    
86
        #region Init, Load
87
        private void Initialize()
88
        {
89
            this.ID2ManagerRadRibbonBar.Expanded = false;
90
        }
91

    
92
        protected override void OnLoad(EventArgs e)
93
        {
94
            try
95
            {
96
                #region 테마를 읽어 적용
97
                string theme = Classes.ID2Helper.IniReadValue(Program.IniFilePath, "App", "Theme");
98
                if (!string.IsNullOrEmpty(theme))
99
                {
100
                    Telerik.WinControls.ThemeResolutionService.ApplicationThemeName = theme;
101
                    Program.ThemeName = theme;
102
                }
103
                #endregion
104

    
105
                this.radDropDownButtonElementTheme.Text = Program.ThemeName;
106
                this.radBrowseEditorAutoCADFolder.ValueChanged += RadBrowseEditorAutoCADFolder_ValueChanged;
107
                this.radBrowseEditorAVEVAFolder.ValueChanged += RadBrowseEditorAVEVAFolder_ValueChanged;
108
                this.radGridViewDocument.DataBindingComplete += RadGridViewDocument_DataBindingComplete;
109
                this.radGridViewDocument.ViewRowFormatting += RadGridViewDocument_ViewRowFormatting;
110
                this.radGridViewDocument.CellClick += RadGridViewDocument_CellClick;
111
                this.radButtonElementCompare.Click += RadButtonElementCompare_Click;
112
                this.radButtonElementConfiguration.Click += RadButtonElementConfiguration_Click;
113

    
114
                this.radButtonElementRefreshCommand.Click += RadButtonElementRefreshCommand_Click;
115

    
116
                Program.AutoCADFolder = Classes.ID2Helper.IniReadValue(Program.IniFilePath, "Path", "AutoCAD Folder");
117
                if (!string.IsNullOrEmpty(Program.AutoCADFolder)) this.radBrowseEditorAutoCADFolder.Value = Program.AutoCADFolder;
118

    
119
                Program.AVEVAPIDFolder = Classes.ID2Helper.IniReadValue(Program.IniFilePath, "Path", "AVEVA P&ID Folder");
120
                if (!string.IsNullOrEmpty(Program.AVEVAPIDFolder)) this.radBrowseEditorAVEVAFolder.Value = Program.AVEVAPIDFolder;
121
            }
122
            catch (Exception ex)
123
            {
124
                Program.logger.Error($"An exception occurred from {MethodBase.GetCurrentMethod().Name}", ex);
125
                RadMessageBox.Show("Failed to load project.", Application.ProductName, MessageBoxButtons.OK, RadMessageIcon.Error);
126
            }
127

    
128
            base.OnLoad(e);
129
        }
130

    
131
        /// <summary>
132
        /// 도면 리스트를 갱신한다.
133
        /// </summary>
134
        /// <param name="sender"></param>
135
        /// <param name="e"></param>
136
        private void RadButtonElementRefreshCommand_Click(object sender, EventArgs e)
137
        {
138
            try
139
            {
140
                this.Cursor = Cursors.WaitCursor;
141

    
142
                var AutoCADFiles = Directory.GetFiles(Program.AutoCADFolder, "*.dwg");
143
                var AVEVAFiles = Directory.GetFiles(Program.AVEVAPIDFolder, "*.dwg");
144

    
145
                MakeDocumentList(AutoCADFiles, AVEVAFiles);
146
            }
147
            finally
148
            {
149
                this.Cursor = Cursors.Default;
150
            }
151
        }
152

    
153
        /// <summary>
154
        /// 환경 설정 창을 띄운다.
155
        /// </summary>
156
        /// <param name="sender"></param>
157
        /// <param name="e"></param>
158
        private void RadButtonElementConfiguration_Click(object sender, EventArgs e)
159
        {
160
            using (var frm = new Forms.ExceptLayer())
161
            {
162
                if (DialogResult.OK == frm.ShowDialog(this))
163
                {
164
                    string ExceptLayers = string.Join(",", Forms.ExceptLayer.ExceptLayers.Select(x => x.Name));
165
                    Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Verification", "Except Layers", ExceptLayers);
166

    
167
                    string ExceptLayersVisible = string.Join(",", Forms.ExceptLayer.ExceptLayers.Select(x => x.Visible));
168
                    Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Verification", "Except Layers Visible", ExceptLayersVisible);
169

    
170
                    string LineLayers = string.Join(",", Forms.ExceptLayer.LineLayers.Select(x => x.Name));
171
                    Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Verification", "Line Layers", LineLayers);
172

    
173
                    Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Verification", "Length Tolerance Ratio", 
174
                        Forms.ExceptLayer.LengthToleranceRatio.ToString());
175
                }
176
            }
177
        }
178

    
179
        private void RadButtonElementCompare_Click(object sender, EventArgs e)
180
        {
181
            try
182
            {
183
                this.Cursor = Cursors.WaitCursor;
184

    
185
                var docs = this.radGridViewDocument.Rows.Where(x => (x.DataBoundItem is Document doc) && doc.Checked).
186
                    Select(x => (x.DataBoundItem as Document)).ToList();
187
                var verification = this.LayoutValidation.Controls[0] as Controls.Verification;
188
                verification.CompareDrawings(docs, true);
189
            }
190
            finally
191
            {
192
                this.Cursor = Cursors.Default;
193
            }
194
        }
195

    
196
        private void RadGridViewDocument_CellClick(object sender, GridViewCellEventArgs e)
197
        {
198
            if (e.ColumnIndex == 1 || e.ColumnIndex == 2) 
199
            {
200
                if (this.radGridViewDocument.SelectedRows.Any() && this.radGridViewDocument.SelectedRows[0].DataBoundItem is Document doc)
201
                {
202
                    this.Cursor = Cursors.WaitCursor;
203
                    try
204
                    {
205
                        var verification = this.LayoutValidation.Controls[0] as Controls.Verification;
206
                        verification.CompareDrawings(new List<Document>() { doc });
207
                    }
208
                    finally
209
                    {
210
                        this.Cursor = Cursors.Default;
211
                    }
212
                }
213
            }
214
        }
215

    
216
        private void RadGridViewDocument_ViewRowFormatting(object sender, RowFormattingEventArgs e)
217
        {
218
            if(e.RowElement is GridRowElement)
219
            {
220
                if (e.RowElement.RowInfo.DataBoundItem is Document doc)
221
                {
222
                    if (!doc.IsValid)
223
                    {
224
                        e.RowElement.ForeColor = Color.Gray;
225
                        e.RowElement.Font = Program.UnmatchedFont;
226
                    }
227
                    else
228
                    {
229
                        e.RowElement.ResetValue(LightVisualElement.ForeColorProperty, ValueResetFlags.Local);
230
                        e.RowElement.Font = Program.MatchedFont;
231
                    }
232
                }
233
            }
234
            else
235
            {
236
                e.RowElement.ResetValue(LightVisualElement.ForeColorProperty, ValueResetFlags.Local);
237
                e.RowElement.ResetValue(LightVisualElement.FontProperty, ValueResetFlags.Local);
238
            }
239
        }
240

    
241
        private void RadGridViewDocument_DataBindingComplete(object sender, GridViewBindingCompleteEventArgs e)
242
        {
243
            radGridViewDocument.BestFitColumns();
244
        }
245

    
246
        private void RadBrowseEditorAVEVAFolder_ValueChanged(object sender, EventArgs e)
247
        {
248
            if(Directory.Exists(this.radBrowseEditorAVEVAFolder.Value))
249
            {
250
                Program.AVEVAPIDFolder = this.radBrowseEditorAVEVAFolder.Value;
251
                Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Path", "AVEVA P&ID Folder", Program.AVEVAPIDFolder);
252

    
253
                var AVEVAFiles = Directory.GetFiles(Program.AVEVAPIDFolder, "*.dwg");
254

    
255
                var AutoCADFiles = new List<string>();
256
                if (Directory.Exists(this.radBrowseEditorAutoCADFolder.Value))
257
                {
258
                    AutoCADFiles.AddRange(Directory.GetFiles(this.radBrowseEditorAutoCADFolder.Value, "*.dwg"));
259
                }
260

    
261
                MakeDocumentList(AutoCADFiles, AVEVAFiles);
262
            }
263
        }
264

    
265
        private void RadBrowseEditorAutoCADFolder_ValueChanged(object sender, EventArgs e)
266
        {
267
            if (Directory.Exists(this.radBrowseEditorAutoCADFolder.Value))
268
            {
269
                Program.AutoCADFolder = this.radBrowseEditorAutoCADFolder.Value;
270
                Classes.ID2Helper.IniWriteValue(Program.IniFilePath, "Path", "AutoCAD Folder", Program.AutoCADFolder);
271

    
272
                var AutoCADFiles = Directory.GetFiles(Program.AutoCADFolder, "*.dwg");
273

    
274
                var AVEVAFiles = new List<string>();
275
                if (Directory.Exists(this.radBrowseEditorAVEVAFolder.Value))
276
                {
277
                    AVEVAFiles.AddRange(Directory.GetFiles(this.radBrowseEditorAVEVAFolder.Value, "*.dwg"));
278
                }
279

    
280
                MakeDocumentList(AutoCADFiles, AVEVAFiles);
281
            }
282
        }
283

    
284
        private void MakeDocumentList(IList<string> AutoCADFiles, IList<string> AVEVAFiles)
285
        {
286
            radGridViewDocument.DataSource = null;
287

    
288
            Program.Documents.Clear();
289
            AutoCADFiles.ForAll(x => Program.Documents.Add(new Document(Path.GetFileNameWithoutExtension(x).ToUpper()) { AutoCADFileName = Path.GetFileNameWithoutExtension(x).ToUpper()}));
290

    
291
            var addings = new List<Document>();
292
            foreach(var file in AVEVAFiles)
293
            {
294
                string FileName = Path.GetFileNameWithoutExtension(file).ToUpper();
295
                var doc = Program.Documents.Find(x => x.AutoCADFileName == FileName);
296
                if(doc != null)
297
                {
298
                    doc.AVEVAFileName = FileName;
299
                }
300
                else
301
                {
302
                    addings.Add(new Document(FileName) { AVEVAFileName = FileName });
303
                }
304
            }
305

    
306
            addings.ForAll(x => Program.Documents.Add(x));
307

    
308
            radGridViewDocument.DataSource = Program.Documents;
309
        }
310
        #endregion
311

    
312
        private void RadDropDownList_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e)
313
        {
314
            if (sender is RadDropDownList ddl)
315
            {
316
                if (ddl.SelectedValue != null)
317
                {
318
                    if (string.IsNullOrEmpty(ddl.SelectedValue.ToString()))
319
                    {
320
                        ddl.BackColor = Color.White;
321
                        ddl.ForeColor = Color.Black;
322
                        ddl.Font = new Font("Segoe UI", 8.25F, FontStyle.Regular);
323
                    }
324
                    else
325
                    {
326
                        ddl.BackColor = Color.DarkSlateBlue;
327
                        ddl.ForeColor = Color.Yellow;
328
                        ddl.Font = new Font("Segoe UI", 8.25F, FontStyle.Bold);
329
                    }
330
                }
331
            }
332
        }
333

    
334
        private void RadTextBoxDocumentNo_KeyUp(object sender, KeyEventArgs e)
335
        {
336
            if (sender is RadTextBox txtBox)
337
            {
338
                if (txtBox.Text.Length > 0)
339
                {
340
                    txtBox.BackColor = Color.DarkSlateBlue;
341
                    txtBox.ForeColor = Color.Yellow;
342
                    txtBox.Font = new Font("Segoe UI", 8.25F, FontStyle.Bold);
343
                }
344
                else
345
                {
346
                    txtBox.BackColor = Color.White;
347
                    txtBox.ForeColor = Color.Black;
348
                    txtBox.Font = new Font("Segoe UI", 8.25F, FontStyle.Regular);
349
                }
350
            }
351
        }
352

    
353
        /*
354
        private void GetCheckedList()
355
        {
356
            RadGridView grid = this.radGridViewDocuments;
357

    
358
            //var checkers = grid.Rows.Where(x =>
359
            //{
360
            //    return x.Cells["Checked"].Value != null && Convert.ToBoolean(x.Cells["Checked"].Value);
361
            //}).Select(x => x.DataBoundItem as Documents).ToList();
362

    
363
            //var viewRows = new Queue<GridViewRowInfo>(this.radGridViewDocuments.MasterTemplate.DataView.Where(x => x.Cells["spq");
364
            var viewRows = grid.MasterTemplate.DataView;
365

    
366
            var checkers = viewRows.Where(x =>
367
            {
368
                return x.Cells["Checked"].Value != null && Convert.ToBoolean(x.Cells["Checked"].Value);
369
            }).ToList();
370

    
371
            var rows = new GridViewDataRowInfo[checkers.Count];
372
            checkers.CopyTo(rows, 0);
373

    
374
            if (rows.Length > 0)
375
            {
376
                grid.BeginUpdate();
377

    
378
                int nLoop = 0;
379
                rows.ForAll(x =>
380
                {
381
                    grid.Rows.Remove(rows[nLoop]);
382
                    nLoop++;
383
                });
384

    
385
                grid.EndUpdate();
386
            }
387

    
388

    
389
            //grid.Rows.re grid.Rows.ToList().Intersect(checkers)
390

    
391

    
392
            //RadMessageBox.Show($"{checkers.Count()}", Globals.Name, MessageBoxButtons.OK, RadMessageIcon.Info);
393
            //RadMessageBox.Show($"{this.documents.Count}", Globals.Name, MessageBoxButtons.OK, RadMessageIcon.Info);
394
            this.lbSelectAndTotal.Text = $"{grid.MasterTemplate.DataView.Count} / {this.TotalCount} (Selected / Total)";
395
        }
396
        */
397

    
398
        #region ColumnGroup
399
        private void InitColumnGroupsViewDefinition(RadGridView gridView)
400
        {
401
            ColumnGroupsViewDefinition columnGroupsView = new ColumnGroupsViewDefinition();
402

    
403
            List<string> chkColNames = new List<string>() { "Checked" };
404

    
405
            List<string> docLinkColNames = new List<string>() { "AutoCADLink", "ID2Connection", "PDFLink", "MarkupLink", "AVEVALink", "Compare" };
406
            List<string> docInfoColNames = new List<string>() { "RefProjectCode", "System", "SubSystemCode", "DocumentNo", "PersonInCharge", "Worker", "AVEVAPersonInCharge", "AVEVAWorker", "JobLevel", "RevisonNo" };
407
            List<string> rvToColNames = new List<string>() { "ToIsDiscussion", "ToRemarks", "ToCreator", "ToCapture" };
408
            List<string> rvFrColNames = new List<string>() { "FrReviewStatus", "FrRemarks", "FrCreator", "FrCapture" };
409
            List<string> wkID2ColNames = new List<string>() { "ID2StartDate", "ID2EndDate", "ID2Status", "ID2Issues", "ID2Capture", "ReplyModifications", "ReplyRequester" };
410
            List<string> wkAVEVAColNames = new List<string>() { "IsConvert", "AVEVAConvertDate", "AVEVAWorkDate", "AVEVAStatus", "AVEVAIssues" };
411
            List<string> valProdColNames = new List<string>() { "AVEVAReviewDate", "ProdReviewer", "ProdIsResult", "ProdRemarks" };
412
            List<string> valCntColNames = new List<string>() { "ClientReviewer", "ClientIsResult", "ClientRemarks" };
413
            List<string> dtColNames = new List<string>() { "DTIsGateWay", "DTIsImport", "DTIsRegSystem", "DTRemarks" };
414

    
415
            //체크
416
            GridViewColumnGroup chkColGrp = new GridViewColumnGroup("√") { IsPinned = true };
417
            GridViewColumnGroupRow chkColGrpRow = new GridViewColumnGroupRow();
418
            chkColGrpRow.ColumnNames.AddRange(chkColNames);
419
            chkColGrp.Rows.Add(chkColGrpRow);
420

    
421
            //도면
422
            GridViewColumnGroup docColGrp = new GridViewColumnGroup("도면 ");
423
            GridViewColumnGroup docLinkColGrp = new GridViewColumnGroup("프로그램 연동");
424
            GridViewColumnGroup docInfoColGrp = new GridViewColumnGroup("도면정보");
425

    
426
            GridViewColumnGroupRow docLinkColGrpRow = new GridViewColumnGroupRow();
427
            docLinkColGrpRow.ColumnNames.AddRange(docLinkColNames);
428

    
429
            GridViewColumnGroupRow docInfoColGrpRow = new GridViewColumnGroupRow();
430
            docInfoColGrpRow.ColumnNames.AddRange(docInfoColNames);
431

    
432
            docLinkColGrp.Rows.Add(docLinkColGrpRow);
433
            docColGrp.Groups.Add(docLinkColGrp);
434
            docInfoColGrp.Rows.Add(docInfoColGrpRow);
435
            docColGrp.Groups.Add(docInfoColGrp);
436

    
437
            //검토
438
            GridViewColumnGroup rvColGrp = new GridViewColumnGroup("검토", "review");
439
            GridViewColumnGroup rvToColGrp = new GridViewColumnGroup("도프텍");
440
            GridViewColumnGroup rvFrColGrp = new GridViewColumnGroup("삼성");
441

    
442
            GridViewColumnGroupRow rvToColGrpRow = new GridViewColumnGroupRow();
443
            rvToColGrpRow.ColumnNames.AddRange(rvToColNames);
444

    
445
            GridViewColumnGroupRow rvFrColGrpRow = new GridViewColumnGroupRow();
446
            rvFrColGrpRow.ColumnNames.AddRange(rvFrColNames);
447

    
448
            rvToColGrp.Rows.Add(rvToColGrpRow);
449
            rvFrColGrp.Rows.Add(rvFrColGrpRow);
450

    
451
            rvColGrp.Groups.Add(rvToColGrp);
452
            rvColGrp.Groups.Add(rvFrColGrp);
453

    
454
            //작업
455
            GridViewColumnGroup wkColGrp = new GridViewColumnGroup("작업", "work");
456
            GridViewColumnGroup wkID2ColGrp = new GridViewColumnGroup("ID2");
457
            GridViewColumnGroup wkAVEVAColGrp = new GridViewColumnGroup("AVEVA");
458

    
459
            GridViewColumnGroupRow wkID2ColGrpRow = new GridViewColumnGroupRow();
460
            wkID2ColGrpRow.ColumnNames.AddRange(wkID2ColNames);
461

    
462
            GridViewColumnGroupRow wkAVEVAColGrpRow = new GridViewColumnGroupRow();
463
            wkAVEVAColGrpRow.ColumnNames.AddRange(wkAVEVAColNames);
464

    
465
            wkID2ColGrp.Rows.Add(wkID2ColGrpRow);
466
            wkAVEVAColGrp.Rows.Add(wkAVEVAColGrpRow);
467

    
468
            wkColGrp.Groups.Add(wkID2ColGrp);
469
            wkColGrp.Groups.Add(wkAVEVAColGrp);
470

    
471
            //Validation
472
            GridViewColumnGroup valColGrp = new GridViewColumnGroup("Validation", "validation");
473
            GridViewColumnGroup valProdColGrp = new GridViewColumnGroup("도프텍");
474
            GridViewColumnGroup valCntColGrp = new GridViewColumnGroup("삼성전자");
475

    
476
            GridViewColumnGroupRow valProdColGrpRow = new GridViewColumnGroupRow();
477
            valProdColGrpRow.ColumnNames.AddRange(valProdColNames);
478

    
479
            GridViewColumnGroupRow valCntColGrpRow = new GridViewColumnGroupRow();
480
            valCntColGrpRow.ColumnNames.AddRange(valCntColNames);
481

    
482
            valProdColGrp.Rows.Add(valProdColGrpRow);
483
            valCntColGrp.Rows.Add(valCntColGrpRow);
484

    
485
            valColGrp.Groups.Add(valProdColGrp);
486
            valColGrp.Groups.Add(valCntColGrp);
487

    
488
            //AVEVA Net
489
            GridViewColumnGroup dtColGrp = new GridViewColumnGroup("AVEVA Net\n(Digital Twin)", "avevanet");
490

    
491
            GridViewColumnGroupRow dtColGrpRow = new GridViewColumnGroupRow();
492
            dtColGrpRow.ColumnNames.AddRange(dtColNames);
493

    
494
            dtColGrp.Rows.Add(dtColGrpRow);
495

    
496
            //Group 추가
497
            columnGroupsView.ColumnGroups.Add(chkColGrp);
498
            columnGroupsView.ColumnGroups.Add(docColGrp);
499
            columnGroupsView.ColumnGroups.Add(wkColGrp);
500
            columnGroupsView.ColumnGroups.Add(rvColGrp);
501
            columnGroupsView.ColumnGroups.Add(valColGrp);
502
            columnGroupsView.ColumnGroups.Add(dtColGrp);
503

    
504
            gridView.MasterTemplate.ViewDefinition = columnGroupsView;
505
        }
506
        #endregion
507
    }
508
}
509

    
510
public class FilterColumn
511
{
512
    public string Name { get; set; }
513
    public string FieldName { get; set; }
514
    public bool IsSelect { get; set; }
515
}
클립보드 이미지 추가 (최대 크기: 500 MB)