source: branches/eraser6/pluginsRewrite/Eraser/SchedulerPanel.cs @ 2285

Revision 2276, 20.8 KB checked in by lowjoel, 4 years ago (diff)

Implement drag&drop for the task properties window as well. Because of code duplication, extract the common drag&drop code to TaskDragDropHelper?.cs Fixes #384: Task Edit dialog: drag and drop

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Drawing;
26using System.Text;
27using System.Windows.Forms;
28
29using System.Globalization;
30using System.IO;
31using System.ComponentModel;
32
33using Eraser.Manager;
34using Eraser.Util;
35using Eraser.DefaultPlugins;
36using Microsoft.Samples;
37using ProgressChangedEventArgs = Eraser.Util.ProgressChangedEventArgs;
38
39namespace Eraser
40{
41    internal partial class SchedulerPanel : Eraser.BasePanel
42    {
43        public SchedulerPanel()
44        {
45            InitializeComponent();
46            Theming.ApplyTheme(schedulerDefaultMenu);
47            if (!IsHandleCreated)
48                CreateHandle();
49
50            //Populate the scheduler list-view with the current task list
51            ExecutorTasksCollection tasks = Program.eraserClient.Tasks;
52            foreach (Task task in tasks)
53                CreateTask(task);
54
55            //Hook the event machinery to our class. Handle the task Added and Removed
56            //events.
57            Program.eraserClient.TaskAdded += TaskAdded;
58            Program.eraserClient.TaskDeleted += TaskDeleted;
59        }
60
61        #region List-View Task Management
62        private void CreateTask(Task task)
63        {
64            //Add the item to the list view
65            ListViewItem item = scheduler.Items.Add(task.UIText);
66            item.SubItems.Add(string.Empty);
67            item.SubItems.Add(string.Empty);
68
69            //Set the tag of the item so we know which task on the list-view
70            //corresponds to the physical task object.
71            item.Tag = task;
72
73            //Add our event handlers to the task
74            task.TaskStarted += TaskStarted;
75            task.ProgressChanged += TaskProgressChanged;
76            task.TaskFinished += TaskFinished;
77
78            //Show the fields on the list view
79            UpdateTask(item);
80
81            //If the task is set to Run Immediately, then show that status.
82            if (task.Schedule == Schedule.RunNow)
83                item.SubItems[1].Text = S._("Queued for execution");
84        }
85
86        private void UpdateTask(ListViewItem item)
87        {
88            //Get the task object
89            Task task = (Task)item.Tag;
90
91            //Set the task name
92            item.Text = task.UIText;
93
94            //Set the next run time of the task
95            if (task.Queued)
96                item.SubItems[1].Text = S._("Queued for execution");
97            else if (task.Executing)
98                TaskStarted(task, new TaskEventArgs(task));
99            else if (task.Schedule is RecurringSchedule)
100                item.SubItems[1].Text = ((task.Schedule as RecurringSchedule).NextRun.
101                    ToString("f", CultureInfo.CurrentCulture));
102            else if (task.Schedule == Schedule.RunManually || task.Schedule == Schedule.RunNow)
103                item.SubItems[1].Text = S._("Not queued");
104            else
105                item.SubItems[1].Text = task.Schedule.UIText;
106
107            //Set the group of the task.
108            CategorizeTask(task, item);
109        }
110
111        private void CategorizeTask(Task task)
112        {
113            CategorizeTask(task, GetTaskItem(task));
114        }
115
116        private void CategorizeTask(Task task, ListViewItem item)
117        {
118            if (task.Schedule == Schedule.RunNow || task.Schedule == Schedule.RunManually)
119                item.Group = scheduler.Groups["manual"];
120            else if (task.Schedule == Schedule.RunOnRestart)
121                item.Group = scheduler.Groups["restart"];
122            else
123                item.Group = scheduler.Groups["recurring"];
124        }
125        #endregion
126
127        #region Task Event handlers
128        /// <summary>
129        /// Handles the Task Added event.
130        /// </summary>
131        private void TaskAdded(object sender, TaskEventArgs e)
132        {
133            if (InvokeRequired)
134            {
135                Invoke((EventHandler<TaskEventArgs>)TaskAdded, sender, e);
136                return;
137            }
138
139            //Display a balloon notification if the parent frame has been minimised.
140            MainForm parent = (MainForm)FindForm();
141            if (parent != null && (parent.WindowState == FormWindowState.Minimized || !parent.Visible))
142            {
143                parent.ShowNotificationBalloon(S._("New task added"), S._("{0} " +
144                    "has just been added to the list of tasks.", e.Task.UIText),
145                    ToolTipIcon.Info);
146            }
147
148            CreateTask(e.Task);
149        }
150
151        private void DeleteSelectedTasks()
152        {
153            if (MessageBox.Show(this, S._("Are you sure you want to delete the selected tasks?"),
154                    S._("Eraser"), MessageBoxButtons.YesNo, MessageBoxIcon.Question,
155                    MessageBoxDefaultButton.Button1, Localisation.IsRightToLeft(this) ?
156                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0
157                ) != DialogResult.Yes)
158            {
159                return;
160            }
161
162            foreach (ListViewItem item in scheduler.SelectedItems)
163            {
164                Task task = (Task)item.Tag;
165                if (!task.Executing)
166                    Program.eraserClient.Tasks.Remove(task);
167            }
168        }
169
170        /// <summary>
171        /// Handles the task deleted event.
172        /// </summary>
173        private void TaskDeleted(object sender, TaskEventArgs e)
174        {
175            if (InvokeRequired)
176            {
177                Invoke((EventHandler<TaskEventArgs>)TaskDeleted, sender, e);
178                return;
179            }
180
181            foreach (ListViewItem item in scheduler.Items)
182                if (((Task)item.Tag) == e.Task)
183                {
184                    scheduler.Items.Remove(item);
185                    break;
186                }
187
188            PositionProgressBar();
189        }
190
191        /// <summary>
192        /// Handles the task start event.
193        /// </summary>
194        /// <param name="e">The task event object.</param>
195        void TaskStarted(object sender, EventArgs e)
196        {
197            if (InvokeRequired)
198            {
199                Invoke((EventHandler)TaskStarted, sender, e);
200                return;
201            }
202
203            //Get the list view item
204            Task task = (Task)sender;
205            ListViewItem item = GetTaskItem(task);
206
207            //Update the status.
208            item.SubItems[1].Text = S._("Running...");
209
210            //Show the progress bar
211            schedulerProgress.Tag = item;
212            schedulerProgress.Visible = true;
213            schedulerProgress.Value = 0;
214            PositionProgressBar();
215        }
216
217        /// <summary>
218        /// Handles the progress event by the task.
219        /// </summary>
220        void TaskProgressChanged(object sender, ProgressChangedEventArgs e)
221        {
222            //Make sure we handle the event in the main thread as this requires
223            //GUI calls.
224            if (InvokeRequired)
225            {
226                Invoke((EventHandler<ProgressChangedEventArgs>)TaskProgressChanged, sender, e);
227                return;
228            }
229
230            //Update the progress bar
231            ErasureTarget target = (ErasureTarget)sender;
232            SteppedProgressManager progress = target.Task.Progress;
233            schedulerProgress.Style = progress.ProgressIndeterminate ?
234                ProgressBarStyle.Marquee : ProgressBarStyle.Continuous;
235           
236            if (!progress.ProgressIndeterminate)
237                schedulerProgress.Value = (int)(progress.Progress * 1000.0);
238        }
239
240        /// <summary>
241        /// Handles the task completion event.
242        /// </summary>
243        void TaskFinished(object sender, EventArgs e)
244        {
245            if (InvokeRequired)
246            {
247                Invoke((EventHandler)TaskFinished, sender, e);
248                return;
249            }
250
251            //Get the list view item
252            Task task = (Task)sender;
253            ListViewItem item = GetTaskItem(task);
254            if (item == null)
255                return;
256
257            //Hide the progress bar
258            if (schedulerProgress.Tag != null && schedulerProgress.Tag == item)
259            {
260                schedulerProgress.Tag = null;
261                schedulerProgress.Visible = false;
262            }
263
264            //Get the exit status of the task.
265            LogLevel highestLevel = task.Log.Last().Highest;
266
267            //Show a balloon to inform the user
268            MainForm parent = (MainForm)FindForm();
269            if (parent.WindowState == FormWindowState.Minimized || !parent.Visible)
270            {
271                string message = null;
272                ToolTipIcon icon = ToolTipIcon.None;
273
274                switch (highestLevel)
275                {
276                    case LogLevel.Warning:
277                        message = S._("The task {0} has completed with warnings.", task.UIText);
278                        icon = ToolTipIcon.Warning;
279                        break;
280                    case LogLevel.Error:
281                        message = S._("The task {0} has completed with errors.", task.UIText);
282                        icon = ToolTipIcon.Error;
283                        break;
284                    case LogLevel.Fatal:
285                        message = S._("The task {0} did not complete.", task.UIText);
286                        icon = ToolTipIcon.Error;
287                        break;
288                    default:
289                        message = S._("The task {0} has completed.", task.UIText);
290                        icon = ToolTipIcon.Info;
291                        break;
292                }
293
294                parent.ShowNotificationBalloon(S._("Task executed"), message,
295                    icon);
296            }
297
298            //If the user requested us to remove completed one-time tasks, do so.
299            if (EraserSettings.Get().ClearCompletedTasks &&
300                (task.Schedule == Schedule.RunNow) && highestLevel < LogLevel.Warning)
301            {
302                Program.eraserClient.Tasks.Remove(task);
303            }
304
305            //Otherwise update the UI
306            else
307            {
308                switch (highestLevel)
309                {
310                    case LogLevel.Warning:
311                        item.SubItems[2].Text = S._("Completed with warnings");
312                        break;
313                    case LogLevel.Error:
314                        item.SubItems[2].Text = S._("Completed with errors");
315                        break;
316                    case LogLevel.Fatal:
317                        item.SubItems[2].Text = S._("Not completed");
318                        break;
319                    default:
320                        item.SubItems[2].Text = S._("Completed");
321                        break;
322                }
323
324                //Recategorize the task. Do not assume the task has maintained the
325                //category since run-on-restart tasks will be changed to immediately
326                //run tasks.
327                CategorizeTask(task, item);
328
329                //Update the status of the task.
330                UpdateTask(item);
331            }
332        }
333        #endregion
334
335        #region List-View Event handlers
336        /// <summary>
337        /// Occurs when the user presses a key on the list view.
338        /// </summary>
339        /// <param name="sender">The list view which triggered the event.</param>
340        /// <param name="e">Event argument.</param>
341        private void scheduler_KeyDown(object sender, KeyEventArgs e)
342        {
343            if (e.KeyCode == Keys.Delete)
344                DeleteSelectedTasks();
345        }
346
347        /// <summary>
348        /// Occurs when the user double-clicks a scheduler item. This will result
349        /// in the log viewer being called, or the progress dialog to be displayed.
350        /// </summary>
351        /// <param name="sender">The list view which triggered the event.</param>
352        /// <param name="e">Event argument.</param>
353        private void scheduler_ItemActivate(object sender, EventArgs e)
354        {
355            if (scheduler.SelectedItems.Count == 0)
356                return;
357
358            ListViewItem item = scheduler.SelectedItems[0];
359            if (((Task)item.Tag).Executing)
360                using (ProgressForm form = new ProgressForm((Task)item.Tag))
361                    form.ShowDialog();
362            else
363                editTaskToolStripMenuItem_Click(sender, e);
364        }
365
366        /// <summary>
367        /// Occurs when the user drags a file over the scheduler
368        /// </summary>
369        private void scheduler_DragEnter(object sender, DragEventArgs e)
370        {
371            //Get the list of files.
372            bool recycleBin = false;
373            List<string> paths = new List<string>(TaskDragDropHelper.GetFiles(e, out recycleBin));
374
375            //We also need to determine if we are importing task lists.
376            bool isTaskList = !recycleBin;
377
378            for (int i = 0; i < paths.Count; ++i)
379            {
380                //Does this item exclude a task list import?
381                if (isTaskList && Path.GetExtension(paths[i]) != ".ersx")
382                    isTaskList = false;
383
384                //Just use the file name/directory name.
385                paths[i] = Path.GetFileName(paths[i]);
386            }
387
388            //Add the recycle bin if it was dropped.
389            if (recycleBin)
390                paths.Add(S._("Recycle Bin"));
391
392            string description = null;
393            if (paths.Count == 0)
394            {
395                e.Effect = DragDropEffects.None;
396                description = S._("Cannot erase the selected items");
397            }
398            else if (isTaskList)
399            {
400                e.Effect = DragDropEffects.Copy;
401                description = S._("Import tasks from {0}");
402            }
403            else
404            {
405                e.Effect = DragDropEffects.Move;
406                description = S._("Erase {0}");
407            }
408
409            TaskDragDropHelper.OnDragEnter(this, e, description, paths);
410        }
411
412        private void scheduler_DragLeave(object sender, EventArgs e)
413        {
414            DropTargetHelper.DragLeave(this);
415        }
416
417        private void scheduler_DragOver(object sender, DragEventArgs e)
418        {
419            DropTargetHelper.DragOver(new Point(e.X, e.Y), e.Effect);
420        }
421
422        /// <summary>
423        /// Occurs when the user drops a file into the scheduler.
424        /// </summary>
425        private void scheduler_DragDrop(object sender, DragEventArgs e)
426        {
427            TaskDragDropHelper.OnDrop(e);
428            if (e.Effect == DragDropEffects.None)
429                return;
430
431            //Determine our action.
432            bool recycleBin = false;
433            List<string> paths = new List<string>(TaskDragDropHelper.GetFiles(e, out recycleBin));
434            bool isTaskList = !recycleBin;
435
436            foreach (string path in paths)
437            {
438                //Does this item exclude a task list import?
439                if (isTaskList && Path.GetExtension(path) != ".ersx")
440                {
441                    isTaskList = false;
442                    break;
443                }
444            }
445
446            if (isTaskList)
447            {
448                foreach (string file in paths)
449                    using (FileStream stream = new FileStream(file, FileMode.Open,
450                        FileAccess.Read, FileShare.Read))
451                    {
452                        try
453                        {
454                            Program.eraserClient.Tasks.LoadFromStream(stream);
455                        }
456                        catch (InvalidDataException ex)
457                        {
458                            MessageBox.Show(S._("Could not import task list from {0}. The " +
459                                "error returned was: {1}", file, ex.Message), S._("Eraser"),
460                                MessageBoxButtons.OK, MessageBoxIcon.Error,
461                                MessageBoxDefaultButton.Button1,
462                                Localisation.IsRightToLeft(this) ?
463                                    MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
464                        }
465                    }
466            }
467            else
468            {
469                //Create a task with the default settings
470                Task task = new Task();
471                foreach (ErasureTarget target in TaskDragDropHelper.GetTargets(paths, recycleBin))
472                    task.Targets.Add(target);
473
474                //If the task has no targets, we should not go on.
475                if (task.Targets.Count == 0)
476                    return;
477
478                //Schedule the task dialog to be shown (to get to the event loop so that
479                //ComCtl32.dll v6 is used.)
480                BeginInvoke((Action<Task>)scheduler_DragDropConfirm, task);
481            }
482        }
483
484        /// <summary>
485        /// Called when a set of files are dropped into Eraser and to let the user
486        /// decide what to do with the collection.
487        /// </summary>
488        /// <param name="task">The task which requires confirmation.</param>
489        private void scheduler_DragDropConfirm(Task task)
490        {
491            //Add the task, asking the user for his intent.
492            DialogResult action = DialogResult.No;
493            if (TaskDialog.IsAvailableOnThisOS)
494            {
495                TaskDialog dialog = new TaskDialog();
496                dialog.WindowTitle = S._("Eraser");
497                dialog.MainIcon = TaskDialogIcon.Information;
498                dialog.MainInstruction = S._("You have dropped a set of files and folders into Eraser. What do you want to do with them?");
499                dialog.AllowDialogCancellation = true;
500                dialog.Buttons = new TaskDialogButton[] {
501                    new TaskDialogButton((int)DialogResult.Yes, S._("Erase the selected items\nSchedules the selected items for immediate erasure.")),
502                    new TaskDialogButton((int)DialogResult.OK, S._("Create a new Task\nA task will be created containing the selected items.")),
503                    new TaskDialogButton((int)DialogResult.No, S._("Cancel the drag-and-drop operation"))
504                };
505                dialog.RightToLeftLayout = Localisation.IsRightToLeft(this);
506                dialog.UseCommandLinks = true;
507                action = (DialogResult)dialog.Show(this);
508            }
509            else
510            {
511                action = MessageBox.Show(S._("Are you sure you wish to erase the selected "
512                    + "items?"), S._("Eraser"), MessageBoxButtons.YesNo,
513                    MessageBoxIcon.Question, MessageBoxDefaultButton.Button2,
514                    Localisation.IsRightToLeft(this) ?
515                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
516            }
517
518            switch (action)
519            {
520                case DialogResult.OK:
521                    task.Schedule = Schedule.RunManually;
522                    goto case DialogResult.Yes;
523
524                case DialogResult.Yes:
525                    Program.eraserClient.Tasks.Add(task);
526                    break;
527            }
528        }
529
530        /// <summary>
531        /// Occurs when the user right-clicks the list view.
532        /// </summary>
533        /// <param name="sender">The list view which generated this event.</param>
534        /// <param name="e">Event argument.</param>
535        private void schedulerMenu_Opening(object sender, CancelEventArgs e)
536        {
537            //If nothing's selected, show the Scheduler menu which just allows users to
538            //create new tasks (like from the toolbar)
539            if (scheduler.SelectedItems.Count == 0)
540            {
541                schedulerDefaultMenu.Show(schedulerMenu.Left, schedulerMenu.Top);
542                e.Cancel = true;
543                return;
544            }
545
546            bool aTaskNotQueued = false;
547            bool aTaskExecuting = false;
548            foreach (ListViewItem item in scheduler.SelectedItems)
549            {
550                Task task = (Task)item.Tag;
551                aTaskNotQueued = aTaskNotQueued || (!task.Queued && !task.Executing);
552                aTaskExecuting = aTaskExecuting || task.Executing;
553            }
554
555            runNowToolStripMenuItem.Enabled = aTaskNotQueued;
556            cancelTaskToolStripMenuItem.Enabled = aTaskExecuting;
557
558            editTaskToolStripMenuItem.Enabled = scheduler.SelectedItems.Count == 1 &&
559                !((Task)scheduler.SelectedItems[0].Tag).Executing &&
560                !((Task)scheduler.SelectedItems[0].Tag).Queued;
561            deleteTaskToolStripMenuItem.Enabled = !aTaskExecuting;
562        }
563
564        /// <summary>
565        /// Occurs when the user selects the New Task context menu item.
566        /// </summary>
567        /// <param name="sender">The menu which generated this event.</param>
568        /// <param name="e">Event argument.</param>
569        private void newTaskToolStripMenuItem_Click(object sender, EventArgs e)
570        {
571            using (TaskPropertiesForm form = new TaskPropertiesForm())
572            {
573                if (form.ShowDialog() == DialogResult.OK)
574                {
575                    Task task = form.Task;
576                    Program.eraserClient.Tasks.Add(task);
577                }
578            }
579        }
580
581        /// <summary>
582        /// Occurs whent the user selects the Run Now context menu item.
583        /// </summary>
584        /// <param name="sender">The menu which generated this event.</param>
585        /// <param name="e">Event argument.</param>
586        private void runNowToolStripMenuItem_Click(object sender, EventArgs e)
587        {
588            foreach (ListViewItem item in scheduler.SelectedItems)
589            {
590                //Queue the task
591                Task task = (Task)item.Tag;
592                if (!task.Executing && !task.Queued)
593                {
594                    Program.eraserClient.QueueTask(task);
595
596                    //Update the UI
597                    item.SubItems[1].Text = S._("Queued for execution");
598                }
599            }
600        }
601
602        /// <summary>
603        /// Occurs whent the user selects the Cancel Task context menu item.
604        /// </summary>
605        /// <param name="sender">The menu which generated this event.</param>
606        /// <param name="e">Event argument.</param>
607        private void cancelTaskToolStripMenuItem_Click(object sender, EventArgs e)
608        {
609            foreach (ListViewItem item in scheduler.SelectedItems)
610            {
611                //Queue the task
612                Task task = (Task)item.Tag;
613                if (task.Executing || task.Queued)
614                {
615                    task.Cancel();
616
617                    //Update the UI
618                    item.SubItems[1].Text = string.Empty;
619                }
620            }
621        }
622
623        /// <summary>
624        /// Occurs when the user selects the View Task Log context menu item.
625        /// </summary>
626        /// <param name="sender">The menu item which generated this event.</param>
627        /// <param name="e">Event argument.</param>
628        private void viewTaskLogToolStripMenuItem_Click(object sender, EventArgs e)
629        {
630            if (scheduler.SelectedItems.Count != 1)
631                return;
632
633            ListViewItem item = scheduler.SelectedItems[0];
634            using (LogForm form = new LogForm((Task)item.Tag))
635                form.ShowDialog();
636        }
637
638        /// <summary>
639        /// Occurs when the user selects the Edit Task context menu item.
640        /// </summary>
641        /// <param name="sender">The menu item which generated this event.</param>
642        /// <param name="e">Event argument.</param>
643        private void editTaskToolStripMenuItem_Click(object sender, EventArgs e)
644        {
645            if (scheduler.SelectedItems.Count != 1 ||
646                ((Task)scheduler.SelectedItems[0].Tag).Executing ||
647                ((Task)scheduler.SelectedItems[0].Tag).Queued)
648            {
649                return;
650            }
651
652            //Make sure that the task is not being executed, or else. This can
653            //be done in the Client library, but there will be no effect on the
654            //currently running task.
655            ListViewItem item = scheduler.SelectedItems[0];
656            Task task = (Task)item.Tag;
657            if (task.Executing)
658                return;
659
660            //Edit the task.
661            using (TaskPropertiesForm form = new TaskPropertiesForm())
662            {
663                form.Task = task;
664                if (form.ShowDialog() == DialogResult.OK)
665                {
666                    task = form.Task;
667
668                    //Update the list view
669                    UpdateTask(item);
670                }
671            }
672        }
673
674        /// <summary>
675        /// Occurs when the user selects the Delete Task context menu item.
676        /// </summary>
677        /// <param name="sender">The menu item which generated this event.</param>
678        /// <param name="e">Event argument.</param>
679        private void deleteTaskToolStripMenuItem_Click(object sender, EventArgs e)
680        {
681            DeleteSelectedTasks();
682        }
683        #endregion
684
685        #region Item management
686        /// <summary>
687        /// Retrieves the ListViewItem for the given task.
688        /// </summary>
689        /// <param name="task">The task object whose list view entry is being sought.</param>
690        /// <returns>A ListViewItem for the given task object.</returns>
691        private ListViewItem GetTaskItem(Task task)
692        {
693            foreach (ListViewItem item in scheduler.Items)
694                if (item.Tag == task)
695                    return item;
696
697            return null;
698        }
699
700        /// <summary>
701        /// Maintains the position of the progress bar.
702        /// </summary>
703        private void PositionProgressBar()
704        {
705            if (schedulerProgress.Tag == null)
706                return;
707
708            Rectangle rect = ((ListViewItem)schedulerProgress.Tag).SubItems[2].Bounds;
709            rect.Offset(2, 2);
710            schedulerProgress.Location = rect.Location;
711            schedulerProgress.Size = rect.Size;
712        }
713
714        private void scheduler_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
715        {
716            e.DrawDefault = true;
717            if (schedulerProgress.Tag != null)
718                PositionProgressBar();
719        }
720
721        private void scheduler_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
722        {
723            e.DrawDefault = true;
724        }
725        #endregion
726    }
727}
Note: See TracBrowser for help on using the repository browser.