source: trunk/eraser/Eraser/SchedulerPanel.cs @ 2326

Revision 2326, 20.8 KB checked in by lowjoel, 19 months ago (diff)

The title of the balloon tip should be "Task Completed" and not "Task Executed" for clarity. This was suggested by Jackjack in #388.
Fixes #388: balloon tips wording post wiping

  • 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 completed"), 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.