Logo
programming4us
programming4us
programming4us
programming4us
Windows XP
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone
 
 
Windows Phone

Dependency Properties - Panels with Properties

3/20/2011 3:58:04 PM
The Windows Presentation Foundation has a panel I often find useful called UniformGrid. As the name suggests, the UniformGrid divides its area into cells, each of which has the same dimensions.

By default, UniformGrid automatically determines a number of rows and columns by taking the ceiling of the square root of the number of children. For example, if there are 20 children, UniformGrid calculates 5 rows and columns (even though it might make more sense to have 5 rows and 4 columns, or 4 rows and 5 columns). You can override this calculation by explicitly setting the Rows or Columns property of UniformGrid to a non-zero number.

Almost always, I find myself setting either Rows or Columns to 1, in effect making a single column or row of equally sized cells. This is not like a StackPanel that continues off the screen if it has too many children, but more like a single-column or single-row Grid where every RowDefinition or ColumnDefinition has a GridLength set to Star, and hence allocates the same space.

My version of UniformGrid is called UniformStack. It doesn’t have a Rows or Columns property but it does have an Orientation property—the same property defined by StackPanel—to indicate whether the children of the panel will be arranged vertically or horizontally.

Here’s the portion of the UniformStack class that defines the single dependency property and the property-changed handler:

Example 1. Silverlight Project: Petzold.Phone.Silverlight File: UniformStack.cs (excerpt)
public class UniformStack : Panel
{
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation",
typeof(Orientation),
typeof(UniformStack),
new PropertyMetadata(Orientation.Vertical, OnOrientationChanged));

public Orientation Orientation
{
set { SetValue(OrientationProperty, value); }
get { return (Orientation)GetValue(OrientationProperty); }
}

static void OnOrientationChanged(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
(obj as UniformStack).InvalidateMeasure();
}

. . .
}

The definitions of the dependency property and CLR property are straightforward. The property-changed handler casts the first argument to the class type, as usual, and then simply calls InvalidateMeasure. This is a method defined by UIElement, and it’s basically telling the layout system: “Whatever you think you know about how big I am, forget it. I’m a whole different size.” This call initiates the measure pass of layout from the root of the visual tree because the size of thid panel could affect parent classes. The measure pass is followed automatically by an arrange pass. (Layout passes are also initiated whenever the size of the panel changes, or when elements are added to or removed from the Children collection, or when an existing child changes size.)

There is also an InvalidateArrange method, which initiates just the second half of the layout process, but this is much rarer. Perhaps if you have a panel that dynamically moves its elements around without itself changing size you would have occasion to call InvalidateArrange.

The InvalidateMeasure method eventually causes a call to be made to MeasureOverride, and let’s think for a moment what needs to be done.

Consider a UniformStack with a horizontal orientation. Suppose the panel has five children, and the availableSize offered to the panel has a Width of 400 and a HeightWidth of 80 (1/5th the total available width) and a Height of 200. That’s the panel’s paradigm. of 200. Each child should be offered a size with a

But what if the Width property of availableSize is infinite? What should happen in that case?

Well, it’s not entirely clear. Certainly the panel has no choice but to offer to each child a Width of infinity. After that, one reasonable solution is to return a size from MeasureOverride with a Width that is five times the Width of the widest child.

That’s what I do here:

Example 2. Silverlight Project: Petzold.Phone.Silverlight File: UniformStack.cs (excerpt)
protected override Size MeasureOverride(Size availableSize)
{
if (Children.Count == 0)
return new Size();

Size availableChildSize = new Size();
Size maxChildSize = new Size();
Size compositeSize = new Size();

// Calculate an available size for each child
if (Orientation == Orientation.Horizontal)
availableChildSize = new Size(availableSize.Width / Children.Count,
availableSize.Height);
else
availableChildSize = new Size(availableSize.Width,
availableSize.Height / Children.Count);

// Enumerate the children, and find the widest width and the highest height
foreach (UIElement child in Children)
{
child.Measure(availableChildSize);
maxChildSize.Width = Math.Max(maxChildSize.Width, child.DesiredSize.Width);
maxChildSize.Height = Math.Max(maxChildSize.Height, child.DesiredSize.Height);
}
// Now determine a composite size that depends on infinite available width or
height
if (Orientation == Orientation.Horizontal)
{
if (Double.IsPositiveInfinity(availableSize.Width))
compositeSize = new Size(maxChildSize.Width * Children.Count,
maxChildSize.Height);
else
compositeSize = new Size(availableSize.Width, maxChildSize.Height);
}
else
{
if (Double.IsPositiveInfinity(availableSize.Height))
compositeSize = new Size(maxChildSize.Width,
maxChildSize.Height * Children.Count);
else
compositeSize = new Size(maxChildSize.Width, availableSize.Height);
}

return compositeSize;
}

The method begins by diving out if the panel has no children; this avoids division by zero later on.

