Try it here.
See a picture below: :)
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.