Saturday 21 August 2010

Updating UI Elements using ProgressChanged event of BackgroundWorker

As you probably know, the only thread which can update a UI element, such as a Label, is the main thread which created that UI element.

In my previous article, I wrote about an approach to update the UI elements using the Dispatcher object of the UI element. Here I’d like to show you another approach which is to use ProgressChanged event of the BackgroundWorker:

public partial class Window1 : Window
{
BackgroundWorker aWorker = new BackgroundWorker();

public Window1()
{
InitializeComponent();
aWorker.WorkerSupportsCancellation = true;
aWorker.WorkerReportsProgress = true;
aWorker.DoWork += aWorker_DoWork;
aWorker.RunWorkerCompleted += aWorker_RunWorkerCompleted;
aWorker.ProgressChanged += new ProgressChangedEventHandler(aWorker_ProgressChanged);
}

/// <summary>
/// Handles the ProgressChanged event of the aWorker control. this runs on the main thread
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
void aWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
UpdateLabel(Int32.Parse(e.UserState.ToString()));
}

/// <summary>
/// Handles the DoWork event of the aWorker control.
/// This runs in a new thread
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
private void aWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = 0; i <= 500; i++)
{
Thread.Sleep(100);

if (aWorker.CancellationPending)
{
e.Cancel = true;
return;
}
aWorker.ReportProgress(0, i);
}
}

private void aWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (!(e.Cancelled))
Label2.Content = "Run Completed!";
else
Label2.Content = "Run Cancelled!";
}

private void UpdateLabel(int i)
{
Label1.Content = "Cycles: " + i.ToString();
}

private void btnStart_Click(object sender, RoutedEventArgs e)
{
aWorker.RunWorkerAsync();
}

private void btnCancel_Click(object sender, RoutedEventArgs e)
{
aWorker.CancelAsync();
}
}

Notes:
  • There are 3 easy steps you need to take to enable this:

    • Set WorkerReportsProgress property

    • Subscribe to ProgressChanged event

    • Call ReportProgress() method from the worker thread

WPF: How to Enable Journal When Using PageFunctions

PageFunctions are xamle pages which enable us to create complex journal and navigations. Unlike Page, PageFunction can be used to return an object to the caller page.

Caller Page:

public partial class Page1 : Page
{
public Page1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
PageFunction1 apage = new PageFunction1();
apage.Return += new ReturnEventHandler<object>(apage_Return);
NavigationService.Navigate(apage);
}

void apage_Return(object sender, ReturnEventArgs<object> e)
{
List<string> alist = (List<string>)e.Result;
foreach (string s in alist)
listBox1.Items.Add(s);
}
}

Notes:
  • Return event of a PageFunction is raised when the data is returned back from the PageFunction

CustomContentState:

This class helps to store the state for the contents of the journal when navigating forward or backward in a PageFunction:

[Serializable()]
public class CustomJournalEntry : CustomContentState
{
private List<ListBoxItem> atops;
private List<ListBoxItem> ctops;
public List<ListBoxItem> AvailableToppings
{
get
{
return atops;
}
set
{
atops = value;
}
}
public List<ListBoxItem> ChosenToppings
{
get
{
return ctops;
}
set
{
ctops = value;
}
}
public override string JournalEntryName
{
get
{
return "Custom Journal Entry";
}
}
public delegate void ReplayDelegate(CustomJournalEntry c);
private ReplayDelegate replaydelegate;
public override void Replay(NavigationService navigationService, NavigationMode mode)
{
this.replaydelegate(this);
}
public CustomJournalEntry(List<ListBoxItem> available, List<ListBoxItem> chosen, ReplayDelegate replay)
{
atops = available;
ctops = chosen;
replaydelegate = replay;
}
}

Notes:
  • This class inherits from CustomContentState class and is Serializable

  • Replay method has been overridden and it’s called by the navigation engine when moving forward or backward on the journal

PageFunction class:

public partial class PageFunction1 : PageFunction<Object>, IProvideCustomContentState
{
public PageFunction1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
List<ListBoxItem> alist = new List<ListBoxItem>();
List<ListBoxItem> blist = new List<ListBoxItem>();
foreach (ListBoxItem lll in listBox1.Items)
alist.Add(lll);
foreach (ListBoxItem ll in listBox2.Items)
blist.Add(ll);
NavigationService.AddBackEntry(new CustomJournalEntry(alist, blist, ReplayCallback));
ListBoxItem l = (ListBoxItem)listBox1.SelectedItem;
listBox1.Items.Remove(l);
listBox2.Items.Add(l);
}

private void button2_Click(object sender, RoutedEventArgs e)
{
List<ListBoxItem> alist = new List<ListBoxItem>();
List<ListBoxItem> blist = new List<ListBoxItem>();
foreach (ListBoxItem lll in listBox1.Items)
alist.Add(lll);
foreach (ListBoxItem ll in listBox2.Items)
blist.Add(ll);
NavigationService.AddBackEntry(new CustomJournalEntry(alist, blist, ReplayCallback));
ListBoxItem l = (ListBoxItem)listBox2.SelectedItem;
listBox2.Items.Remove(l);
listBox1.Items.Add(l);

}