An availableChildSize is calculated based on the Orientation property by ignoring the presence of infinity in the availableSize for the panel. (Infinity divided by the number of children will still be infinity, and that’s what’s required in that case.) The enumeration of the children calls Measure on each child with that availableChildSize. The logic involving the DesiredSize of the child also ignores infinite dimensions but instead accumulates a maxChildSize. This actually represents the width of the widest child and the height of the tallest child; it’s possible that no single child has the same dimensions as maxChildSize.

The final calculation of compositeSize takes into account both Orientation and the possibility of an infinite dimension. Notice that compositeSize is sometimes based on one of the availableSize dimensions; this is normally not proper but the method does it only when it knows that dimension is not infinite.

The ArrangeOverride method calls Arrange on each child with the same size (called finalChildSize in the method) but with different x and y positions relative to the panel depending on orientation:

Example 3. Silverlight Project: Petzold.Phone.Silverlight File: UniformStack.cs (excerpt)
protected override Size ArrangeOverride(Size finalSize)
{
if (Children.Count > 0)
{
Size finalChildSize = new Size();
double x = 0;
double y = 0;

if (Orientation == Orientation.Horizontal)
finalChildSize = new Size(finalSize.Width / Children.Count,
finalSize.Height);
else
finalChildSize = new Size(finalSize.Width,
finalSize.Height / Children.Count);

foreach (UIElement child in Children)
{
child.Arrange(new Rect(new Point(x, y), finalChildSize));

if (Orientation == Orientation.Horizontal)
x += finalChildSize.Width;
else
y += finalChildSize.Height;
}
}

return base.ArrangeOverride(finalSize);
}

Let’s use the UniformStack to make a bar chart!

The QuickBarChart program actually uses three UniformStack panels:

Example 4. Silverlight Project: QuickBarChart File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<petzold:UniformStack Orientation="Vertical">

<petzold:UniformStack x:Name="barChartPanel"
Orientation="Horizontal" />

<petzold:UniformStack Orientation="Horizontal">

<Button Content="Add 10 Items"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="OnButtonClick" />

<TextBlock Name="txtblk"
Text="0"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</petzold:UniformStack>
</petzold:UniformStack>
</Grid>

The first UniformStack with a Vertical orientation simply divides the content area into two equal areas. (See how much easier it is to use than a regular Grid?) The top half contains another UniformStack with nothing in it (yet). The bottom one contains a UniformStack with a Horizontal orientation for a Button and a TextBlock.

Clicking the Button causes the code-behind file to add 10 more Rectangle elements to the UniformStack named barChartPanel:

Example 5. Silverlight Project: QuickBarChart File: MainPage.xaml.cs (excerpt)
public partial class MainPage : PhoneApplicationPage
{
Random rand = new Random();

public MainPage()
{
InitializeComponent();
}

void OnButtonClick(object sender, RoutedEventArgs args)
{
for (int i = 0; i < 10; i++)
{
Rectangle rect = new Rectangle();
rect.Fill = this.Resources["PhoneAccentBrush"] as Brush;
rect.VerticalAlignment = VerticalAlignment.Bottom;
rect.Height = barChartPanel.ActualHeight * rand.NextDouble();
rect.Margin = new Thickness(0, 0, 0.5, 0);

barChartPanel.Children.Add(rect);
}

txtblk.Text = barChartPanel.Children.Count.ToString();
}
}

Notice that each Rectangle has a little half-pixel Margin on the right so there’s at least some spacing between the bars. Still, I think you’ll be surprised how many you can put in there before the display logic gives up:


Other -----------------
- Dependency Properties - A New Type of Toggle
- Dependency Properties - Deriving from UserControl
- Dependency Properties - The Dependency Property Difference
- Dependency Properties - The Problem Illustrated
- The App Bar and Controls - TextBox and Keyboard Input
- The App Bar and Controls - Buttons and Styles
- The App Bar and Controls - Toggling a Stopwatch
- The App Bar and Controls - The Button Hierarchy
- The App Bar and Controls - Theme Styles and Precedence
- The App Bar and Controls - The Concept of Content
 
 
Trailer game
Video tutorials
- How To Install Windows 8 On VMware Workstation 9

- How To Install Windows 8

- How To Install Windows Server 2012

- How To Disable Windows 8 Metro UI

- How To Change Account Picture In Windows 8

- How To Unlock Administrator Account in Windows 8

- How To Restart, Log Off And Shutdown Windows 8

- How To Login To Skype Using A Microsoft Account

- How To Enable Aero Glass Effect In Windows 8

- How To Disable Windows Update in Windows 8

- How To Disable Windows 8 Metro UI

- How To Add Widgets To Windows 8 Lock Screen
programming4us programming4us
Popular tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 windows Phone 7 windows Phone 8
programming4us programming4us
 
Popular keywords
HOW TO Swimlane in Visio Visio sort key Pen and Touch Creating groups in Windows Server Raid in Windows Server Exchange 2010 maintenance Exchange server mail enabled groups Debugging Tools Collaborating
programming4us programming4us
Trailer game
 
programming4us
Girls
programming4us
Windows Vista
programming4us
Windows 7
programming4us
Windows Azure
programming4us
Windows Server
programming4us
Windows Phone