v1.0.0.0
20
ProgramQueuer.sln
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||||
|
# Visual Studio 2010
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProgramQueuer", "ProgramQueuer\ProgramQueuer.csproj", "{B1825A4D-32CB-45E2-9CFE-77622DCC1641}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Debug|x86.ActiveCfg = Debug|x86
|
||||||
|
{B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Debug|x86.Build.0 = Debug|x86
|
||||||
|
{B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Release|x86.ActiveCfg = Release|x86
|
||||||
|
{B1825A4D-32CB-45E2-9CFE-77622DCC1641}.Release|x86.Build.0 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
8
ProgramQueuer/App.xaml
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
<Application x:Class="ProgramQueuer.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
16
ProgramQueuer/App.xaml.cs
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ProgramQueuer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for App.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
50
ProgramQueuer/Helpers/BoolToVisibility.cs
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Simple boolean value converter that converts it to a visbility value where true is visible and false is collapsed.
|
||||||
|
/// </summary>
|
||||||
|
[ValueConversion(typeof(bool), typeof(Visibility))]
|
||||||
|
public class BoolToVisibility : IValueConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert from a boolean value of true to Visibility.Visible and false to Visibility.Collapsed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">A boolean value to convert.</param>
|
||||||
|
/// <param name="targetType">The type of the target value. This property is ignored.</param>
|
||||||
|
/// <param name="parameter">Parameter to pass to the converter. This property is ignored.</param>
|
||||||
|
/// <param name="culture">A reference to the target CultureInfo. This property is ignored.</param>
|
||||||
|
/// <returns>A Visibility value of either Visible or Collapsed depending on the value parameter.</returns>
|
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
bool visible = (bool)value;
|
||||||
|
if (visible)
|
||||||
|
return Visibility.Visible;
|
||||||
|
return Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert back from a Visibility value to a boolean value where Visibility.Visible converts to true and Visibility.Collapsed to false.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The visibility value to convert back to a boolean value.</param>
|
||||||
|
/// <param name="targetType">The type of the target value. This property is ignored.</param>
|
||||||
|
/// <param name="parameter">Parameter to pass to the converter. This property is ignored.</param>
|
||||||
|
/// <param name="culture">A reference to the target CultureInfo. This property is ignored.</param>
|
||||||
|
/// <returns>A boolean value representing the visibility of the control.</returns>
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
var visible = (Visibility)value;
|
||||||
|
if (visible == Visibility.Collapsed)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
ProgramQueuer/Helpers/BooleanInverter.cs
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simple boolean converter that inverts the boolean value.
|
||||||
|
/// </summary>
|
||||||
|
[ValueConversion(typeof(bool), typeof(bool))]
|
||||||
|
public class BooleanInverter : IValueConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a boolean value to the inverted value. Seriously, if I have to explain this, then you are in the wrong business.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The boolean value to invert.</param>
|
||||||
|
/// <param name="targetType">The type of the target value. This property is ignored.</param>
|
||||||
|
/// <param name="parameter">Parameter to pass to the converter. This property is ignored.</param>
|
||||||
|
/// <param name="culture">A reference to the target CultureInfo. This property is ignored.</param>
|
||||||
|
/// <returns>An inverted boolean value.</returns>
|
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert an inverted boolean value to it's original value. Basically inverted.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The boolean value to invert back.</param>
|
||||||
|
/// <param name="targetType">The type of the target value. This property is ignored.</param>
|
||||||
|
/// <param name="parameter">Parameter to pass to the converter. This property is ignored.</param>
|
||||||
|
/// <param name="culture">A reference to the target CultureInfo. This property is ignored.</param>
|
||||||
|
/// <returns>An inverted boolean value.</returns>
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
175
ProgramQueuer/Helpers/DragAdorner.cs
Executable file
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright (C) Josh Smith - January 2007
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using System.Windows.Media.Animation;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace WPF.JoshSmith.Adorners
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders a visual which can follow the mouse cursor,
|
||||||
|
/// such as during a drag-and-drop operation.
|
||||||
|
/// </summary>
|
||||||
|
public class DragAdorner : Adorner
|
||||||
|
{
|
||||||
|
#region Data
|
||||||
|
|
||||||
|
private Rectangle child = null;
|
||||||
|
private double offsetLeft = 0;
|
||||||
|
private double offsetTop = 0;
|
||||||
|
|
||||||
|
#endregion // Data
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of DragVisualAdorner.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="adornedElement">The element being adorned.</param>
|
||||||
|
/// <param name="size">The size of the adorner.</param>
|
||||||
|
/// <param name="brush">A brush to with which to paint the adorner.</param>
|
||||||
|
public DragAdorner(UIElement adornedElement, Size size, Brush brush)
|
||||||
|
: base(adornedElement)
|
||||||
|
{
|
||||||
|
Rectangle rect = new Rectangle();
|
||||||
|
rect.Fill = brush;
|
||||||
|
rect.Width = size.Width;
|
||||||
|
rect.Height = size.Height;
|
||||||
|
rect.IsHitTestVisible = false;
|
||||||
|
this.child = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Constructor
|
||||||
|
|
||||||
|
#region Public Interface
|
||||||
|
|
||||||
|
#region GetDesiredTransform
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="transform"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
|
||||||
|
{
|
||||||
|
GeneralTransformGroup result = new GeneralTransformGroup();
|
||||||
|
result.Children.Add(base.GetDesiredTransform(transform));
|
||||||
|
result.Children.Add(new TranslateTransform(this.offsetLeft, this.offsetTop));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // GetDesiredTransform
|
||||||
|
|
||||||
|
#region OffsetLeft
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the horizontal offset of the adorner.
|
||||||
|
/// </summary>
|
||||||
|
public double OffsetLeft
|
||||||
|
{
|
||||||
|
get { return this.offsetLeft; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.offsetLeft = value;
|
||||||
|
UpdateLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // OffsetLeft
|
||||||
|
|
||||||
|
#region SetOffsets
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the location of the adorner in one atomic operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="left"></param>
|
||||||
|
/// <param name="top"></param>
|
||||||
|
public void SetOffsets(double left, double top)
|
||||||
|
{
|
||||||
|
this.offsetLeft = left;
|
||||||
|
this.offsetTop = top;
|
||||||
|
this.UpdateLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // SetOffsets
|
||||||
|
|
||||||
|
#region OffsetTop
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the vertical offset of the adorner.
|
||||||
|
/// </summary>
|
||||||
|
public double OffsetTop
|
||||||
|
{
|
||||||
|
get { return this.offsetTop; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.offsetTop = value;
|
||||||
|
UpdateLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // OffsetTop
|
||||||
|
|
||||||
|
#endregion // Public Interface
|
||||||
|
|
||||||
|
#region Protected Overrides
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="constraint"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override Size MeasureOverride(Size constraint)
|
||||||
|
{
|
||||||
|
this.child.Measure(constraint);
|
||||||
|
return this.child.DesiredSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="finalSize"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override Size ArrangeOverride(Size finalSize)
|
||||||
|
{
|
||||||
|
this.child.Arrange(new Rect(finalSize));
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override Visual GetVisualChild(int index)
|
||||||
|
{
|
||||||
|
return this.child;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override. Always returns 1.
|
||||||
|
/// </summary>
|
||||||
|
protected override int VisualChildrenCount
|
||||||
|
{
|
||||||
|
get { return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Protected Overrides
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
private void UpdateLocation()
|
||||||
|
{
|
||||||
|
AdornerLayer adornerLayer = this.Parent as AdornerLayer;
|
||||||
|
if (adornerLayer != null)
|
||||||
|
adornerLayer.Update(this.AdornedElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Private Helpers
|
||||||
|
}
|
||||||
|
}
|
864
ProgramQueuer/Helpers/ListViewDragDropManager.cs
Executable file
|
@ -0,0 +1,864 @@
|
||||||
|
// Copyright (C) Josh Smith - January 2007
|
||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using WPF.JoshSmith.Adorners;
|
||||||
|
using WPF.JoshSmith.Controls.Utilities;
|
||||||
|
|
||||||
|
namespace WPF.JoshSmith.ServiceProviders.UI
|
||||||
|
{
|
||||||
|
#region ListViewDragDropManager
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the dragging and dropping of ListViewItems in a ListView.
|
||||||
|
/// The ItemType type parameter indicates the type of the objects in
|
||||||
|
/// the ListView's items source. The ListView's ItemsSource must be
|
||||||
|
/// set to an instance of ObservableCollection of ItemType, or an
|
||||||
|
/// Exception will be thrown.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="ItemType">The type of the ListView's items.</typeparam>
|
||||||
|
public class ListViewDragDropManager<ItemType> where ItemType : class
|
||||||
|
{
|
||||||
|
#region Data
|
||||||
|
|
||||||
|
bool canInitiateDrag;
|
||||||
|
DragAdorner dragAdorner;
|
||||||
|
double dragAdornerOpacity;
|
||||||
|
int indexToSelect;
|
||||||
|
bool isDragInProgress;
|
||||||
|
ItemType itemUnderDragCursor;
|
||||||
|
ListView listView;
|
||||||
|
Point ptMouseDown;
|
||||||
|
bool showDragAdorner;
|
||||||
|
|
||||||
|
#endregion // Data
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of ListViewDragManager.
|
||||||
|
/// </summary>
|
||||||
|
public ListViewDragDropManager()
|
||||||
|
{
|
||||||
|
this.canInitiateDrag = false;
|
||||||
|
this.dragAdornerOpacity = 0.7;
|
||||||
|
this.indexToSelect = -1;
|
||||||
|
this.showDragAdorner = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of ListViewDragManager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listView"></param>
|
||||||
|
public ListViewDragDropManager(ListView listView)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
this.ListView = listView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of ListViewDragManager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listView"></param>
|
||||||
|
/// <param name="dragAdornerOpacity"></param>
|
||||||
|
public ListViewDragDropManager(ListView listView, double dragAdornerOpacity)
|
||||||
|
: this(listView)
|
||||||
|
{
|
||||||
|
this.DragAdornerOpacity = dragAdornerOpacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of ListViewDragManager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listView"></param>
|
||||||
|
/// <param name="showDragAdorner"></param>
|
||||||
|
public ListViewDragDropManager(ListView listView, bool showDragAdorner)
|
||||||
|
: this(listView)
|
||||||
|
{
|
||||||
|
this.ShowDragAdorner = showDragAdorner;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Constructors
|
||||||
|
|
||||||
|
#region Public Interface
|
||||||
|
|
||||||
|
#region DragAdornerOpacity
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the opacity of the drag adorner. This property has no
|
||||||
|
/// effect if ShowDragAdorner is false. The default value is 0.7
|
||||||
|
/// </summary>
|
||||||
|
public double DragAdornerOpacity
|
||||||
|
{
|
||||||
|
get { return this.dragAdornerOpacity; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsDragInProgress)
|
||||||
|
throw new InvalidOperationException("Cannot set the DragAdornerOpacity property during a drag operation.");
|
||||||
|
|
||||||
|
if (value < 0.0 || value > 1.0)
|
||||||
|
throw new ArgumentOutOfRangeException("DragAdornerOpacity", value, "Must be between 0 and 1.");
|
||||||
|
|
||||||
|
this.dragAdornerOpacity = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // DragAdornerOpacity
|
||||||
|
|
||||||
|
#region IsDragInProgress
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if there is currently a drag operation being managed.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDragInProgress
|
||||||
|
{
|
||||||
|
get { return this.isDragInProgress; }
|
||||||
|
private set { this.isDragInProgress = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IsDragInProgress
|
||||||
|
|
||||||
|
#region ListView
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets the ListView whose dragging is managed. This property
|
||||||
|
/// can be set to null, to prevent drag management from occuring. If
|
||||||
|
/// the ListView's AllowDrop property is false, it will be set to true.
|
||||||
|
/// </summary>
|
||||||
|
public ListView ListView
|
||||||
|
{
|
||||||
|
get { return listView; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsDragInProgress)
|
||||||
|
throw new InvalidOperationException("Cannot set the ListView property during a drag operation.");
|
||||||
|
|
||||||
|
if (this.listView != null)
|
||||||
|
{
|
||||||
|
#region Unhook Events
|
||||||
|
|
||||||
|
this.listView.PreviewMouseLeftButtonDown -= listView_PreviewMouseLeftButtonDown;
|
||||||
|
this.listView.PreviewMouseMove -= listView_PreviewMouseMove;
|
||||||
|
this.listView.DragOver -= listView_DragOver;
|
||||||
|
this.listView.DragLeave -= listView_DragLeave;
|
||||||
|
this.listView.DragEnter -= listView_DragEnter;
|
||||||
|
this.listView.Drop -= listView_Drop;
|
||||||
|
|
||||||
|
#endregion // Unhook Events
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listView = value;
|
||||||
|
|
||||||
|
if (this.listView != null)
|
||||||
|
{
|
||||||
|
if (!this.listView.AllowDrop)
|
||||||
|
this.listView.AllowDrop = true;
|
||||||
|
|
||||||
|
#region Hook Events
|
||||||
|
|
||||||
|
this.listView.PreviewMouseLeftButtonDown += listView_PreviewMouseLeftButtonDown;
|
||||||
|
this.listView.PreviewMouseMove += listView_PreviewMouseMove;
|
||||||
|
this.listView.DragOver += listView_DragOver;
|
||||||
|
this.listView.DragLeave += listView_DragLeave;
|
||||||
|
this.listView.DragEnter += listView_DragEnter;
|
||||||
|
this.listView.Drop += listView_Drop;
|
||||||
|
|
||||||
|
#endregion // Hook Events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ListView
|
||||||
|
|
||||||
|
#region ProcessDrop [event]
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a drop occurs. By default the dropped item will be moved
|
||||||
|
/// to the target index. Handle this event if relocating the dropped item
|
||||||
|
/// requires custom behavior. Note, if this event is handled the default
|
||||||
|
/// item dropping logic will not occur.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<ProcessDropEventArgs<ItemType>> ProcessDrop;
|
||||||
|
|
||||||
|
#endregion // ProcessDrop [event]
|
||||||
|
|
||||||
|
#region ShowDragAdorner
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/sets whether a visual representation of the ListViewItem being dragged
|
||||||
|
/// follows the mouse cursor during a drag operation. The default value is true.
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowDragAdorner
|
||||||
|
{
|
||||||
|
get { return this.showDragAdorner; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.IsDragInProgress)
|
||||||
|
throw new InvalidOperationException("Cannot set the ShowDragAdorner property during a drag operation.");
|
||||||
|
|
||||||
|
this.showDragAdorner = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ShowDragAdorner
|
||||||
|
|
||||||
|
#endregion // Public Interface
|
||||||
|
|
||||||
|
#region Event Handling Methods
|
||||||
|
|
||||||
|
#region listView_PreviewMouseLeftButtonDown
|
||||||
|
|
||||||
|
void listView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (this.IsMouseOverScrollbar)
|
||||||
|
{
|
||||||
|
// 4/13/2007 - Set the flag to false when cursor is over scrollbar.
|
||||||
|
this.canInitiateDrag = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = this.IndexUnderDragCursor;
|
||||||
|
this.canInitiateDrag = index > -1;
|
||||||
|
|
||||||
|
if (this.canInitiateDrag)
|
||||||
|
{
|
||||||
|
// Remember the location and index of the ListViewItem the user clicked on for later.
|
||||||
|
this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView);
|
||||||
|
this.indexToSelect = index;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.ptMouseDown = new Point(-10000, -10000);
|
||||||
|
this.indexToSelect = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_PreviewMouseLeftButtonDown
|
||||||
|
|
||||||
|
#region listView_PreviewMouseMove
|
||||||
|
|
||||||
|
void listView_PreviewMouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (!this.CanStartDragOperation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Select the item the user clicked on.
|
||||||
|
if (this.listView.SelectedIndex != this.indexToSelect)
|
||||||
|
this.listView.SelectedIndex = this.indexToSelect;
|
||||||
|
|
||||||
|
// If the item at the selected index is null, there's nothing
|
||||||
|
// we can do, so just return;
|
||||||
|
if (this.listView.SelectedItem == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ListViewItem itemToDrag = this.GetListViewItem(this.listView.SelectedIndex);
|
||||||
|
if (itemToDrag == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdornerLayer adornerLayer = this.ShowDragAdornerResolved ? this.InitializeAdornerLayer(itemToDrag) : null;
|
||||||
|
|
||||||
|
this.InitializeDragOperation(itemToDrag);
|
||||||
|
this.PerformDragOperation();
|
||||||
|
this.FinishDragOperation(itemToDrag, adornerLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_PreviewMouseMove
|
||||||
|
|
||||||
|
#region listView_DragOver
|
||||||
|
|
||||||
|
void listView_DragOver(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
e.Effects = DragDropEffects.Move;
|
||||||
|
|
||||||
|
if (this.ShowDragAdornerResolved)
|
||||||
|
this.UpdateDragAdornerLocation();
|
||||||
|
|
||||||
|
// Update the item which is known to be currently under the drag cursor.
|
||||||
|
int index = this.IndexUnderDragCursor;
|
||||||
|
this.ItemUnderDragCursor = index < 0 ? null : this.ListView.Items[index] as ItemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_DragOver
|
||||||
|
|
||||||
|
#region listView_DragLeave
|
||||||
|
|
||||||
|
void listView_DragLeave(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (!this.IsMouseOver(this.listView))
|
||||||
|
{
|
||||||
|
if (this.ItemUnderDragCursor != null)
|
||||||
|
this.ItemUnderDragCursor = null;
|
||||||
|
|
||||||
|
if (this.dragAdorner != null)
|
||||||
|
this.dragAdorner.Visibility = Visibility.Collapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_DragLeave
|
||||||
|
|
||||||
|
#region listView_DragEnter
|
||||||
|
|
||||||
|
void listView_DragEnter(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (this.dragAdorner != null && this.dragAdorner.Visibility != Visibility.Visible)
|
||||||
|
{
|
||||||
|
// Update the location of the adorner and then show it.
|
||||||
|
this.UpdateDragAdornerLocation();
|
||||||
|
this.dragAdorner.Visibility = Visibility.Visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_DragEnter
|
||||||
|
|
||||||
|
#region listView_Drop
|
||||||
|
|
||||||
|
void listView_Drop(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (this.ItemUnderDragCursor != null)
|
||||||
|
this.ItemUnderDragCursor = null;
|
||||||
|
|
||||||
|
e.Effects = DragDropEffects.None;
|
||||||
|
|
||||||
|
if (!e.Data.GetDataPresent(typeof(ItemType)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the data object which was dropped.
|
||||||
|
ItemType data = e.Data.GetData(typeof(ItemType)) as ItemType;
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get the ObservableCollection<ItemType> which contains the dropped data object.
|
||||||
|
ObservableCollection<ItemType> itemsSource = this.listView.ItemsSource as ObservableCollection<ItemType>;
|
||||||
|
if (itemsSource == null)
|
||||||
|
throw new Exception(
|
||||||
|
"A ListView managed by ListViewDragManager must have its ItemsSource set to an ObservableCollection<ItemType>.");
|
||||||
|
|
||||||
|
int oldIndex = itemsSource.IndexOf(data);
|
||||||
|
int newIndex = this.IndexUnderDragCursor;
|
||||||
|
|
||||||
|
if (newIndex < 0)
|
||||||
|
{
|
||||||
|
// The drag started somewhere else, and our ListView is empty
|
||||||
|
// so make the new item the first in the list.
|
||||||
|
if (itemsSource.Count == 0)
|
||||||
|
newIndex = 0;
|
||||||
|
|
||||||
|
// The drag started somewhere else, but our ListView has items
|
||||||
|
// so make the new item the last in the list.
|
||||||
|
else if (oldIndex < 0)
|
||||||
|
newIndex = itemsSource.Count;
|
||||||
|
|
||||||
|
// The user is trying to drop an item from our ListView into
|
||||||
|
// our ListView, but the mouse is not over an item, so don't
|
||||||
|
// let them drop it.
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dropping an item back onto itself is not considered an actual 'drop'.
|
||||||
|
if (oldIndex == newIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this.ProcessDrop != null)
|
||||||
|
{
|
||||||
|
// Let the client code process the drop.
|
||||||
|
ProcessDropEventArgs<ItemType> args = new ProcessDropEventArgs<ItemType>(itemsSource, data, oldIndex, newIndex, e.AllowedEffects);
|
||||||
|
this.ProcessDrop(this, args);
|
||||||
|
e.Effects = args.Effects;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Move the dragged data object from it's original index to the
|
||||||
|
// new index (according to where the mouse cursor is). If it was
|
||||||
|
// not previously in the ListBox, then insert the item.
|
||||||
|
if (oldIndex > -1)
|
||||||
|
itemsSource.Move(oldIndex, newIndex);
|
||||||
|
else
|
||||||
|
itemsSource.Insert(newIndex, data);
|
||||||
|
|
||||||
|
// Set the Effects property so that the call to DoDragDrop will return 'Move'.
|
||||||
|
e.Effects = DragDropEffects.Move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // listView_Drop
|
||||||
|
|
||||||
|
#endregion // Event Handling Methods
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
#region CanStartDragOperation
|
||||||
|
|
||||||
|
bool CanStartDragOperation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Mouse.LeftButton != MouseButtonState.Pressed)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!this.canInitiateDrag)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this.indexToSelect == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!this.HasCursorLeftDragThreshold)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // CanStartDragOperation
|
||||||
|
|
||||||
|
#region FinishDragOperation
|
||||||
|
|
||||||
|
void FinishDragOperation(ListViewItem draggedItem, AdornerLayer adornerLayer)
|
||||||
|
{
|
||||||
|
// Let the ListViewItem know that it is not being dragged anymore.
|
||||||
|
ListViewItemDragState.SetIsBeingDragged(draggedItem, false);
|
||||||
|
|
||||||
|
this.IsDragInProgress = false;
|
||||||
|
|
||||||
|
if (this.ItemUnderDragCursor != null)
|
||||||
|
this.ItemUnderDragCursor = null;
|
||||||
|
|
||||||
|
// Remove the drag adorner from the adorner layer.
|
||||||
|
if (adornerLayer != null)
|
||||||
|
{
|
||||||
|
adornerLayer.Remove(this.dragAdorner);
|
||||||
|
this.dragAdorner = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // FinishDragOperation
|
||||||
|
|
||||||
|
#region GetListViewItem
|
||||||
|
|
||||||
|
ListViewItem GetListViewItem(int index)
|
||||||
|
{
|
||||||
|
if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return this.listView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListViewItem GetListViewItem(ItemType dataItem)
|
||||||
|
{
|
||||||
|
if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return this.listView.ItemContainerGenerator.ContainerFromItem(dataItem) as ListViewItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // GetListViewItem
|
||||||
|
|
||||||
|
#region HasCursorLeftDragThreshold
|
||||||
|
|
||||||
|
bool HasCursorLeftDragThreshold
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.indexToSelect < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ListViewItem item = this.GetListViewItem(this.indexToSelect);
|
||||||
|
Rect bounds = VisualTreeHelper.GetDescendantBounds(item);
|
||||||
|
Point ptInItem = this.listView.TranslatePoint(this.ptMouseDown, item);
|
||||||
|
|
||||||
|
// In case the cursor is at the very top or bottom of the ListViewItem
|
||||||
|
// we want to make the vertical threshold very small so that dragging
|
||||||
|
// over an adjacent item does not select it.
|
||||||
|
double topOffset = Math.Abs(ptInItem.Y);
|
||||||
|
double btmOffset = Math.Abs(bounds.Height - ptInItem.Y);
|
||||||
|
double vertOffset = Math.Min(topOffset, btmOffset);
|
||||||
|
|
||||||
|
double width = SystemParameters.MinimumHorizontalDragDistance * 2;
|
||||||
|
double height = Math.Min(SystemParameters.MinimumVerticalDragDistance, vertOffset) * 2;
|
||||||
|
Size szThreshold = new Size(width, height);
|
||||||
|
|
||||||
|
Rect rect = new Rect(this.ptMouseDown, szThreshold);
|
||||||
|
rect.Offset(szThreshold.Width / -2, szThreshold.Height / -2);
|
||||||
|
Point ptInListView = MouseUtilities.GetMousePosition(this.listView);
|
||||||
|
return !rect.Contains(ptInListView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // HasCursorLeftDragThreshold
|
||||||
|
|
||||||
|
#region IndexUnderDragCursor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the index of the ListViewItem underneath the
|
||||||
|
/// drag cursor, or -1 if the cursor is not over an item.
|
||||||
|
/// </summary>
|
||||||
|
int IndexUnderDragCursor
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < this.listView.Items.Count; ++i)
|
||||||
|
{
|
||||||
|
ListViewItem item = this.GetListViewItem(i);
|
||||||
|
if (this.IsMouseOver(item))
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IndexUnderDragCursor
|
||||||
|
|
||||||
|
#region InitializeAdornerLayer
|
||||||
|
|
||||||
|
AdornerLayer InitializeAdornerLayer(ListViewItem itemToDrag)
|
||||||
|
{
|
||||||
|
// Create a brush which will paint the ListViewItem onto
|
||||||
|
// a visual in the adorner layer.
|
||||||
|
VisualBrush brush = new VisualBrush(itemToDrag);
|
||||||
|
|
||||||
|
// Create an element which displays the source item while it is dragged.
|
||||||
|
this.dragAdorner = new DragAdorner(this.listView, itemToDrag.RenderSize, brush);
|
||||||
|
|
||||||
|
// Set the drag adorner's opacity.
|
||||||
|
this.dragAdorner.Opacity = this.DragAdornerOpacity;
|
||||||
|
|
||||||
|
AdornerLayer layer = AdornerLayer.GetAdornerLayer(this.listView);
|
||||||
|
layer.Add(dragAdorner);
|
||||||
|
|
||||||
|
// Save the location of the cursor when the left mouse button was pressed.
|
||||||
|
this.ptMouseDown = MouseUtilities.GetMousePosition(this.listView);
|
||||||
|
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // InitializeAdornerLayer
|
||||||
|
|
||||||
|
#region InitializeDragOperation
|
||||||
|
|
||||||
|
void InitializeDragOperation(ListViewItem itemToDrag)
|
||||||
|
{
|
||||||
|
// Set some flags used during the drag operation.
|
||||||
|
this.IsDragInProgress = true;
|
||||||
|
this.canInitiateDrag = false;
|
||||||
|
|
||||||
|
// Let the ListViewItem know that it is being dragged.
|
||||||
|
ListViewItemDragState.SetIsBeingDragged(itemToDrag, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // InitializeDragOperation
|
||||||
|
|
||||||
|
#region IsMouseOver
|
||||||
|
|
||||||
|
bool IsMouseOver(Visual target)
|
||||||
|
{
|
||||||
|
// We need to use MouseUtilities to figure out the cursor
|
||||||
|
// coordinates because, during a drag-drop operation, the WPF
|
||||||
|
// mechanisms for getting the coordinates behave strangely.
|
||||||
|
|
||||||
|
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
|
||||||
|
Point mousePos = MouseUtilities.GetMousePosition(target);
|
||||||
|
return bounds.Contains(mousePos);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IsMouseOver
|
||||||
|
|
||||||
|
#region IsMouseOverScrollbar
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the mouse cursor is over a scrollbar in the ListView.
|
||||||
|
/// </summary>
|
||||||
|
bool IsMouseOverScrollbar
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Point ptMouse = MouseUtilities.GetMousePosition(this.listView);
|
||||||
|
HitTestResult res = VisualTreeHelper.HitTest(this.listView, ptMouse);
|
||||||
|
if (res == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DependencyObject depObj = res.VisualHit;
|
||||||
|
while (depObj != null)
|
||||||
|
{
|
||||||
|
if (depObj is ScrollBar)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// VisualTreeHelper works with objects of type Visual or Visual3D.
|
||||||
|
// If the current object is not derived from Visual or Visual3D,
|
||||||
|
// then use the LogicalTreeHelper to find the parent element.
|
||||||
|
if (depObj is Visual || depObj is System.Windows.Media.Media3D.Visual3D)
|
||||||
|
depObj = VisualTreeHelper.GetParent(depObj);
|
||||||
|
else
|
||||||
|
depObj = LogicalTreeHelper.GetParent(depObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IsMouseOverScrollbar
|
||||||
|
|
||||||
|
#region ItemUnderDragCursor
|
||||||
|
|
||||||
|
ItemType ItemUnderDragCursor
|
||||||
|
{
|
||||||
|
get { return this.itemUnderDragCursor; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (this.itemUnderDragCursor == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The first pass handles the previous item under the cursor.
|
||||||
|
// The second pass handles the new one.
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
if (i == 1)
|
||||||
|
this.itemUnderDragCursor = value;
|
||||||
|
|
||||||
|
if (this.itemUnderDragCursor != null)
|
||||||
|
{
|
||||||
|
ListViewItem listViewItem = this.GetListViewItem(this.itemUnderDragCursor);
|
||||||
|
if (listViewItem != null)
|
||||||
|
ListViewItemDragState.SetIsUnderDragCursor(listViewItem, i == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ItemUnderDragCursor
|
||||||
|
|
||||||
|
#region PerformDragOperation
|
||||||
|
|
||||||
|
void PerformDragOperation()
|
||||||
|
{
|
||||||
|
ItemType selectedItem = this.listView.SelectedItem as ItemType;
|
||||||
|
DragDropEffects allowedEffects = DragDropEffects.Move | DragDropEffects.Move | DragDropEffects.Link;
|
||||||
|
if (DragDrop.DoDragDrop(this.listView, selectedItem, allowedEffects) != DragDropEffects.None)
|
||||||
|
{
|
||||||
|
// The item was dropped into a new location,
|
||||||
|
// so make it the new selected item.
|
||||||
|
this.listView.SelectedItem = selectedItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // PerformDragOperation
|
||||||
|
|
||||||
|
#region ShowDragAdornerResolved
|
||||||
|
|
||||||
|
bool ShowDragAdornerResolved
|
||||||
|
{
|
||||||
|
get { return this.ShowDragAdorner && this.DragAdornerOpacity > 0.0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ShowDragAdornerResolved
|
||||||
|
|
||||||
|
#region UpdateDragAdornerLocation
|
||||||
|
|
||||||
|
void UpdateDragAdornerLocation()
|
||||||
|
{
|
||||||
|
if (this.dragAdorner != null)
|
||||||
|
{
|
||||||
|
Point ptCursor = MouseUtilities.GetMousePosition(this.ListView);
|
||||||
|
|
||||||
|
double left = ptCursor.X - this.ptMouseDown.X;
|
||||||
|
|
||||||
|
// 4/13/2007 - Made the top offset relative to the item being dragged.
|
||||||
|
ListViewItem itemBeingDragged = this.GetListViewItem(this.indexToSelect);
|
||||||
|
Point itemLoc = itemBeingDragged.TranslatePoint(new Point(0, 0), this.ListView);
|
||||||
|
double top = itemLoc.Y + ptCursor.Y - this.ptMouseDown.Y;
|
||||||
|
|
||||||
|
this.dragAdorner.SetOffsets(left, top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // UpdateDragAdornerLocation
|
||||||
|
|
||||||
|
#endregion // Private Helpers
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ListViewDragDropManager
|
||||||
|
|
||||||
|
#region ListViewItemDragState
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exposes attached properties used in conjunction with the ListViewDragDropManager class.
|
||||||
|
/// Those properties can be used to allow triggers to modify the appearance of ListViewItems
|
||||||
|
/// in a ListView during a drag-drop operation.
|
||||||
|
/// </summary>
|
||||||
|
public static class ListViewItemDragState
|
||||||
|
{
|
||||||
|
#region IsBeingDragged
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the ListViewItemDragState's IsBeingDragged attached property.
|
||||||
|
/// This field is read-only.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty IsBeingDraggedProperty =
|
||||||
|
DependencyProperty.RegisterAttached(
|
||||||
|
"IsBeingDragged",
|
||||||
|
typeof(bool),
|
||||||
|
typeof(ListViewItemDragState),
|
||||||
|
new UIPropertyMetadata(false));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the specified ListViewItem is being dragged, else false.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The ListViewItem to check.</param>
|
||||||
|
public static bool GetIsBeingDragged(ListViewItem item)
|
||||||
|
{
|
||||||
|
return (bool)item.GetValue(IsBeingDraggedProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the IsBeingDragged attached property for the specified ListViewItem.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The ListViewItem to set the property on.</param>
|
||||||
|
/// <param name="value">Pass true if the element is being dragged, else false.</param>
|
||||||
|
internal static void SetIsBeingDragged(ListViewItem item, bool value)
|
||||||
|
{
|
||||||
|
item.SetValue(IsBeingDraggedProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IsBeingDragged
|
||||||
|
|
||||||
|
#region IsUnderDragCursor
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the ListViewItemDragState's IsUnderDragCursor attached property.
|
||||||
|
/// This field is read-only.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DependencyProperty IsUnderDragCursorProperty =
|
||||||
|
DependencyProperty.RegisterAttached(
|
||||||
|
"IsUnderDragCursor",
|
||||||
|
typeof(bool),
|
||||||
|
typeof(ListViewItemDragState),
|
||||||
|
new UIPropertyMetadata(false));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the specified ListViewItem is currently underneath the cursor
|
||||||
|
/// during a drag-drop operation, else false.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The ListViewItem to check.</param>
|
||||||
|
public static bool GetIsUnderDragCursor(ListViewItem item)
|
||||||
|
{
|
||||||
|
return (bool)item.GetValue(IsUnderDragCursorProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the IsUnderDragCursor attached property for the specified ListViewItem.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The ListViewItem to set the property on.</param>
|
||||||
|
/// <param name="value">Pass true if the element is underneath the drag cursor, else false.</param>
|
||||||
|
internal static void SetIsUnderDragCursor(ListViewItem item, bool value)
|
||||||
|
{
|
||||||
|
item.SetValue(IsUnderDragCursorProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IsUnderDragCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ListViewItemDragState
|
||||||
|
|
||||||
|
#region ProcessDropEventArgs
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event arguments used by the ListViewDragDropManager.ProcessDrop event.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="ItemType">The type of data object being dropped.</typeparam>
|
||||||
|
public class ProcessDropEventArgs<ItemType> : EventArgs where ItemType : class
|
||||||
|
{
|
||||||
|
#region Data
|
||||||
|
|
||||||
|
ObservableCollection<ItemType> itemsSource;
|
||||||
|
ItemType dataItem;
|
||||||
|
int oldIndex;
|
||||||
|
int newIndex;
|
||||||
|
DragDropEffects allowedEffects = DragDropEffects.None;
|
||||||
|
DragDropEffects effects = DragDropEffects.None;
|
||||||
|
|
||||||
|
#endregion // Data
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
internal ProcessDropEventArgs(
|
||||||
|
ObservableCollection<ItemType> itemsSource,
|
||||||
|
ItemType dataItem,
|
||||||
|
int oldIndex,
|
||||||
|
int newIndex,
|
||||||
|
DragDropEffects allowedEffects)
|
||||||
|
{
|
||||||
|
this.itemsSource = itemsSource;
|
||||||
|
this.dataItem = dataItem;
|
||||||
|
this.oldIndex = oldIndex;
|
||||||
|
this.newIndex = newIndex;
|
||||||
|
this.allowedEffects = allowedEffects;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Constructor
|
||||||
|
|
||||||
|
#region Public Properties
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The items source of the ListView where the drop occurred.
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<ItemType> ItemsSource
|
||||||
|
{
|
||||||
|
get { return this.itemsSource; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data object which was dropped.
|
||||||
|
/// </summary>
|
||||||
|
public ItemType DataItem
|
||||||
|
{
|
||||||
|
get { return this.dataItem; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current index of the data item being dropped, in the ItemsSource collection.
|
||||||
|
/// </summary>
|
||||||
|
public int OldIndex
|
||||||
|
{
|
||||||
|
get { return this.oldIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target index of the data item being dropped, in the ItemsSource collection.
|
||||||
|
/// </summary>
|
||||||
|
public int NewIndex
|
||||||
|
{
|
||||||
|
get { return this.newIndex; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The drag drop effects allowed to be performed.
|
||||||
|
/// </summary>
|
||||||
|
public DragDropEffects AllowedEffects
|
||||||
|
{
|
||||||
|
get { return allowedEffects; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The drag drop effect(s) performed on the dropped item.
|
||||||
|
/// </summary>
|
||||||
|
public DragDropEffects Effects
|
||||||
|
{
|
||||||
|
get { return effects; }
|
||||||
|
set { effects = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Public Properties
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // ProcessDropEventArgs
|
||||||
|
}
|
58
ProgramQueuer/Helpers/MouseUtilities.cs
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
|
namespace WPF.JoshSmith.Controls.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides access to the mouse location by calling unmanaged code.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class was written by Dan Crevier (Microsoft).
|
||||||
|
/// http://blogs.msdn.com/llobo/archive/2006/09/06/Scrolling-Scrollviewer-on-Mouse-Drag-at-the-boundaries.aspx
|
||||||
|
/// </remarks>
|
||||||
|
public class MouseUtilities
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct Win32Point
|
||||||
|
{
|
||||||
|
public Int32 X;
|
||||||
|
public Int32 Y;
|
||||||
|
};
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool GetCursorPos(ref Win32Point pt);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool ScreenToClient(IntPtr hwnd, ref Win32Point pt);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the mouse cursor location. This method is necessary during
|
||||||
|
/// a drag-drop operation because the WPF mechanisms for retrieving the
|
||||||
|
/// cursor coordinates are unreliable.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="relativeTo">The Visual to which the mouse coordinates will be relative.</param>
|
||||||
|
public static Point GetMousePosition(Visual relativeTo)
|
||||||
|
{
|
||||||
|
Win32Point mouse = new Win32Point();
|
||||||
|
GetCursorPos(ref mouse);
|
||||||
|
|
||||||
|
// Using PointFromScreen instead of Dan Crevier's code (commented out below)
|
||||||
|
// is a bug fix created by William J. Roberts. Read his comments about the fix
|
||||||
|
// here: http://www.codeproject.com/useritems/ListViewDragDropManager.asp?msg=1911611#xx1911611xx
|
||||||
|
return relativeTo.PointFromScreen(new Point((double)mouse.X, (double)mouse.Y));
|
||||||
|
|
||||||
|
#region Commented Out
|
||||||
|
//System.Windows.Interop.HwndSource presentationSource =
|
||||||
|
// (System.Windows.Interop.HwndSource)PresentationSource.FromVisual( relativeTo );
|
||||||
|
//ScreenToClient( presentationSource.Handle, ref mouse );
|
||||||
|
//GeneralTransform transform = relativeTo.TransformToAncestor( presentationSource.RootVisual );
|
||||||
|
//Point offset = transform.Transform( new Point( 0, 0 ) );
|
||||||
|
//return new Point( mouse.X - offset.X, mouse.Y - offset.Y );
|
||||||
|
#endregion // Commented Out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
177
ProgramQueuer/Helpers/ValueConverterGroup.cs
Executable file
|
@ -0,0 +1,177 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A value converter which contains a list of IValueConverters and invokes their Convert or ConvertBack methods
|
||||||
|
/// in the order that they exist in the list. The output of one converter is piped into the next converter
|
||||||
|
/// allowing for modular value converters to be chained together. If the ConvertBack method is invoked, the
|
||||||
|
/// value converters are executed in reverse order (highest to lowest index). Do not leave an element in the
|
||||||
|
/// Converters property collection null, every element must reference a valid IValueConverter instance. If a
|
||||||
|
/// value converter's type is not decorated with the ValueConversionAttribute, an InvalidOperationException will be
|
||||||
|
/// thrown when the converter is added to the Converters collection.
|
||||||
|
/// </summary>
|
||||||
|
[System.Windows.Markup.ContentProperty("Converters")]
|
||||||
|
public class ValueConverterGroup : IValueConverter
|
||||||
|
{
|
||||||
|
#region Data
|
||||||
|
|
||||||
|
private readonly ObservableCollection<IValueConverter> converters = new ObservableCollection<IValueConverter>();
|
||||||
|
private readonly Dictionary<IValueConverter, ValueConversionAttribute> cachedAttributes = new Dictionary<IValueConverter, ValueConversionAttribute>();
|
||||||
|
|
||||||
|
#endregion // Data
|
||||||
|
|
||||||
|
#region Constructor
|
||||||
|
|
||||||
|
public ValueConverterGroup()
|
||||||
|
{
|
||||||
|
this.converters.CollectionChanged += this.OnConvertersCollectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Constructor
|
||||||
|
|
||||||
|
#region Converters
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the list of IValueConverters contained in this converter.
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<IValueConverter> Converters
|
||||||
|
{
|
||||||
|
get { return this.converters; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // Converters
|
||||||
|
|
||||||
|
#region IValueConverter Members
|
||||||
|
|
||||||
|
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
object output = value;
|
||||||
|
|
||||||
|
for (int i = 0; i < this.Converters.Count; i++)
|
||||||
|
{
|
||||||
|
//Run the converter.
|
||||||
|
output = this.Converters[i].Convert(output, this.GetTargetType(i, targetType, true), parameter, culture);
|
||||||
|
if (output == Binding.DoNothing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
object output = value;
|
||||||
|
|
||||||
|
for (int i = this.Converters.Count - 1; i > -1; i--)
|
||||||
|
{
|
||||||
|
//Run the converter.
|
||||||
|
output = this.Converters[i].Convert(output, this.GetTargetType(i, targetType, true), parameter, culture);
|
||||||
|
if (output == Binding.DoNothing)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // IValueConverter Members
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
#region GetTargetType
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the target type for a conversion operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="converterIndex">The index of the current converter about to be executed.</param>
|
||||||
|
/// <param name="finalTargetType">The 'targetType' argument passed into the conversion method.</param>
|
||||||
|
/// <param name="convert">Pass true if calling from the Convert method, or false if calling from ConvertBack.</param>
|
||||||
|
protected virtual Type GetTargetType(int converterIndex, Type finalTargetType, bool convert)
|
||||||
|
{
|
||||||
|
// If the current converter is not the last/first in the list,
|
||||||
|
// get a reference to the next/previous converter.
|
||||||
|
IValueConverter nextConverter = null;
|
||||||
|
int nextIndex = converterIndex;
|
||||||
|
|
||||||
|
//Grab the index for the next converter.
|
||||||
|
if (convert && nextIndex < this.Converters.Count - 1)
|
||||||
|
nextIndex++;
|
||||||
|
else if (!convert && nextIndex > 0)
|
||||||
|
nextIndex--;
|
||||||
|
|
||||||
|
//Check to see if we have a new index.
|
||||||
|
if (nextIndex != converterIndex)
|
||||||
|
//Make sure the converter at our new index is not null.
|
||||||
|
if (this.Converters[nextIndex] == null)
|
||||||
|
throw new InvalidOperationException("The Converters collection of the ValueConverterGroup contains a null reference at index: " + nextIndex);
|
||||||
|
else
|
||||||
|
//Get our next converter.
|
||||||
|
nextConverter = this.Converters[nextIndex];
|
||||||
|
|
||||||
|
//Check if we have to go through another converter. If this (current) is the last converter then this will be null.
|
||||||
|
if (nextConverter != null)
|
||||||
|
{
|
||||||
|
var conversionAttribute = cachedAttributes[nextConverter];
|
||||||
|
|
||||||
|
// If the Convert method is going to be called, we need to use the SourceType of the next
|
||||||
|
// converter in the list. If ConvertBack is called, use the TargetType.
|
||||||
|
return convert ? conversionAttribute.SourceType : conversionAttribute.TargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current converter is the last one to be executed return the target type passed into the conversion method.
|
||||||
|
return finalTargetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // GetTargetType
|
||||||
|
|
||||||
|
#region OnConvertersCollectionChanged
|
||||||
|
|
||||||
|
void OnConvertersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
// The 'Converters' collection has been modified, so validate that each value converter it now
|
||||||
|
// contains is decorated with ValueConversionAttribute and then cache the attribute value.
|
||||||
|
|
||||||
|
IList convertersToProcess = null;
|
||||||
|
if (e.Action == NotifyCollectionChangedAction.Add ||
|
||||||
|
e.Action == NotifyCollectionChangedAction.Replace)
|
||||||
|
{
|
||||||
|
convertersToProcess = e.NewItems;
|
||||||
|
}
|
||||||
|
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||||
|
{
|
||||||
|
foreach (IValueConverter converter in e.OldItems)
|
||||||
|
this.cachedAttributes.Remove(converter);
|
||||||
|
}
|
||||||
|
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||||
|
{
|
||||||
|
this.cachedAttributes.Clear();
|
||||||
|
convertersToProcess = this.converters;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertersToProcess != null && convertersToProcess.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (IValueConverter converter in convertersToProcess)
|
||||||
|
{
|
||||||
|
object[] attributes = converter.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false);
|
||||||
|
|
||||||
|
if (attributes.Length != 1)
|
||||||
|
throw new InvalidOperationException("All value converters added to a ValueConverterGroup must be decorated with the ValueConversionAttribute attribute exactly once.");
|
||||||
|
|
||||||
|
this.cachedAttributes.Add(converter, attributes[0] as ValueConversionAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // OnConvertersCollectionChanged
|
||||||
|
|
||||||
|
#endregion // Private Helpers
|
||||||
|
}
|
||||||
|
}
|
45
ProgramQueuer/Helpers/ValueConverters.cs
Executable file
|
@ -0,0 +1,45 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Helpers
|
||||||
|
{
|
||||||
|
[ValueConversion(typeof(bool), typeof(Visibility))]
|
||||||
|
public class BoolToVisibililty : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
bool visible = (bool)value;
|
||||||
|
if (visible)
|
||||||
|
return Visibility.Visible;
|
||||||
|
return Visibility.Hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
Visibility visible = (Visibility)value;
|
||||||
|
if (visible == Visibility.Hidden)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ValueConversion(typeof(bool), typeof(bool))]
|
||||||
|
public class InvertBool : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
return !(bool)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
ProgramQueuer/MainWindow.xaml
Executable file
|
@ -0,0 +1,95 @@
|
||||||
|
<Window x:Class="ProgramQueuer.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:ProgramQueuer.Helpers"
|
||||||
|
xmlns:properties="clr-namespace:ProgramQueuer.Properties"
|
||||||
|
Title="MainWindow"
|
||||||
|
Closing="Window_Closing"
|
||||||
|
AllowDrop="True"
|
||||||
|
DragEnter="ListView_DragEnter" Drop="ListView_Drop"
|
||||||
|
Width="{Binding Path=width, Source={x:Static properties:Settings.Default}, Mode=TwoWay}"
|
||||||
|
Height="{Binding Path=height, Source={x:Static properties:Settings.Default}, Mode=TwoWay}" Loaded="Window_Loaded" Icon="/ProgramQueuer;component/reports.ico">
|
||||||
|
<Window.Resources>
|
||||||
|
<converters:BoolToVisibility x:Key="boolToVisible" />
|
||||||
|
<converters:ValueConverterGroup x:Key="boolInvertedToVisible">
|
||||||
|
<converters:BooleanInverter />
|
||||||
|
<converters:BoolToVisibility />
|
||||||
|
</converters:ValueConverterGroup>
|
||||||
|
</Window.Resources>
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="{Binding Path=split_height, Source={x:Static properties:Settings.Default}, Mode=OneWay}" />
|
||||||
|
<RowDefinition Height="9" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<ListView x:Name="listPrograms" Margin="6 6 6 0" AllowDrop="True" ItemsSource="{Binding QueueList}" SelectionMode="Single">
|
||||||
|
<ListView.View>
|
||||||
|
<GridView>
|
||||||
|
<GridViewColumn Header="" Width="28">
|
||||||
|
<GridViewColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/play.png" Visibility="{Binding Path=Working, Converter={StaticResource boolToVisible}}" />
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/application.png" Visibility="{Binding Path=Finished, Converter={StaticResource boolInvertedToVisible}}" />
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/accept.png" Visibility="{Binding Path=Finished, Converter={StaticResource boolToVisible}}" />
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</GridViewColumn.CellTemplate>
|
||||||
|
</GridViewColumn>
|
||||||
|
<GridViewColumn Header="File/Program" Width="{Binding Path=columnWidth1, Source={x:Static properties:Settings.Default}, Mode=TwoWay}">
|
||||||
|
<GridViewColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding Name}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</GridViewColumn.CellTemplate>
|
||||||
|
</GridViewColumn>
|
||||||
|
<GridViewColumn Header="Options" Width="{Binding Path=columnWidth2, Source={x:Static properties:Settings.Default}, Mode=TwoWay}">
|
||||||
|
<GridViewColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Margin="0 0 6 0" BorderBrush="Transparent" Background="Transparent" Visibility="{Binding Path=Working, Converter={StaticResource boolInvertedToVisible}}" Click="buttonRemove_Click">
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/remove.png" />
|
||||||
|
</Button>
|
||||||
|
<Button Margin="0 0 6 0" BorderBrush="Transparent" Background="Transparent" Visibility="{Binding Path=Working, Converter={StaticResource boolToVisible}}" Click="buttonStopCurrent_Click">
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/stop.png" />
|
||||||
|
</Button>
|
||||||
|
<!--<Button Margin="0 0 6 0" BorderBrush="Transparent" Background="Transparent">
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/window.png" />
|
||||||
|
</Button>-->
|
||||||
|
</StackPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</GridViewColumn.CellTemplate>
|
||||||
|
</GridViewColumn>
|
||||||
|
<GridViewColumn Header="Status" Width="{Binding Path=columnWidth3, Source={x:Static properties:Settings.Default}, Mode=TwoWay}">
|
||||||
|
<GridViewColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<TextBlock Text="{Binding Status}" />
|
||||||
|
</DataTemplate>
|
||||||
|
</GridViewColumn.CellTemplate>
|
||||||
|
</GridViewColumn>
|
||||||
|
</GridView>
|
||||||
|
</ListView.View>
|
||||||
|
</ListView>
|
||||||
|
<StackPanel Grid.Column="1">
|
||||||
|
<Button Margin="0 6 6 6" BorderBrush="Transparent" Background="Transparent" Visibility="{Binding Path=Working, Converter={StaticResource boolInvertedToVisible}}" Click="buttonAdd_Click">
|
||||||
|
<Image Height="16" Width="16" Source="/ProgramQueuer;component/Resources/add.png" />
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<GridSplitter ResizeDirection="Rows" Grid.Row="1" BorderThickness="1" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DragCompleted="GridSplitter_DragCompleted" />
|
||||||
|
<GroupBox Grid.Row="2" Margin="6 0 6 6" Header="Output" Padding="0 3 0 0">
|
||||||
|
<TextBox x:Name="textboxStatus" AcceptsReturn="True" IsReadOnly="True" Background="Black" Foreground="White" Text="{Binding Path=SelectedItem.Output, ElementName=listPrograms}" TextChanged="TextBox_TextChanged" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" FontFamily="Fixedsys Excelsior 2.00">
|
||||||
|
|
||||||
|
</TextBox>
|
||||||
|
</GroupBox>
|
||||||
|
<Button x:Name="buttonExit" Grid.Row="3" HorizontalAlignment="Left" Margin="6 0 6 6" Width="120" Click="ButtonExit_Click">Close</Button>
|
||||||
|
<Button x:Name="buttonStop" Grid.Row="3" HorizontalAlignment="Right" Margin="6 0 6 6" Width="120" Click="ButtonStop_Click" Visibility="{Binding Path=Working, Converter={StaticResource boolToVisible}}">Stop</Button>
|
||||||
|
<Button x:Name="buttonWork" Grid.Row="3" HorizontalAlignment="Right" Margin="6 0 6 6" Width="120" Click="ButtonWork_Click" Visibility="{Binding Path=Working, Converter={StaticResource boolInvertedToVisible}}">Start</Button>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
118
ProgramQueuer/MainWindow.xaml.cs
Executable file
|
@ -0,0 +1,118 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using ProgramQueuer.Queuer;
|
||||||
|
using WPF.JoshSmith.ServiceProviders.UI;
|
||||||
|
|
||||||
|
namespace ProgramQueuer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
EntryManager _manager;
|
||||||
|
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
_manager = new EntryManager();
|
||||||
|
this.DataContext = _manager;
|
||||||
|
this.listPrograms.ItemsSource = _manager.QueueList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
new ListViewDragDropManager<ProgramEntry>(this.listPrograms);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GridSplitter_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
|
||||||
|
{
|
||||||
|
ProgramQueuer.Properties.Settings.Default.split_height = (int)((sender as GridSplitter).Parent as Grid).RowDefinitions[2].Height.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
|
{
|
||||||
|
if (_manager.Working)
|
||||||
|
if (MessageBox.Show("Are you sure you want to stop current worker and batch and exit application?", "You sure you want to exit?", MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||||
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ProgramQueuer.Properties.Settings.Default.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonExit_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
this.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonWork_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_manager.RunQueuer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ButtonStop_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show("Are you sure you want to stop current batch?", "Stop worker", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
_manager.ForceStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ListView_DragEnter(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||||
|
e.Effects = DragDropEffects.Link;
|
||||||
|
else
|
||||||
|
e.Effects = DragDropEffects.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ListView_Drop(object sender, DragEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||||
|
{
|
||||||
|
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false);
|
||||||
|
foreach (string file in files)
|
||||||
|
{
|
||||||
|
_manager.QueueList.Add(new ProgramEntry { Name = file , Status = "Queued"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buttonRemove_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_manager.QueueList.Remove((sender as Control).DataContext as ProgramEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buttonStopCurrent_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (MessageBox.Show("Are you sure you want to stop current running process and continue to next?", "Stop current worker?", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
_manager.ForceStopCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||||
|
{
|
||||||
|
textboxStatus.CaretIndex = textboxStatus.Text.Length;
|
||||||
|
textboxStatus.ScrollToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buttonAdd_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
ProgramQueuer/ProgramQueuer.csproj
Executable file
|
@ -0,0 +1,142 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||||
|
<ProductVersion>8.0.30703</ProductVersion>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>{B1825A4D-32CB-45E2-9CFE-77622DCC1641}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>ProgramQueuer</RootNamespace>
|
||||||
|
<AssemblyName>ProgramQueuer</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||||
|
<PlatformTarget>x86</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ApplicationIcon>reports.ico</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="App.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="Helpers\BooleanInverter.cs" />
|
||||||
|
<Compile Include="Helpers\BoolToVisibility.cs" />
|
||||||
|
<Compile Include="Helpers\DragAdorner.cs" />
|
||||||
|
<Compile Include="Helpers\ListViewDragDropManager.cs" />
|
||||||
|
<Compile Include="Helpers\MouseUtilities.cs" />
|
||||||
|
<Compile Include="Helpers\ValueConverterGroup.cs" />
|
||||||
|
<Compile Include="Helpers\ValueConverters.cs" />
|
||||||
|
<Compile Include="Queuer\EntryManager.cs" />
|
||||||
|
<Compile Include="Queuer\ProcessIOManager.cs" />
|
||||||
|
<Compile Include="Queuer\ProgramEntry.cs" />
|
||||||
|
<Compile Include="Settings.xaml.cs">
|
||||||
|
<DependentUpon>Settings.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Page Include="MainWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="App.xaml.cs">
|
||||||
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainWindow.xaml.cs">
|
||||||
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Page Include="Settings.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="app.config" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
<AppDesigner Include="Properties\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\accept.png" />
|
||||||
|
<Resource Include="Resources\remove.png" />
|
||||||
|
<Resource Include="Resources\window.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\application.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\stop.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\warning.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\play.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="reports.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Resource Include="Resources\add.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
55
ProgramQueuer/Properties/AssemblyInfo.cs
Executable file
|
@ -0,0 +1,55 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Resources;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("ProgramQueuer")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Microsoft")]
|
||||||
|
[assembly: AssemblyProduct("ProgramQueuer")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
//In order to begin building localizable applications, set
|
||||||
|
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||||
|
//inside a <PropertyGroup>. For example, if you are using US english
|
||||||
|
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||||
|
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||||
|
//the line below to match the UICulture setting in the project file.
|
||||||
|
|
||||||
|
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||||
|
|
||||||
|
|
||||||
|
[assembly: ThemeInfo(
|
||||||
|
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// or application resource dictionaries)
|
||||||
|
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||||
|
//(used if a resource is not found in the page,
|
||||||
|
// app, or any theme specific resource dictionaries)
|
||||||
|
)]
|
||||||
|
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
71
ProgramQueuer/Properties/Resources.Designer.cs
generated
Executable file
|
@ -0,0 +1,71 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.1
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProgramQueuer.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
117
ProgramQueuer/Properties/Resources.resx
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
98
ProgramQueuer/Properties/Settings.Designer.cs
generated
Executable file
|
@ -0,0 +1,98 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.1
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Properties {
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default {
|
||||||
|
get {
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("500")]
|
||||||
|
public double width {
|
||||||
|
get {
|
||||||
|
return ((double)(this["width"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["width"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("500")]
|
||||||
|
public double height {
|
||||||
|
get {
|
||||||
|
return ((double)(this["height"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["height"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("200")]
|
||||||
|
public double split_height {
|
||||||
|
get {
|
||||||
|
return ((double)(this["split_height"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["split_height"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("300")]
|
||||||
|
public double columnWidth1 {
|
||||||
|
get {
|
||||||
|
return ((double)(this["columnWidth1"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["columnWidth1"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("50")]
|
||||||
|
public double columnWidth2 {
|
||||||
|
get {
|
||||||
|
return ((double)(this["columnWidth2"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["columnWidth2"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Configuration.DefaultSettingValueAttribute("200")]
|
||||||
|
public double columnWidth3 {
|
||||||
|
get {
|
||||||
|
return ((double)(this["columnWidth3"]));
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
this["columnWidth3"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
ProgramQueuer/Properties/Settings.settings
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ProgramQueuer.Properties" GeneratedClassName="Settings">
|
||||||
|
<Profiles />
|
||||||
|
<Settings>
|
||||||
|
<Setting Name="width" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">500</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="height" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">500</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="split_height" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">200</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="columnWidth1" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">300</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="columnWidth2" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">50</Value>
|
||||||
|
</Setting>
|
||||||
|
<Setting Name="columnWidth3" Type="System.Double" Scope="User">
|
||||||
|
<Value Profile="(Default)">200</Value>
|
||||||
|
</Setting>
|
||||||
|
</Settings>
|
||||||
|
</SettingsFile>
|
175
ProgramQueuer/Queuer/EntryManager.cs
Executable file
|
@ -0,0 +1,175 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Queuer
|
||||||
|
{
|
||||||
|
public class EntryManager : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
bool _working;
|
||||||
|
bool _clearNext;
|
||||||
|
string _buffer;
|
||||||
|
ProgramEntry _currentEntry;
|
||||||
|
Process _currentProcess;
|
||||||
|
ProcessIOManager _processManager;
|
||||||
|
|
||||||
|
public EntryManager()
|
||||||
|
{
|
||||||
|
QueueList = new ObservableCollection<ProgramEntry>();
|
||||||
|
_buffer = "";
|
||||||
|
_currentEntry = null;
|
||||||
|
_processManager = new ProcessIOManager();
|
||||||
|
_processManager.StderrTextRead += new StringReadEventHandler(_processManager_StdoutTextRead);
|
||||||
|
_processManager.StdoutTextRead += new StringReadEventHandler(_processManager_StdoutTextRead);
|
||||||
|
_currentProcess = new Process();
|
||||||
|
_currentProcess.StartInfo.UseShellExecute = false;
|
||||||
|
_currentProcess.StartInfo.RedirectStandardOutput = true;
|
||||||
|
_currentProcess.StartInfo.RedirectStandardError = true;
|
||||||
|
_currentProcess.StartInfo.RedirectStandardInput = true;
|
||||||
|
_currentProcess.StartInfo.CreateNoWindow = true;
|
||||||
|
_currentProcess.EnableRaisingEvents = true;
|
||||||
|
_currentProcess.Exited += new EventHandler(_currentProcess_Exited);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableCollection<ProgramEntry> QueueList;
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
||||||
|
|
||||||
|
public ProgramEntry CurrentEntry
|
||||||
|
{
|
||||||
|
get { return _currentEntry; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_currentEntry = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("CurrentEntry"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Working
|
||||||
|
{
|
||||||
|
get { return _working; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_working = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Working"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunQueuer()
|
||||||
|
{
|
||||||
|
if (QueueList.Count > 0)
|
||||||
|
{
|
||||||
|
Working = true;
|
||||||
|
RunProgram(QueueList[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceStopCurrent()
|
||||||
|
{
|
||||||
|
_currentProcess.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceStop()
|
||||||
|
{
|
||||||
|
this.Working = false;
|
||||||
|
_currentProcess.Kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunProgram(ProgramEntry entry)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_currentEntry = entry;
|
||||||
|
_currentEntry.Output = "";
|
||||||
|
_currentEntry.Working = true;
|
||||||
|
_currentEntry.Status = "Starting";
|
||||||
|
_currentProcess.StartInfo.FileName = _currentEntry.Name;
|
||||||
|
_currentProcess.StartInfo.WorkingDirectory = new FileInfo(_currentEntry.Name).DirectoryName;
|
||||||
|
_currentProcess.Start();
|
||||||
|
_processManager.RunningProcess = _currentProcess;
|
||||||
|
_processManager.StartProcessOutputRead();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_currentEntry.Working = false;
|
||||||
|
_currentEntry.Finished = false;
|
||||||
|
_currentEntry.Output += string.Format("Error while starting {0}:\n\n{1}", _currentEntry.Name, e.ToString());
|
||||||
|
if (RunNext())
|
||||||
|
{
|
||||||
|
_currentEntry = null;
|
||||||
|
this.Working = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RunNext()
|
||||||
|
{
|
||||||
|
_currentEntry.Status = "Finished";
|
||||||
|
if (QueueList.IndexOf(_currentEntry) >= 0 && QueueList.IndexOf(_currentEntry) < QueueList.Count - 1)
|
||||||
|
RunProgram(QueueList[QueueList.IndexOf(_currentEntry) + 1]);
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _currentProcess_Exited(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_processManager.StopMonitoringProcessOutput();
|
||||||
|
_currentEntry.Working = false;
|
||||||
|
_currentEntry.Finished = true;
|
||||||
|
if (!this.Working)
|
||||||
|
return;
|
||||||
|
if (RunNext())
|
||||||
|
{
|
||||||
|
_currentEntry = null;
|
||||||
|
this.Working = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _processManager_StdoutTextRead(string text)
|
||||||
|
{
|
||||||
|
string[] lines = text.Split('\r');
|
||||||
|
if (!text.EndsWith("\r") && !text.EndsWith("\n") && _clearNext)
|
||||||
|
{
|
||||||
|
_buffer += text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = _buffer + text;
|
||||||
|
_buffer = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (_clearNext && text == "\n")
|
||||||
|
_clearNext = false;
|
||||||
|
|
||||||
|
while (text.IndexOf('\b') >= 0)
|
||||||
|
{
|
||||||
|
if (_currentEntry.Output.Length > 0 && _currentEntry.Output[_currentEntry.Output.Length - 1] != '\n')
|
||||||
|
_currentEntry.Output = _currentEntry.Output.Remove(_currentEntry.Output.Length - 1);
|
||||||
|
text = text.Remove(text.IndexOf('\b'), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clearNext && text.Replace("\n", "").Replace("\r", "").Trim() != "")
|
||||||
|
if (_currentEntry.Output.LastIndexOf('\n') < _currentEntry.Output.Length - 1)
|
||||||
|
_currentEntry.Output = _currentEntry.Output.Remove(_currentEntry.Output.LastIndexOf('\n') + 1) + text;
|
||||||
|
else
|
||||||
|
_currentEntry.Output += text;
|
||||||
|
else
|
||||||
|
_currentEntry.Output += text;
|
||||||
|
|
||||||
|
if (text.Replace("\n", "").Trim() != "")
|
||||||
|
_currentEntry.Status = text.Replace("\n", "").Replace("\r", "");
|
||||||
|
|
||||||
|
if (lines.Length == 2 && lines[1] == "")
|
||||||
|
_clearNext = true;
|
||||||
|
else
|
||||||
|
_clearNext = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
342
ProgramQueuer/Queuer/ProcessIOManager.cs
Executable file
|
@ -0,0 +1,342 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************************
|
||||||
|
* Author: Curt C.
|
||||||
|
* Email : harpyeaglecp@aol.com
|
||||||
|
*
|
||||||
|
* Project File: "Solving Problems of Monitoring Standard Output and Error Streams Of A Running Process"
|
||||||
|
* for http://www.codeproject.com
|
||||||
|
*
|
||||||
|
* This software is released under the Code Project Open License (CPOL)
|
||||||
|
*
|
||||||
|
* See official license description at: http://www.codeproject.com/info/cpol10.aspx
|
||||||
|
*
|
||||||
|
* This software is provided AS-IS without any warranty of any kind.
|
||||||
|
*
|
||||||
|
* >>> Please leave this header intact when using this file in other projects <<<
|
||||||
|
***************************************************************************************/
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Queuer
|
||||||
|
{
|
||||||
|
// Delegate definition used for events to receive notification
|
||||||
|
// of text read from stdout or stderr of a running process.
|
||||||
|
public delegate void StringReadEventHandler(string text);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class that manages the reading of the output produced by a given 'Process'
|
||||||
|
/// and reports the output via events.
|
||||||
|
/// Both standard error (stderr) and standard output (stdout) are
|
||||||
|
/// managed and reported.
|
||||||
|
/// The stdout and stderr monitoring and reading are each performed
|
||||||
|
/// by separate background threads. Each thread blocks on a Read()
|
||||||
|
/// method, waiting for text in the stream being monitored to become available.
|
||||||
|
///
|
||||||
|
/// Note the Process.RedirectStandardOutput must be set to true in order
|
||||||
|
/// to read standard output from the process, and the Process.RedirectStandardError
|
||||||
|
/// must be set to true to read standard error.
|
||||||
|
/// </summary>
|
||||||
|
public class ProcessIOManager
|
||||||
|
{
|
||||||
|
#region Private_Fields
|
||||||
|
// Command line process that is executing and being
|
||||||
|
// monitored by this class for stdout/stderr output.
|
||||||
|
private Process runningProcess;
|
||||||
|
|
||||||
|
// Thread to monitor and read standard output (stdout)
|
||||||
|
private Thread stdoutThread;
|
||||||
|
|
||||||
|
// Thread to monitor and read standard error (stderr)
|
||||||
|
private Thread stderrThread;
|
||||||
|
|
||||||
|
// Buffer to hold characters read from either stdout, or stderr streams
|
||||||
|
private StringBuilder streambuffer;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public_Properties_And_Events
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the process being monitored
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The running process.</value>
|
||||||
|
public Process RunningProcess
|
||||||
|
{
|
||||||
|
get { return runningProcess; }
|
||||||
|
set { runningProcess = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event to notify of a string read from stdout stream
|
||||||
|
public event StringReadEventHandler StdoutTextRead;
|
||||||
|
|
||||||
|
// Event to notify of a string read from stderr stream
|
||||||
|
public event StringReadEventHandler StderrTextRead;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructor_And_Initialization
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ProcessIOManager"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="StartProcessOutputRead"/>
|
||||||
|
public ProcessIOManager()
|
||||||
|
{
|
||||||
|
this.streambuffer = new StringBuilder(256);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ProcessIOManager"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process">The process.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Does not automatically start listening for stdout/stderr.
|
||||||
|
/// Call StartProcessOutputRead() to begin listening for process output.
|
||||||
|
/// </remarks>
|
||||||
|
/// <seealso cref="StartProcessOutputRead"/>
|
||||||
|
public ProcessIOManager(Process process)
|
||||||
|
{
|
||||||
|
if (process == null)
|
||||||
|
throw new Exception("ProcessIoManager unable to set running process - null value is supplied");
|
||||||
|
|
||||||
|
if (process.HasExited == true)
|
||||||
|
throw new Exception("ProcessIoManager unable to set running process - the process has already existed.");
|
||||||
|
|
||||||
|
this.runningProcess = process;
|
||||||
|
this.streambuffer = new StringBuilder(256);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public_Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the background threads reading any output produced (standard output, standard error)
|
||||||
|
/// that is produces by the running process.
|
||||||
|
/// </summary>
|
||||||
|
public void StartProcessOutputRead()
|
||||||
|
{
|
||||||
|
// Just to make sure there aren't previous threads running.
|
||||||
|
StopMonitoringProcessOutput();
|
||||||
|
|
||||||
|
// Make sure we have a valid, running process
|
||||||
|
CheckForValidProcess("Unable to start monitoring process output.", true);
|
||||||
|
|
||||||
|
// If the stdout is redirected for the process, then start
|
||||||
|
// the stdout thread, which will manage the reading of stdout from the
|
||||||
|
// running process, and report text read via supplied events.
|
||||||
|
if (runningProcess.StartInfo.RedirectStandardOutput == true)
|
||||||
|
{
|
||||||
|
stdoutThread = new Thread(new ThreadStart(ReadStandardOutputThreadMethod));
|
||||||
|
// Make thread a background thread - if it was foreground, then
|
||||||
|
// the thread could hang up the process from exiting. Background
|
||||||
|
// threads will be forced to stop on main thread termination.
|
||||||
|
stdoutThread.IsBackground = true;
|
||||||
|
stdoutThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the stderr is redirected for the process, then start
|
||||||
|
// the stderr thread, which will manage the reading of stderr from the
|
||||||
|
// running process, and report text read via supplied events.
|
||||||
|
if (runningProcess.StartInfo.RedirectStandardError == true)
|
||||||
|
{
|
||||||
|
stderrThread = new Thread(new ThreadStart(ReadStandardErrorThreadMethod));
|
||||||
|
stderrThread.IsBackground = true;
|
||||||
|
stderrThread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the supplied text string to the standard input (stdin) of the running process
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>In order to be able to write to the Process, the StartInfo.RedirectStandardInput must be set to true.</remarks>
|
||||||
|
/// <param name="text">The text to write to running process input stream.</param>
|
||||||
|
public void WriteStdin(string text)
|
||||||
|
{
|
||||||
|
// Make sure we have a valid, running process
|
||||||
|
CheckForValidProcess("Unable to write to process standard input.", true);
|
||||||
|
if (runningProcess.StartInfo.RedirectStandardInput == true)
|
||||||
|
runningProcess.StandardInput.WriteLine(text);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private_Methods
|
||||||
|
/// <summary>
|
||||||
|
/// Checks for valid (non-null Process), and optionally check to see if the process has exited.
|
||||||
|
/// Throws Exception if process is null, or if process has existed and checkForHasExited is true.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="errorMessageText">The error message text to display if an exception is thrown.</param>
|
||||||
|
/// <param name="checkForHasExited">if set to <c>true</c> [check if process has exited].</param>
|
||||||
|
private void CheckForValidProcess(string errorMessageText, bool checkForHasExited)
|
||||||
|
{
|
||||||
|
errorMessageText = (errorMessageText == null ? "" : errorMessageText.Trim());
|
||||||
|
if (runningProcess == null)
|
||||||
|
throw new Exception(errorMessageText + " (Running process must be available)");
|
||||||
|
|
||||||
|
if (checkForHasExited && runningProcess.HasExited)
|
||||||
|
throw new Exception(errorMessageText + " (Process has exited)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read characters from the supplied stream, and accumulate them in the
|
||||||
|
/// 'streambuffer' variable until there are no more characters to read.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstCharRead">The first character that has already been read.</param>
|
||||||
|
/// <param name="streamReader">The stream reader to read text from.</param>
|
||||||
|
/// <param name="isstdout">if set to <c>true</c> the stream is assumed to be standard output, otherwise assumed to be standard error.</param>
|
||||||
|
private void ReadStream(int firstCharRead, StreamReader streamReader, bool isstdout)
|
||||||
|
{
|
||||||
|
// One of the streams (stdout, stderr) has characters ready to be written
|
||||||
|
// Flush the supplied stream until no more characters remain to be read.
|
||||||
|
// The synchronized/ locked section of code to prevent the other thread from
|
||||||
|
// reading its stream at the same time, producing intermixed stderr/stdout results.
|
||||||
|
// If the threads were not synchronized, the threads
|
||||||
|
// could read from both stream simultaneously, and jumble up the text with
|
||||||
|
// stderr and stdout text intermixed.
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
// Single character read from either stdout or stderr
|
||||||
|
int ch;
|
||||||
|
// Clear the stream buffer to hold the text to be read
|
||||||
|
streambuffer.Length = 0;
|
||||||
|
|
||||||
|
//Console.WriteLine("CHAR=" + firstCharRead);
|
||||||
|
streambuffer.Append((char)firstCharRead);
|
||||||
|
|
||||||
|
// While there are more characters to be read
|
||||||
|
while (streamReader.Peek() > -1)
|
||||||
|
{
|
||||||
|
// Read the character in the queue
|
||||||
|
ch = streamReader.Read();
|
||||||
|
|
||||||
|
if (ch != '\n' && streambuffer.Length > 0 && streambuffer[streambuffer.Length - 1] == '\r')
|
||||||
|
NotifyAndFlushBufferText(streambuffer, isstdout);
|
||||||
|
|
||||||
|
// Accumulate the characters read in the stream buffer
|
||||||
|
streambuffer.Append((char)ch);
|
||||||
|
|
||||||
|
// Send text one line at a time - much more efficient than
|
||||||
|
// one character at a time
|
||||||
|
if (ch == '\n')
|
||||||
|
NotifyAndFlushBufferText(streambuffer, isstdout);
|
||||||
|
}
|
||||||
|
// Flush any remaining text in the buffer
|
||||||
|
NotifyAndFlushBufferText(streambuffer, isstdout);
|
||||||
|
} // End lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the OnStdoutTextRead (if isstdout==true)/ OnStderrTextRead events
|
||||||
|
/// with the supplied streambuilder 'textbuffer', then clears
|
||||||
|
/// textbuffer after event is invoked.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="textbuffer">The textbuffer containing the text string to pass to events.</param>
|
||||||
|
/// <param name="isstdout">if set to true, the stdout event is invoked, otherwise stedrr event is invoked.</param>
|
||||||
|
private void NotifyAndFlushBufferText(StringBuilder textbuffer, bool isstdout)
|
||||||
|
{
|
||||||
|
if (textbuffer.Length > 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isstdout == true && StdoutTextRead != null)
|
||||||
|
{ // Send notificatin of text read from stdout
|
||||||
|
StdoutTextRead(textbuffer.ToString());
|
||||||
|
}
|
||||||
|
else if (isstdout == false && StderrTextRead != null)
|
||||||
|
{ // Send notificatin of text read from stderr
|
||||||
|
StderrTextRead(textbuffer.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// 'Clear' the text buffer
|
||||||
|
textbuffer.Length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method started in a background thread (stdoutThread) to manage the reading and reporting of
|
||||||
|
/// standard output text produced by the running process.
|
||||||
|
/// </summary>
|
||||||
|
private void ReadStandardOutputThreadMethod()
|
||||||
|
{
|
||||||
|
// Main entry point for thread - make sure the main entry method
|
||||||
|
// is surrounded with try catch, so an uncaught exception won't
|
||||||
|
// screw up the entire application
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// character read from stdout
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
// The Read() method will block until something is available
|
||||||
|
while (runningProcess != null && (ch = runningProcess.StandardOutput.Read()) > -1)
|
||||||
|
{
|
||||||
|
// a character has become available for reading
|
||||||
|
// block the other thread and process this stream's input.
|
||||||
|
ReadStream(ch, runningProcess.StandardOutput, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ProcessIoManager.ReadStandardOutputThreadMethod()- Exception caught:" +
|
||||||
|
ex.Message + "\nStack Trace:" + ex.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Method started in a background thread (stderrThread) to manage the reading and reporting of
|
||||||
|
/// standard error text produced by the running process.
|
||||||
|
/// </summary>
|
||||||
|
private void ReadStandardErrorThreadMethod()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Character read from stderr
|
||||||
|
int ch;
|
||||||
|
// The Read() method will block until something is available
|
||||||
|
while (runningProcess != null && (ch = runningProcess.StandardError.Read()) > -1)
|
||||||
|
ReadStream(ch, runningProcess.StandardError, false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ProcessIoManager.ReadStandardErrorThreadMethod()- Exception caught:" +
|
||||||
|
ex.Message + "\nStack Trace:" + ex.StackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops both the standard input and stardard error background reader threads (via the Abort() method)
|
||||||
|
/// </summary>
|
||||||
|
public void StopMonitoringProcessOutput()
|
||||||
|
{
|
||||||
|
// Stop the stdout reader thread
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (stdoutThread != null)
|
||||||
|
stdoutThread.Abort();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ProcessIoManager.StopReadThreads()-Exception caught on stopping stdout thread.\n" +
|
||||||
|
"Exception Message:\n" + ex.Message + "\nStack Trace:\n" + ex.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the stderr reader thread
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (stderrThread != null)
|
||||||
|
stderrThread.Abort();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("ProcessIoManager.StopReadThreads()-Exception caught on stopping stderr thread.\n" +
|
||||||
|
"Exception Message:\n" + ex.Message + "\nStack Trace:\n" + ex.StackTrace);
|
||||||
|
}
|
||||||
|
stdoutThread = null;
|
||||||
|
stderrThread = null;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
85
ProgramQueuer/Queuer/ProgramEntry.cs
Executable file
|
@ -0,0 +1,85 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ProgramQueuer.Queuer
|
||||||
|
{
|
||||||
|
public class ProgramEntry : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
bool _error;
|
||||||
|
bool _working;
|
||||||
|
bool _finished;
|
||||||
|
string _name;
|
||||||
|
string _output;
|
||||||
|
string _status;
|
||||||
|
|
||||||
|
public ProgramEntry()
|
||||||
|
{
|
||||||
|
Finished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged = delegate { };
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return _name; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_name = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Output
|
||||||
|
{
|
||||||
|
get { return _output; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_output = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Output"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Status
|
||||||
|
{
|
||||||
|
get { return _status; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_status = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Status"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Finished
|
||||||
|
{
|
||||||
|
get { return _finished; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_finished = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Finished"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Working
|
||||||
|
{
|
||||||
|
get { return _working; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_working = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Working"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Error
|
||||||
|
{
|
||||||
|
get { return _error; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_error = value;
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs("Error"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
ProgramQueuer/Resources/accept.png
Executable file
After Width: | Height: | Size: 984 B |
BIN
ProgramQueuer/Resources/add.png
Executable file
After Width: | Height: | Size: 940 B |
BIN
ProgramQueuer/Resources/application.png
Executable file
After Width: | Height: | Size: 554 B |
BIN
ProgramQueuer/Resources/play.png
Executable file
After Width: | Height: | Size: 923 B |
BIN
ProgramQueuer/Resources/remove.png
Executable file
After Width: | Height: | Size: 1,004 B |
BIN
ProgramQueuer/Resources/stop.png
Executable file
After Width: | Height: | Size: 913 B |
BIN
ProgramQueuer/Resources/warning.png
Executable file
After Width: | Height: | Size: 713 B |
BIN
ProgramQueuer/Resources/window.png
Executable file
After Width: | Height: | Size: 307 B |
12
ProgramQueuer/Settings.xaml
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
<Window x:Class="ProgramQueuer.Settings"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="Settings" Height="300" Width="300">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
26
ProgramQueuer/Settings.xaml.cs
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace ProgramQueuer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for Settings.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class Settings : Window
|
||||||
|
{
|
||||||
|
public Settings()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
ProgramQueuer/app.config
Executable file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<configSections>
|
||||||
|
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
|
||||||
|
<section name="ProgramQueuer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
|
||||||
|
</sectionGroup>
|
||||||
|
</configSections>
|
||||||
|
<userSettings>
|
||||||
|
<ProgramQueuer.Properties.Settings>
|
||||||
|
<setting name="width" serializeAs="String">
|
||||||
|
<value>500</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="height" serializeAs="String">
|
||||||
|
<value>500</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="split_height" serializeAs="String">
|
||||||
|
<value>200</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="columnWidth1" serializeAs="String">
|
||||||
|
<value>300</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="columnWidth2" serializeAs="String">
|
||||||
|
<value>50</value>
|
||||||
|
</setting>
|
||||||
|
<setting name="columnWidth3" serializeAs="String">
|
||||||
|
<value>200</value>
|
||||||
|
</setting>
|
||||||
|
</ProgramQueuer.Properties.Settings>
|
||||||
|
</userSettings>
|
||||||
|
</configuration>
|
BIN
ProgramQueuer/reports.ico
Executable file
After Width: | Height: | Size: 109 KiB |