Friday, November 14, 2008

Tutor.com Classroom How-To #10: manually detecting and installing the Silverlight plug-in

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Use the isInstalled() function provided by Silverlight.js:


<script language="javascript" type="text/javascript">

Silverlight.InstallAndCreateSilverlight = function(version)
{
var retryTimeout = 1000; //the interval at which instantiation is attempted(ms)
if (Silverlight.isInstalled(version))
{
...
}
else if (!Silverlight.isInstalled(version))
{
TimeoutDelegate = function()
{
Silverlight.InstallAndCreateSilverlight(version);
}

setTimeout(TimeoutDelegate, retryTimeout);
}
}

Silverlight.InstallAndCreateSilverlight('2.0.30923.0');

</script>

Thursday, November 13, 2008

Tutor.com Classroom How-To #9: creating a Path object from a Stroke

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Simply transfer Stroke stylus points to a PolyLineSegment on a Path:


static Path getPathFromStroke(Stroke str)
{
//make a path from this stroke
Path path = new Path();
PathGeometry pg = new PathGeometry();

//start our figure from the first stylus point in the stroke
PathFigure fg = new PathFigure();
fg.Segments = new PathSegmentCollection();
fg.StartPoint = new Point(str.StylusPoints[0].X, str.StylusPoints[0].Y);

PolyLineSegment seg = new PolyLineSegment();

//add each additional stylus point to the line segment
for (int x = 1; x < str.StylusPoints.Count; x++)
StylusPoint sp = str.StylusPoints[x];

seg.Points.Add(new Point(sp.X, sp.Y));

fg.Segments.Add(seg);

pg.Figures = new PathFigureCollection();
pg.Figures.Add(fg);

path.Data = pg;

return path;
}

Wednesday, November 12, 2008

Tutor.com Classroom How-To #8: trapping and processing mouse movement in an InkPresenter

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

In the MouseDown event, instantiate a module-level Stroke object (e.g. m_DrawingStroke) to collect points traversed by the mouse. Fire the StrokeAdded event so that listeners can prepare for the operation as necessary. Most importantly, call CaptureMouse() to notify the mouse input engine to give you high frequency mouse event notifications.


void Whiteboard_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//get our current position
Point p = e.GetPosition(this);

//create a new styluspoint collection for our new stroke
StylusPointCollection coll = new StylusPointCollection();
coll.Add(new StylusPoint() { X = p.X, Y = p.Y });

m_DrawingStroke = new Stroke(coll);

//fire notification event
if (this.StrokeAdded != null)
this.StrokeAdded(m_DrawingStroke);

//begin capturing mouse input
this.CaptureMouse();
}

In MouseMove, add the traversed point to the Stroke’s StylusPoints collection, and fire the StrokeChanging event to alert interested listeners.

void Whiteboard_MouseMove(object sender, MouseEventArgs e)
{
//get our current position
Point p = e.GetPosition(this);

//add the traversed point to our collection
StylusPoint sp = new StylusPoint() { X = p.X, Y = p.Y };
m_DrawingStroke.StylusPoints.Add(sp);

//fire notification event
if (this.StrokeChanging != null)
this.StrokeChanging(m_DrawingStroke);
}

In MouseUp, release mouse capture and fire the StrokeComplete event.

void Whiteboard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
//release mouse capture
this.ReleaseMouseCapture();

//fire notification event
if (this.StrokeComplete != null)
this.StrokeComplete(m_DrawingStroke);
}

Tuesday, November 11, 2008

Tutor.com Classroom How-To #7: screen-scraping an application via Win32 PrintWindow() API

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

In the WPF application, we make calls to the PrintWindow() Win32 API to gather screen contents. We then splice the return bitmap into a 4x4 grid and compare hashes of each of the slices with the previous slices’ hashes. If a slice has changed, we package up its bytes and ship it across the wire.

