Oh Silverlight 2.0 Console (Example and Source code)

Try it here.

See a picture below: :)

image

I ran into one odd issue when using Silverlight 2.0 Beta 2….

 

void Page_Loaded(object sender, RoutedEventArgs e)
{
    // if you don't invoke this asynchronously, you can't add at load time
    // (seems to be a silverlight 2.0 issue)
    Dispatcher.BeginInvoke(delegate()
    {
        consoleCanvas.AnimateNewChild(
                string.Format("Welcome!\nThe time right now is {0}.", 
                DateTime.Now.ToLocalTime()));
        consoleCanvas.AnimateNewChild("\n\nThanks for stopping by WiredPrairie.us. "
            + "This code may be used for only for good, not evil.");
    });
}

The code above for example — if the ConsoleCanvas control (the simple base control for the console output-like functionality) performs any UpdateLayout calls within the context of the Load event, the entire Silverlight control fails to load, silently. The only work-around I could discover was to delay what I wanted to occur by using the asynchronous invoke method on the Dispatcher object.

The easiest way to force a UIElement to measure itself is to use a little trick:

_holder.Width = this.ActualWidth;
child.Width = this.ActualWidth;
_holder.Children.Add(child);
_holder.UpdateLayout();

child.Width = child.DesiredSize.Width;
child.Height = child.DesiredSize.Height;

The console control maintains a visible (yet opacity is set to 0.0) StackPanel. Each time a new line of output is added via the AnimateNewChild method, it’s placed in the StackPanel. The StackPanel (_holder) is resized to fit the current width of the entire ConsoleCavnas control. Next, the UpdateLayout method of the StackPanel is called — which forces an immediate measure of all of the children (and thus forces any children of the TextPanel to potentially re-layout). The width and height are grabbed and permanently stored as the new size for the child.

Instead of using Storyboards and various animations, I found it more convenient to simply use a DispatchTimer and do the animations manually.

List<FrameworkElement> remove = new List<FrameworkElement>();

for(int childIndex = 0; childIndex < this.Children.Count; childIndex++)
{
    FrameworkElement child = this.Children[childIndex] as FrameworkElement;

    if (!child.Equals(_holder))
    {                        
        child.Arrange(new Rect(0, Canvas.GetTop(child), 0, 0));
        double top = Canvas.GetTop(child);
        top -= 2.0;
        Canvas.SetTop(child, top);                       
        if (top + child.Height < 0.0)
        {
            remove.Add(child);
        }
    }    
}
// remove them all 
remove.ForEach(fe => this.Children.Remove(fe as UIElement));                

If an element is no longer visible on the screen, it is removed using a Lambda expression and the ForEach method on the List object instance named remove (after the repositioning loop has completed).

Download source, etc. here.