private void Button3_Click(object sender, RoutedEventArgs e)
{
List<string> alist = new List<string>();
foreach (ListBoxItem l in listBox2.Items)
alist.Add(l.Content.ToString());
ReturnEventArgs<object> ee = new ReturnEventArgs<object>((object)alist);
OnReturn(ee);
}
private void ReplayCallback(CustomJournalEntry c)
{
listBox1.Items.Clear();
listBox2.Items.Clear();
foreach (ListBoxItem l in c.AvailableToppings)
listBox1.Items.Add(l);
foreach (ListBoxItem ll in c.ChosenToppings)
listBox2.Items.Add(ll);
}
public System.Windows.Navigation.CustomContentState GetContentState()
{
List<ListBoxItem> alist = new List<ListBoxItem>();
List<ListBoxItem> blist = new List<ListBoxItem>();
foreach (ListBoxItem l in listBox1.Items)
alist.Add(l);
foreach (ListBoxItem ll in listBox2.Items)
blist.Add(ll);
return new CustomJournalEntry(alist, blist, ReplayCallback);
}

}

Notes:
  • This PageFunction should implement IProvideCustomContentState so that GetContentState is implemented. This method is called when moving backward and forward

  • OnReturn() method is called when you want to return the result of the PageFunction to the caller page

  • NavigationService.AddBackEntry() is used to add a current state of the PageFunction to the journal.

Thursday 19 August 2010

Java

C# and Java language syntax comparison: http://www.25hoursaday.com/CsharpVsJava.html

Wednesday 18 August 2010

IPhone Development


Environments/Tools:
Languages supported:
  • C, C++, Objective-C, JavaScript
Learn about Objective-C:

Monday 2 August 2010

How to Connect 3D Points With Each Other in WPF?

 

Imagine you have some 3D spheres that you want them to be connected to each other such as:

image

Assuming you know a point in each of these spheres, the following Code uses a 3D cube to connect 2 3D Points with each other:

private void ConnectPoints(Point3D firstPoint, Point3D secondPoint, DiffuseMaterial diffuseMaterial, double thickness)
{
MeshGeometry3D linkCube = GetCube(firstPoint, secondPoint, thickness);

GeometryModel3D mGeometryLink = new GeometryModel3D(linkCube, diffuseMaterial);
mGeometryLink.Transform = new Transform3DGroup();

ModelVisual3D modelVisual3D = new ModelVisual3D();
modelVisual3D.Content = mGeometryLink;

_models.Add(modelVisual3D);
}

private MeshGeometry3D GetCube(Point3D firstPoint, Point3D secondPoint, double thickness)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(new Point3D(firstPoint.X, firstPoint.Y, firstPoint.Z));
mesh.Positions.Add(new Point3D(firstPoint.X + thickness, firstPoint.Y, firstPoint.Z));
mesh.Positions.Add(new Point3D(firstPoint.X + thickness, firstPoint.Y - thickness, firstPoint.Z));
mesh.Positions.Add(new Point3D(firstPoint.X, firstPoint.Y - thickness, firstPoint.Z));

mesh.Positions.Add(new Point3D(secondPoint.X, secondPoint.Y, secondPoint.Z));
mesh.Positions.Add(new Point3D(secondPoint.X + thickness, secondPoint.Y, secondPoint.Z));
mesh.Positions.Add(new Point3D(secondPoint.X + thickness, secondPoint.Y - thickness, secondPoint.Z));
mesh.Positions.Add(new Point3D(secondPoint.X, secondPoint.Y - thickness, secondPoint.Z));

int k = mesh.Positions.Count - 8;
// Front face
mesh.TriangleIndices.Add(0 + k);
mesh.TriangleIndices.Add(1 + k);
mesh.TriangleIndices.Add(2 + k);
mesh.TriangleIndices.Add(2 + k);
mesh.TriangleIndices.Add(3 + k);
mesh.TriangleIndices.Add(0 + k);

// Back face
mesh.TriangleIndices.Add(6 + k);
mesh.TriangleIndices.Add(5 + k);
mesh.TriangleIndices.Add(4 + k);
mesh.TriangleIndices.Add(4 + k);
mesh.TriangleIndices.Add(7 + k);
mesh.TriangleIndices.Add(6 + k);

// Right face
mesh.TriangleIndices.Add(1 + k);
mesh.TriangleIndices.Add(5 + k);
mesh.TriangleIndices.Add(2 + k);
mesh.TriangleIndices.Add(5 + k);
mesh.TriangleIndices.Add(6 + k);
mesh.TriangleIndices.Add(2 + k);

// Top face
mesh.TriangleIndices.Add(2 + k);
mesh.TriangleIndices.Add(6 + k);
mesh.TriangleIndices.Add(3 + k);
mesh.TriangleIndices.Add(3 + k);
mesh.TriangleIndices.Add(6 + k);
mesh.TriangleIndices.Add(7 + k);

// Bottom face
mesh.TriangleIndices.Add(5 + k);
mesh.TriangleIndices.Add(1 + k);
mesh.TriangleIndices.Add(0 + k);
mesh.TriangleIndices.Add(0 + k);
mesh.TriangleIndices.Add(4 + k);
mesh.TriangleIndices.Add(5 + k);

// Right face
mesh.TriangleIndices.Add(4 + k);
mesh.TriangleIndices.Add(0 + k);
mesh.TriangleIndices.Add(3 + k);
mesh.TriangleIndices.Add(3 + k);
mesh.TriangleIndices.Add(7 + k);
mesh.TriangleIndices.Add(4 + k);
return mesh;
}