There are two key components to making this process of screen scrape, comparison, and messaging fast enough to run each second without overburdening the CPU. The first is that the entire process runs on a thread pool thread via a timer, which frees up the GUI. The second is that our Bitmap uses the Format32bppArgb pixel format, which uses more memory but optimizes performance of PrintWindow() calls.


[DllImport("user32.dll", SetLastError = true)]
static extern bool PrintWindow(IntPtr hwnd, IntPtr hDC, uint nFlags);

void processCapture()
{
using (Graphics g = Graphics.FromImage(captureBitmap))
{
//get window content
IntPtr hdc = g.GetHdc();
try
{
result = PrintWindow(m_Win32HostHandle, hdc);
}
finally
{
g.ReleaseHdc(hdc);
}

//process captureBitmap
...
}
}

Monday, November 10, 2008

Tutor.com Classroom How-To #6: creating a horizontal WrapPanel that is transform-aware and exposes row and column counts

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Begin by deriving from Panel, which is the base class for container controls, and override the MeasureOverride() and ArrangeOverride() functions. These functions are called iteratively as the layout engine determines how to distribute available visual space. Have them call a custom function that takes either the available size (from the measure pass) or the final size (from the arrange pass), and a ShouldArrange parameter to specify which pass is processing, and returns a Size.


private Size measureAndOptionallyArrangeItems(Size size, bool ShouldArrange)
{
Point point = new Point(0, 0);
Size s = new Size(size.Width, 0);

//consider scale transform that might be affecting us
double xfactor = 1;
double yfactor = 1;
if (this.RenderTransform != null)
{
if (this.RenderTransform is ScaleTransform)
{
ScaleTransform st = (ScaleTransform)this.RenderTransform;

xfactor = st.ScaleX;
yfactor = st.ScaleY;
}
}

double largestHeight = 0.0;

this.Rows = 0;
this.Cols = 0;

foreach (UIElement child in Children)
{
if (child.DesiredSize.Height > largestHeight)
largestHeight = child.DesiredSize.Height;

//first row?
if (this.Rows == 0)
this.Rows = 1;

double desiredWidth = child.DesiredSize.Width;

//does this child cause us to wrap?
if (point.X > 0 && point.X + desiredWidth > size.Width * (1 / xfactor))
{
this.Rows++;

s.Height += largestHeight * yfactor;

//goto the next line
point.X = 0;
point.Y += largestHeight;
largestHeight = child.DesiredSize.Height;

if (ShouldArrange)
child.Arrange(new Rect(point, new Point(point.X + desiredWidth, point.Y + child.DesiredSize.Height)));

//set our current location in this new line
point.X = desiredWidth;
}
else
{
if (ShouldArrange)
child.Arrange(new Rect(point, new Point(point.X + desiredWidth, point.Y + child.DesiredSize.Height)));

point.X = point.X + desiredWidth;

//if we're doing first row, set columns
if (this.Rows == 1)
this.Cols++;
}
}

s.Height += largestHeight * yfactor;

return s;
}

Friday, November 7, 2008

Tutor.com Classroom How-To #5: providing automatic reader-to-writer lock upgrade

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Create a class that contains a module-level System.Threading.ReaderWriterLock (e.g. m_Lock) and wrap the AcquireReaderLock() function. Then create an AcquireWriterLock() function that returns a LockCookie wrapper (SafeLockCookie), checks to see if the read lock is held, and upgrades if necessary, and a ReleaseWriterLock()function that processes the SafeLockCookie and releases the lock or downgrades back to read:


public SafeLockCookie AcquireWriterLock()
{
//if we have a read lock, upgrade
if (m_Lock.IsReaderLockHeld)
{
return new SafeLockCookie(m_Lock.UpgradeToWriterLock(TIMEOUT_MS));
}
else
{
m_Lock.AcquireWriterLock(TIMEOUT_MS);
return null;
}
}

