This commit is contained in:
Jonatan Nilsson 2014-03-16 19:01:22 +00:00
parent f4f5746e99
commit b3da45eb94
33 changed files with 2847 additions and 0 deletions

20
ProgramQueuer.sln Executable file
View 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
View 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
View 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
{
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
}
}

View 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
}

View 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
}
}
}

View 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
}
}

View 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
View 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
View 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)
{
}
}
}

View 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>

View 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
View 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;
}
}
}
}

View 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
View 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;
}
}
}
}

View 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>

View 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;
}
}
}

View 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
}
}

View 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"));
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

BIN
ProgramQueuer/Resources/add.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

BIN
ProgramQueuer/Resources/play.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,004 B

BIN
ProgramQueuer/Resources/stop.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

12
ProgramQueuer/Settings.xaml Executable file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB