using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using TaskManagerDemo.Controls;
using TaskManagerDemo.ExampleServiceReference;
using TaskManagerDemo.Utility;
namespace TaskManagerDemo
{
public partial class MainPage : UserControl
{
#region /* Demo of TaskManager features and usage */
private const bool easierVersion = true;
private ExampleServiceClient serviceClient = new ExampleServiceClient();
private Random random = new Random();
private int factor;
// Declare and partially initialize the required TaskManager objects.
private TaskManager refreshTask = new TaskManager("refresh") { AutoComplete = true };
private TaskManager initialGetTask = new TaskManager("initialGetCities") { AutoComplete = true };
private AsyncServiceCallManager getAllCityZipCodesTask = new AsyncServiceCallManager("getAllCityZipCodes");
private AsyncServiceCallManager getSelectedCitiesTask = new AsyncServiceCallManager("getSelectedCities");
private QueuedAsyncServiceCallManager getSelectedZipCodesTask = new QueuedAsyncServiceCallManager("getSelectedZipCodes");
private List<CityZipCode> allCityZipCodeList;
private List<CityZipCode> selectedCityList;
private List<CityZipCode> selectedZipCodeList;
private CityComparer cityComparer = new CityComparer();
private ZipCodeComparer zipCodeComparer = new ZipCodeComparer();
public MainPage()
{
InitializeComponent();
App.Current.Host.Content.Resized += new EventHandler(Content_Resized);
// Finish initializing the TaskManager objects, building a tree with two levels:
// refreshTask
// +- initialGetTask
// | +- getAllCityZipCodesTask
// | +- getSelectedCitiesTask
// +- getSelectedZipCodesTask
initialGetTask.AddChildren(new TaskManager[] { getAllCityZipCodesTask, getSelectedCitiesTask });
refreshTask.AddChildren(new TaskManager[] { initialGetTask, getSelectedZipCodesTask });
getSelectedZipCodesTask.Prerequisite = initialGetTask;
refreshTask.StatusChanged += new EventHandler<EventArgs>(refreshTask_StatusChanged);
refreshTask.SetAllStatus(TaskStatus.NotStarted);
serviceClient.GetAllCityZipCodesCompleted += new EventHandler<GetAllCityZipCodesCompletedEventArgs>(serviceClient_GetAllCityZipCodesCompleted);
serviceClient.GetSelectedCitiesCompleted += new EventHandler<GetSelectedCitiesCompletedEventArgs>(serviceClient_GetSelectedCitiesCompleted);
serviceClient.GetSelectedZipCodesCompleted += new EventHandler<GetSelectedZipCodesCompletedEventArgs>(serviceClient_GetSelectedZipCodesCompleted);
SetWindowDimensions();
RefreshData();
DemoNewFeatures();
}
/// <summary>
/// If run in the debugger, demonstrates modifications and added features as of March, 2010.
/// </summary>
private void DemoNewFeatures()
{
TaskManager initTaskManager = new TaskManager("init") { AutoComplete = true };
AsyncServiceCallManager initPart1TaskManager = new AsyncServiceCallManager("initPart1");
AsyncServiceCallManager initPart2TaskManager = new AsyncServiceCallManager("initPart2");
initTaskManager.AddChildren(new TaskManager[] { initPart1TaskManager, initPart2TaskManager });
TaskManager refreshAllTaskManager = new TaskManager("refresh") { AutoComplete = true };
AsyncServiceCallManager refreshPart1TaskManager = new AsyncServiceCallManager("refreshPart1");
AsyncServiceCallManager refreshPart2TaskManager = new AsyncServiceCallManager("refreshPart2");
refreshAllTaskManager.AddChildren(new TaskManager[] { initTaskManager, refreshPart1TaskManager, refreshPart2TaskManager });
refreshAllTaskManager.SetAllStatus(TaskStatus.NotStarted);
string[] statusesArray = new string[6];
refreshAllTaskManager.SetAllStatus(TaskStatus.InProgress);
statusesArray[0] = DumpStatuses(refreshAllTaskManager);
// Imagine a sequence of service calls that progressively completes the full set of tasks.
initPart1TaskManager.Status = TaskStatus.Completed;
initPart2TaskManager.Status = TaskStatus.Completed;
refreshPart1TaskManager.Status = TaskStatus.Completed;
refreshPart2TaskManager.Status = TaskStatus.Completed;
statusesArray[1] = DumpStatuses(refreshAllTaskManager);
// We want to leave the task tree intact, refresh often and occasionally re-initialize, with or without doing the refresh tasks.
// Here is how we would work with the init tasks separately from the refresh tasks, without modifying the task tree.
initTaskManager.SetAllStatus(TaskStatus.InProgress, true);
statusesArray[2] = DumpStatuses(refreshAllTaskManager);
// Imagine a sequence of service calls that progressively completes the init tasks.
initPart1TaskManager.Status = TaskStatus.Completed;
initPart2TaskManager.Status = TaskStatus.Completed;
statusesArray[3] = DumpStatuses(refreshAllTaskManager);
// Here is how we would work with the refresh tasks separately from the init tasks, without modifying the task tree.
refreshAllTaskManager.SetAllStatus(TaskStatus.InProgress, node => (node != initTaskManager && initTaskManager.FindDescendant(node.Name) == null));
statusesArray[4] = DumpStatuses(refreshAllTaskManager);
// Imagine a sequence of service calls that progressively completes the refresh tasks.
refreshPart1TaskManager.Status = TaskStatus.Completed;
refreshPart2TaskManager.Status = TaskStatus.Completed;
statusesArray[5] = DumpStatuses(refreshAllTaskManager);
// Set a breakpoint on the method's closing curly braceto examine the statusesArray.
}
private string DumpStatuses(TaskManager root)
{
string result = string.Empty;
root.ForEach(node =>
{
if (result.Length > 0)
{
result += "; ";
}
result += node.Name + ": " + node.Status.ToString();
});
return result;
}
private void serviceClient_GetAllCityZipCodesCompleted(object sender, GetAllCityZipCodesCompletedEventArgs e)
{
allCityZipCodeList = e.Result.ToList();
getAllCityZipCodesTask.Status = TaskStatus.Completed;
}
private void serviceClient_GetSelectedCitiesCompleted(object sender, GetSelectedCitiesCompletedEventArgs e)
{
selectedCityList = e.Result.ToList();
if (selectedCityList.Count == 0)
{
getSelectedCitiesTask.Tag = new Exception("No selected Cities found.");
}
getSelectedCitiesTask.Status = TaskStatus.Completed;
}
private void serviceClient_GetSelectedZipCodesCompleted(object sender, GetSelectedZipCodesCompletedEventArgs e)
{
if (getSelectedZipCodesTask.Prerequisite.Status == TaskStatus.Completed)
{
selectedZipCodeList = e.Result.ToList();
if (selectedZipCodeList.Count == 0)
{
getSelectedZipCodesTask.Tag = new Exception("No selected ZIP Codes found.");
}
else if (selectedCityList.Count > 0)
{
#region /* Code that requires selectedCityList and selectedZipCodeList both to be refreshed */
// Some ZIP Codes may contain more than one "city" (small town).
// First get all of the CityZipCode correlations having ZIP Code value found in the list of selected Cities.
List<CityZipCode> filteredAllZipCodeList = allCityZipCodeList
.Where(zipCode => selectedCityList.FirstOrDefault(city => city.ZipCode == zipCode.ZipCode) != null).ToList();
// Get a list of distinct Cities such that each City is found (at least once) in the above list.
List<CityZipCode> newSelectedCityList = selectedCityList
.Where(city => filteredAllZipCodeList.FirstOrDefault(zipCode => zipCode.City == city.City) != null)
.Distinct(cityComparer).ToList();
// Replace the ZIP Code property of each correlation in the latter list with a CSV containing all of the ZIP Codes found in the City.
newSelectedCityList.ForEach(city => city.ZipCode = GetZipCodeCsv(selectedZipCodeList.Where(zipCode => zipCode.City == city.City).ToList()));
// Some cities contain more than one ZIP Code.
// First get all of the CityZipCode correlations having City value found in the list of selected ZIP Codes.
List<CityZipCode> filteredAllCityList = allCityZipCodeList
.Where(city => selectedZipCodeList.FirstOrDefault(zipCode => zipCode.City == city.City) != null).ToList();
// Get a list of distinct ZIP Codes such that each ZIP Code is found (at least once) in the above list.
List<CityZipCode> newSelectedZipCodeList = selectedZipCodeList
.Where(zipCode => filteredAllCityList.FirstOrDefault(city => city.ZipCode == zipCode.ZipCode) != null)
.Distinct(zipCodeComparer).ToList();
// Replace the City property of each correlation in the latter list with a CSV containing all of the Cities found in the ZIP Code.
newSelectedZipCodeList.ForEach(zipCode => zipCode.City = GetCityCsv(selectedCityList.Where(city => city.ZipCode == zipCode.ZipCode).ToList()));
if (easierVersion)
{
// Eliminate Cities that aren't in the list of selected ZIP Codes, and vice versa.
selectedCityList = newSelectedCityList.Where(city => newSelectedZipCodeList.FirstOrDefault(zipCode => zipCode.City.Contains(city.City)) != null).ToList();
selectedZipCodeList = newSelectedZipCodeList.Where(zipCode => newSelectedCityList.FirstOrDefault(city => city.ZipCode.Contains(zipCode.ZipCode)) != null).ToList();
}
else // harder version
{
// Some Cities may not have correlated ZIP Codes (and vice versa).
selectedCityList = newSelectedCityList;
selectedZipCodeList = newSelectedZipCodeList;
}
// Scramble the order of ZIP Codes relative to Cities. (Cities remain in alphabetical order.)
selectedZipCodeList.Sort(delegate(CityZipCode czc1, CityZipCode czc2)
{
int czc1hc = czc1.GetHashCode();
int czc2hc = czc2.GetHashCode();
return Math.Sign(czc1hc - czc2hc);
});
if (selectedCityList.Count == 0 || selectedZipCodeList.Count == 0)
{
refreshTask.Tag = new Exception("Empty City list and/or Zip Code list.");
}
else if (selectedCityList.Count == 1 && selectedZipCodeList.Count == 1)
{
refreshTask.Tag = new Exception("Only one City and one ZIP Code - too easy!");
}
#endregion /* Code that requires selectedCityList and selectedZipCodeList both to be refreshed */
}
getSelectedZipCodesTask.Status = TaskStatus.Completed;
}
else
{
getSelectedZipCodesTask.EnqueueResultHandling(new GenericEventHandler(GetSelectedZipCodesCompletedHelper), sender, e);
}
}
private string GetZipCodeCsv(List<CityZipCode> zipCodeList)
{
string result = string.Empty;
foreach (CityZipCode zipCode in zipCodeList)
{
if (result.Length > 0)
{
result += ",";
}
result += zipCode.ZipCode;
}
return result;
}
private string GetCityCsv(List<CityZipCode> zipCodeList)
{
string result = string.Empty;
foreach (CityZipCode city in zipCodeList)
{
if (result.Length > 0)
{
result += ",";
}
result += city.City;
}
return result;
}
private void GetSelectedZipCodesCompletedHelper(object sender, object e)
{
serviceClient_GetSelectedZipCodesCompleted(sender, (GetSelectedZipCodesCompletedEventArgs)e);
}
private void refreshTask_StatusChanged(object sender, EventArgs e)
{
if (refreshTask.Status == TaskStatus.Completed)
{
if (initialGetTask.Parent != null)
{
// Rearrange the TaskManager tree after the initial set of calls. The new tree, matching the service method
// calls required to refresh the UI, is structured like this:
// refreshTask
// +- getSelectedZipCodesTask
// +- getSelectedCitiesTask
// (It's assumed that the order of the child tasks is unimportant.)
// Notice also that the Prerequisite property of getSelectedZipCodesTask must be updated.
refreshTask.RemoveChild(initialGetTask);
initialGetTask.RemoveChild(getSelectedCitiesTask);
refreshTask.AddChild(getSelectedCitiesTask);
getSelectedZipCodesTask.Prerequisite = getSelectedCitiesTask;
}
ShowTaskErrors(refreshTask);
CityComboBox.ItemsSource = null;
CityComboBox.ItemsSource = selectedCityList;
ZipCodeComboBox.ItemsSource = null;
ZipCodeComboBox.ItemsSource = selectedZipCodeList;
CloseInProgressDialog(this);
}
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
// The redundant calls here are made deliberately for demonstration purposes, but similar redundancy can arise
// as a side-effect of interactions among event handlers and other necessary bits of code. A simple alternative
// to almost certainly more complicated ways of preventing or solving the problem is shown within RefreshData.
RefreshData();
RefreshData();
RefreshData();
}
private void RefreshData()
{
if (refreshTask.Status != TaskStatus.InProgress)
{
refreshTask.SetAllStatus(TaskStatus.InProgress);
ShowInProgressDialog(this, "In Progress ...", "Data Loading");
if (initialGetTask.Parent != null)
{
// First time only.
serviceClient.GetAllCityZipCodesAsync();
}
factor = random.Next();
serviceClient.GetSelectedCitiesAsync(factor);
serviceClient.GetSelectedZipCodesAsync(factor);
}
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CityComboBox.SelectedItem != null && ZipCodeComboBox.SelectedItem != null)
{
// The following expressions are functionally equivalent:
//bool itemsCorrelate = (((CityZipCode)CityComboBox.SelectedItem).ZipCode.Contains(((CityZipCode)ZipCodeComboBox.SelectedItem).ZipCode));
bool itemsCorrelate = (((CityZipCode)ZipCodeComboBox.SelectedItem).City.Contains(((CityZipCode)CityComboBox.SelectedItem).City));
IsZipCodeInCityTextBlock.Text = string.Format("ZIP Code {0} with city.", itemsCorrelate ? "correlates" : "does not correlate");
IsZipCodeInCityTextBlock.Foreground = itemsCorrelate ? solidBlackBrush : solidRedBrush;
}
else
{
IsZipCodeInCityTextBlock.Text = string.Empty;
}
}
public void ShowTaskErrors(TaskManager currentTask)
{
TaskManager thisTask = currentTask;
while (thisTask != null && thisTask.Tag == null)
{
thisTask = TaskManager.WalkToNextFrom(thisTask);
}
if (thisTask != null && thisTask.Tag != null)
{
TaskManager nextTask = TaskManager.WalkToNextFrom(thisTask);
object errorInfo = thisTask.Tag;
thisTask.Tag = null;
Exception ex = errorInfo as Exception;
if (ex != null)
{
// ShowMessageDialog shows the error message.
// When the user clicks OK, the click event handler calls ShowTaskErrors,
// passing nextTask (temporarily saved in savedNextTask) as currentTask.
// Thus the tree of TaskManager objects is walked and all attached error messages are displayed.
ShowMessageDialog(ex.Message + ex.StackTrace, "An error occurred:", nextTask);
}
}
}
#endregion /* Demo of TaskManager features and usage */
#region /* Specific implementations of popup "in progress" dialog and message dialog */
private SolidColorBrush solidBlackBrush = new SolidColorBrush(Colors.Black);
private SolidColorBrush solidRedBrush = new SolidColorBrush(Colors.Red);
private double windowHeight;
private double windowWidth;
private Point dialogLocation = new Point(100, 100);
private bool isInProgressDialogVisible;
private InProgressDialog inProgressDialog;
private PopupWindow popupWindowForDialog;
private InputDialog inputDialog;
private TaskManager savedNextTask;
private void Content_Resized(object sender, EventArgs e)
{
SetWindowDimensions();
}
private void SetWindowDimensions()
{
windowHeight = (App.Current.Host.Content.ActualHeight - 30);
windowWidth = App.Current.Host.Content.ActualWidth - 50;
if (windowWidth < this.MinWidth)
windowWidth = this.MinWidth;
}
public void ShowInProgressDialog(object sender, string title, string text)
{
if (!isInProgressDialogVisible)
{
inProgressDialog = new InProgressDialog();
inProgressDialog.TitleDialog = title;
inProgressDialog.MessageText = text;
inProgressDialog.ResetWidth();
NewWindow(true, "In Progress...", sender);
isInProgressDialogVisible = true;
}
}
public void CloseInProgressDialog(object sender)
{
RemoveWindow("In Progress...", sender);
isInProgressDialogVisible = false;
}
public void ShowMessageDialog(string text, string title, TaskManager nextTask)
{
savedNextTask = nextTask;
ShowMessageDialog(text, title);
}
public void ShowMessageDialog(string text, string title)
{
double width = 250;
double height = 120;
GetDialogDimensions(text, title, out width, out height);
inputDialog = new InputDialog();
inputDialog.OK += new EventHandler<EventArgs>(InputDialogMessageDialog_OK);
inputDialog.Width = width;
inputDialog.Height = height;
inputDialog.SetButtonsContent("OK", string.Empty, text, width, height);
NewWindow(true, title, this);
}
private void GetDialogDimensions(string text, string title, out double width, out double height)
{
text = string.IsNullOrEmpty(text) ? string.Empty : text.Trim();
title = string.IsNullOrEmpty(title) ? string.Empty : title.Trim();
double textWidth = (text.Length > 100 ? 100 : text.Length) * 10 + 60;
double titleWidth = title.Length * 11 + 60;
width = Math.Min(Math.Max(textWidth, titleWidth), 400);
double textHeight = text.Length * 11 + 30;
textHeight = GetHeightFromText(text);
height = Math.Min(textHeight, 400);
}
private double GetHeightFromText(string text)
{
string[] splitText = text.Split('\n');
double height = 95;
for (int i = 0; i < splitText.Length; i++)
{
int length = splitText[i].Length;
if (length < 100)
height = height + 30;
else
height = height + (length / 100) * 30;
}
return height;
}
private void InputDialogMessageDialog_OK(object sender, EventArgs e)
{
RemoveWindow(popupWindowForDialog.Title, sender);
if (savedNextTask != null)
{
ShowTaskErrors(savedNextTask);
}
}
public void NewWindow(bool Modal, string windowType, object source)
{
popupWindowForDialog = new PopupWindow();
popupWindowForDialog.IsModal = Modal;
popupWindowForDialog.CanResize = false;
popupWindowForDialog.ShowStatus = false;
popupWindowForDialog.Title = windowType;
popupWindowForDialog.IsCloseButtonVisible = true;
switch (windowType)
{
case "In Progress...":
popupWindowForDialog.IsCloseButtonVisible = false;
popupWindowForDialog.ShowCloseButton();
popupWindowForDialog.Content = inProgressDialog;
popupWindowForDialog.Height = inProgressDialog.VerticalOffsetY + 30;
double left = windowWidth / 2 - inProgressDialog.HorizontalOffsetX / 2;
double top;
if (dialogLocation == null)
{
top = inProgressDialog.VerticalOffsetY / 2;
popupWindowForDialog.OffsetX = left;
popupWindowForDialog.OffsetY = top;
}
else
{
top = dialogLocation.Y - inProgressDialog.VerticalOffsetY - 10;
if (top <= inProgressDialog.VerticalOffsetY)
top = inProgressDialog.VerticalOffsetY;
dialogLocation = new Point(left, top);
popupWindowForDialog.OffsetX = dialogLocation.X;
popupWindowForDialog.OffsetY = dialogLocation.Y;
}
LayoutRoot.Children.Add(popupWindowForDialog);
Grid.SetRow(popupWindowForDialog, 0);
Grid.SetRowSpan(popupWindowForDialog, 6);
Grid.SetColumn(popupWindowForDialog, 0);
Grid.SetColumnSpan(popupWindowForDialog, 5);
break;
default:
popupWindowForDialog.IsCloseButtonVisible = false;
popupWindowForDialog.ShowCloseButton();
popupWindowForDialog.Content = inputDialog;
popupWindowForDialog.Width = inputDialog.Width;
popupWindowForDialog.Height = inputDialog.Height + 30;
if (dialogLocation == null)
{
popupWindowForDialog.OffsetX = inputDialog.Width;
popupWindowForDialog.OffsetY = inputDialog.Height / 2;
}
else
{
popupWindowForDialog.OffsetX = dialogLocation.X;
popupWindowForDialog.OffsetY = dialogLocation.Y;
}
LayoutRoot.Children.Add(popupWindowForDialog);
Grid.SetRow(popupWindowForDialog, 0);
Grid.SetRowSpan(popupWindowForDialog, 6);
Grid.SetColumn(popupWindowForDialog, 0);
Grid.SetColumnSpan(popupWindowForDialog, 5);
break;
}
}
private void RemoveWindow(string windowName, object source)
{
try
{
List<DragDropControl> list = new List<DragDropControl>();
bool found = false;
foreach (UIElement u in LayoutRoot.Children)
{
DragDropControl dd = u as DragDropControl;
if (dd != null)
list.Add(dd);
}
foreach (UIElement u in list)
{
if (u.GetType() == typeof(PopupWindow))
{
PopupWindow w = u as PopupWindow;
if (w.Title == windowName)
{
LayoutRoot.Children.Remove(u);
w.Close();
found = true;
}
else
{
if (w.Content.GetType() == typeof(InputDialog) && source.GetType() == typeof(InputDialog))
{
InputDialog dialog = (InputDialog)w.Content;
InputDialog sourceDialog = (InputDialog)source;
if (dialog == sourceDialog)
{
LayoutRoot.Children.Remove(u);
w.Close();
found = true;
}
else if (dialog.OK == null)
{
dialog.OK += new EventHandler<EventArgs>(InputDialogMessageDialog_OK);
dialog.CloseDialog();
}
}
}
}
}
if (!found)
{
foreach (UIElement u in list)
{
if (u.GetType() == typeof(PopupWindow))
{
PopupWindow w = u as PopupWindow;
if (w.Content.GetType() == typeof(InputDialog))
{
InputDialog dialog = (InputDialog)w.Content;
if (dialog.OK == null)
{
dialog.OK += new EventHandler<EventArgs>(InputDialogMessageDialog_OK);
dialog.CloseDialog();
}
}
}
}
}
}
catch { }
}
#endregion /* Specific implementations of popup "in progress" dialog and message dialog */
}
}