public void ReleaseWriterLock(SafeLockCookie SafeLockCookie)
{
//do we need to downgrade?
if (SafeLockCookie != null)
{
m_Lock.DowngradeFromWriterLock(ref SafeLockCookie.LockCookie);
}
else
{
m_Lock.ReleaseWriterLock();
}
}

Then have calling functions request write locks by storing a SafeLockCookie when requesting the lock and returning it when releasing:

SafeLockCookie lc = m_Lock.AcquireWriterLock();
try
{
...
}
finally
{
m_Lock.ReleaseWriterLock(lc);
}

Thursday, November 6, 2008

Tutor.com Classroom How-To #4: enforcing an MVC-style architecture

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

The benefits of MVC-style architecture are numerous: testability, modifiability, simplicity of design, etc., and WPF/Silverlight Xaml “binding” provide an ideal structure to effectively detach the front-end from middle-tier business objects.

One simple way to enforce MVC-style architecture is to put classes with distinct responsibilities in separate assemblies, and add the minimal set of references only as required by each assembly. This enforces the principle of separation of concerns, since you’ve actually made it impossible for code in one assembly to be concerned with the business of another if it shouldn’t!

Wednesday, November 5, 2008

Tutor.com Classroom How-To #3: specifying "End-Of-Message" to a TCP/IP listener

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Since a TCP/IP Listener is constantly in a listening state, the Listener needs to know when to act on the bytes received. To accomplish this, listen first for 8 ASCII bytes that represent two 32-bit integers specifying the number of bytes the sender is sending, and then continue listening until receiving this number of bytes. This is an easier solution to implement than using a delimiter to specify end-of-message, since you don’t have to worry about delimiter escaping, and since you can specify the exact number of bytes to listen for via the third parameter to SocketAsyncEventArgs.SetBuffer().

Tuesday, November 4, 2008

Tutor.com Classroom How-To #2: connecting to a TCP/IP Listener

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Using the Socket class, we just need to create a SocketAsyncEventArgs and call ConnectAsync(). Notice an example of the “#if WPF” directive for subtle Silverlight/WPF differences (as described in the "Code Sharing" post) when we set the SocketAsyncEventArgs RemoteEndPoint.


public void Connect(string host, int port)
{
//instantiate socket
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//set up endpoint
#if WPF
m_SocketSendArgs.RemoteEndPoint = new IPEndPoint(Dns.GetHostAddresses(host)[0], port);
#else
m_SocketSendArgs.RemoteEndPoint = new DnsEndPoint(host, port);
#endif

//set up args
SocketAsyncEventArgs args = new SocketAsyncEventArgs();

args.UserToken = m_Socket;
args.RemoteEndPoint = m_SocketSendArgs.RemoteEndPoint;
args.Completed += new EventHandler(OnConnect);

m_Socket.ConnectAsync(args);
}

Monday, November 3, 2008

Tutor.com Classroom How-To #1: sharing code between Silverliht and WPF applications

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

To actually share the code, use compile-time code sharing by choosing “Add As Link” when adding project files, rather than using our source code management’s “Share”. Using the “Add As Link” technique ensures that as changes are made to shared code, successfully building the solution guarantees that the changes are valid across all three applications. Source code management “Share” requires developers to check in their changes, do a “Get Latest Version” on the shared files, rebuild the solution, make any necessary changes, repeat, etc. In the meantime, the build is potentially broken.

The Tutor.com Classroom: How-To tips from the field

These How-To tips are taken from The Tutor.com Classroom: Architecture and techniques using Silverlight, WPF, and the Microsoft .NET Framework.

You can check out the Silverlight Tutor.com Classroom in "practice" mode, although the real experience is with a live tutor on the other side!

Tutor.com’s online learning system, the Tutor.com Classroom, utilizes many of the advanced capabilities of the Microsoft .NET 3.5 platform, including TCP/IP sockets and WPF, in addition to making full use of the Silverlight browser plug-in. The next set of blog postings show solutions to some of the more interesting problems and how the potential of .NET was employed.