Solving development problems  |  About this blog

Archive for the ‘xaml’ tag

Silverlight: Equidistant items in a stack panel

This post describes how to align items in a container so the two neighbour items are always aligned with equal distance. Solution should be flexible, meaning that when items are changed or when container is resized items should keep equal distance in between. Reader should have some basic knowledge about C# and XAML for Silverlight.

Ideas

  • using Canvas and positioning items with absolute coordinates – items are always fixed, coordinates need to be calculated, so it is the least flexible approach, no
  • using StackPanel with items that have margin – margins need to be recalculated when an item is added or removed or when container is resized, maybe
  • using Grid with predefined rows and columns – notice predefined, means less flexibility, but offers equal size rows/columns, maybe
  • using a custom control – making Grid more dynamic but extending number of rows/columns when items are added, yes

Custom Control: EqualDistanceStackPanel

As written before, custom control should extend Grid to become more flexible

public class EqualDistanceStackPanel : Grid
{
}

 

EqualDistanceStackPanel should implement ItemsSource and ItemTemplate just like ItemsControl

#region ItemsSource
public IEnumerable ItemsSource
{
  get { return (IEnumerable)GetValue(ItemsSourceProperty); }
  set { SetValue(ItemsSourceProperty, value); }
}

public static readonly DependencyProperty ItemsSourceProperty =
  DependencyProperty.Register("ItemsSource", typeof(IEnumerable),
  typeof(EqualDistanceStackPanel), new PropertyMetadata(ItemsSourceChanged));

private static void ItemsSourceChanged(DependencyObject d,
  DependencyPropertyChangedEventArgs e)
{
  var panel = d as EqualDistanceStackPanel;
  if (panel != null)
  {
    if (e.OldValue != null)
    {
      panel.ClearItems();
    }
    if (e.NewValue != null)
    {
      panel.BindItems(e.NewValue as IEnumerable);
    }
  }
}
#endregion

public DataTemplate ItemTemplate
{
  get;
  set;
}

 

ClearItems method deletes all previous elements from the panel and BindItems attaches fresh controls from item template.

protected void ClearItems()
{
  this.Children.Clear();
}

protected void BindItems(IEnumerable items)
{
  if (this.ItemTemplate == null)
  {
    return;
  }

  //Create and attach children
  foreach (var item in items)
  {
    var element = this.ItemTemplate.LoadContent() as FrameworkElement;
    element.DataContext = item;
    this.Children.Add(element);
  }

  //Realign in container
  Refresh();
}

 

How should the custom control look in XAML? EqualDistanceStackPanel gets a collection of items, e.g. of UIElement type.

<Grid xmlns:c="clr-namespace:Avivo.Controls">
  <c:EqualDistanceStackPanel ItemsSource="{Binding Items}" Orientation="Vertical">
    <c:EqualDistanceStackPanel.ItemTemplate>
      <DataTemplate>
        <ContentPresenter Content="{Binding}" />
      </DataTemplate>
    </c:EqualDistanceStackPanel.ItemTemplate>
  </c:EqualDistanceStackPanel>
</Grid>

 

Example

Ruler has been made to demonstrate the usage of EqualDistanceStackPanel. Numbers are bound to the control and ticks are bound to another control using the same binding collection. Left image represents container height 300px and right when resized to 460px – elements are automatically aligned with equal distance.

Try demo, resize browser to see the effect. This control has been originally developed for custom chart axes in QR Code Statistics application. EqualDistanceStackPanel control is lightweight, works for the statistics application and should be improved for general usage.

Source code

  • Source code, Visual Studio sample Silverlight and web project

References

Written by developer

March 29th, 2011 at 2:00 pm

WriteableBitmap does not Render()?!

We spent quite some time working on a Silverlight deep zoom application. Like in other projects, a problem appears sooner or later. This time with the WriteableBitmap that couldn’t take screenshot of a basic shape.

//Simple ellipse (green circle)
Ellipse element = new Ellipse()
{
	Width = 50,
	Height = 50,
	Fill = new SolidColorBrush(Colors.Green)
};

//WriteableBitmap current = new WriteableBitmap(50, 50);
//current.Render(element, new MatrixTransform()); //DOES NOT WORK!!! - renders blank transparent image

//But with the constructor works fine
WriteableBitmap bitmap = new WriteableBitmap(element, new MatrixTransform());

//Ellipse captured as raster image painted on a panel's background
Grid grid = new Grid()
{
	Width = 50,
	Height = 50,
	Background = new ImageBrush() { ImageSource = bitmap }
};

Summary for the Render() method says: “Renders an element within the bitmap.”
Render() method didn’t output required result but using constructor instead of the method did do the job. Input parameters were the same.

Maybe Render() method do something else, maybe it is a problem with Vista 64-bit…

Anyway, WriteableBitmap is very useful in Silverlight 3, luckily it works.

Written by developer

December 9th, 2009 at 5:36 pm