When dynamically creating objects in code, use Styles

When you need to create objects in code, you can actually build up the entire look and feel in code.

This results in large blocks of code that do nothing special, but set the visual properties of an object:

Grid grdNow = new Grid();

 

//create Circle

Ellipse cirNow = new Ellipse();

cirNow.Height = 25;

cirNow.Width = 25;

cirNow.VerticalAlignment = VerticalAlignment .Center;

cirNow.HorizontalAlignment = HorizontalAlignment.Center;

cirNow.Fill = new SolidColorBrush (Colors.Red);

cirNow.Margin =new Thickness(5,5,5,5);

 

//create TextBlock

TextBlock txtNow = new TextBlock();

txtNow.VerticalAlignment = VerticalAlignment.Center;

txtNow.HorizontalAlignment = HorizontalAlignment.Center;

txtNow.Foreground = new SolidColorBrush(Colors.White);

txtNow.FontWeight = FontWeights.Bold;

txtNow.FontSize = 14;

txtNow.Margin =new Thickness(0,0,0,2);

txtNow.TextWrapping = TextWrapping.NoWrap;

txtNow.Text="now";

 

//add objects to grid

grdNow.Children.Add(cirNow);

grdNow.Children.Add(txtNow);

 

The drawback of this is that it is a lot of code and hard to maintain. The looks are integrated in the code and that is not what you want.

In Silverlight and WPF there is a standard model to separate the code from the look and feel of the application you’re building. It’s called XAML.

XAML is a markup language that’s easily generated by code and easily maintained by non-programmers with tools of even by hand.

When you are creating objects on the fly you should use Styles. These determine the way the object looks. Only keep the properties necessary for dynamically placing your object on the screen in your code:

Grid grdNow = new Grid();
Ellipse cirNow = new Ellipse();
TextBlock txtNow = new TextBlock();

cirNow.Style = App.Current.Resources["NowStyle"] as Style;
txtNow.Style = App.Current.Resources["NowTextStyle"] as Style;

grdNow.Children.Add(cirNow);
grdNow.Children.Add(txtNow);

This is a lot less code. Below is the markup that you shouldn’t have to worry about when you use Styles in your C# code.

You can even just create an empty Style block in App.xaml: <Style x:Key=”NowStyle” TargetType=”Ellipse” /> and thus use it with default values.

The look of these dynamically created objects can be changed later on or even be themed using a separate ResourceDictionary. A designer can come in at any time and change these properties using a Tool like Expression Blend.

<Style x:Key="NowStyle" TargetType="Ellipse">
    <Setter Property="Height" Value="25"/>
    <Setter Property="Width" Value="25"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="Fill" Value="Red"/>
    <Setter Property="Margin" Value="5"/>
</Style>
<Style x:Key="NowTextStyle" TargetType="TextBlock">
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="Foreground" Value="White"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="Margin" Value="0,0,0,2"/>
    <Setter Property="TextWrapping" Value="NoWrap"/>
    <Setter Property="Text" Value="nu"/>
</Style>

So, when you find yourself creating a lot of C# code to set the visual properties of a dynamically created objects: Stop!

Create a Style in XAML and refer to that Style instead.

Njoy!

Advertenties

Overlapping TabItems with the Silverlight Toolkit TabControl

OverlappingTabs

Standard TabItems are not overlapping, but designers like them to. You may encounter a design with overlapping tabs that you have to create and implement. This is not a simple task, certainly when the tabs have a tapered side that overlaps the tab to the right of it. The Z-Index of these tabs are opposite to the standard layout.

Control Vendors have custom versions of a TabControl, that use properties for overlap. The Toolkit TabControl currently doesn’t support overlapping tabs, but by updating templates and a bit of C# code you can get the same effect. Here’s how it’s done:

Creating an overlapping tab in Expression Design:

OverlappingTabsCreate01

  1. Start Design, start a new file with a size of 640 x 480 pixels.
  2. Add a rectangle, 50 pixels wide, 25 pixels high and make its CornerRadius 3 pixels. Make sure the Rectangle is selected.
  3. Choose Object. Convert Object to Path. This way you can change the vertices of the shape.
  4. Select the Direct Selection tool (second from the top in the Toolbox) and select the bottom right two vertices by dragging a selection area around them.
  5. Drag the two vertices to the right, about half the width of the rectangle. Hold the Shift key to constrain the translation horizontally.
  6. Zoom in to the right hand side of the shape. With the Pen Tool selected, at the right bottom corner delete the second vertice from the bottom by clicking on it.
  7. Hold the Alt Key and click the other vertice to make it a rounded point (fig. 1).
  8. Select the Direct Select Tool and move the second vertice from the top to the right. Select the Pen Tool, hold the Alt Key and drag from this anchor point in the direction of the line of the tab, so the sharp corner becomes smooth.
  9. Using the Direct Selection Tool, move the left vertice at the left bottom corner down to the bottom of the tab, delete the other by clicking on it with the Pen Tool selected (fig. 3).

OverlappingTabsCreate02

figure 1: Create a round point at the bottom right corner.

OverlappingTabsCreate03

figure 2: Create a smooth corner at the top right corner.

OverlappingTabsCreate04

figure 3: Remove a anchor point to create a straight corner at the lower left corner.

Double click the Magnifying Glass Icon at the bottom of the toolbox to show the entire drawing. It helps to place the top right corner of the tab at the 0,0 coordinates using the Action Bar at the bottom of the screen. Check if the Foreground color is white and the border is black. Make sure the tab is selected, showing its bounding box and transformation handles, and select Edit, Copy from the menu.

Implementing overlapping tabs in Expression Blend:

  1. Start Blend, Begin a new Silverlight project, Open MainPage.xaml.
  2. Open the Asset Panel, enter Tab in the search box, select TabControl and drag a rectangle on the Artboard. You’ll get a TabControl with two tabs. A reference to System.Windows.Controls.dll is added to the project references and a namespace xmlns:controls is added to your MainPage.xaml file.
  3. The TabControl is a container for the TabItems. It has a four Grids to place tabs on all sides of the tab area. Tabitems are placed in these grids. The TabItems consist of a Header and a Grid as you can see in the Object and Timeline Panel when a tab is selected. Right click a Tab and select Edit Template, Edit a Copy… from the context menu, choose a name and a location for the ControlTemplate and click OK.

OverlappingTabsImplement01

figure 4. Creating a TabItem ControlTemplate

  1. You’ll enter Template Editing Mode for the TabItem. The Objects and Timeline panel shows eight grids and a FocusVisualElement Border. For each of the four sides (Top, Bottom, Left, Right) you’ll see a Selected and a Unselected Part. Control Parts are recognizable by the green icon next to their name. You can also find them in the Parts Panel.
  2. Locate the TemplateTopSelected Grid in the Object and Timeline Panel and open all the borders and grids that are part of it by clicking the small triangles in from of it. Drag the ContentControl named HeaderTopSelected up to the TemplateTopSelected Grid so it moves to the same hierarchical level. Now you can delete the rest of the content of the TemplateTopSelected Grid. Also delete all the animations that reference those shapes if they are not deleted by Blend.
  3. With this Grid selected, choose Paste from the edit menu. This will place the tab you created in Expression Design into the grid. Remove its Margins to make the tab fit inside the Grid.
  4. Drag the layer of the new Path up so it is placed right beneath the TemplateTopSelected Grid and above the Focus and Disabled visuals in the Objects and Timeline Panel. Move the HeaderTopSelected Content Control beneath the Path.
  5. Repeat Step 3 and 4 for the TemplateTopUnselected Grid. Make its Foreground a slightly darker color than the selected state of the tab.
  6. Set for both the TemplateTopSelected as the TemplateTopUnselected Grid the left margin to –20 pixels. This will ruin the bounding box placing in Blend, but move the tabs to the left at runtime.
  7. Make sure the Grid for the selected state of the tab has a Path at the bottom, that makes the connection with the tab area beneath it. It is important that a tab connects to the area that shows when a tab is selected.
  8. You may want to delete the FocusVisualTop border because it is rectangular and your tabs are not. Also remove any animations targeting this control.
  9. Scope up to the Control Level using the breadcrumbs at the top of the screen or the Return Scope to [UserControl] button at the top of the Objects and Timeline Panel.
  10. Right Click the TabControl and Select Edit Additional Templates, Edit Layout of Items [ItemPanel], Create New. Make sure it is a StackPanel with its Orientation set to Horizontal. Scope up to the Control Level.
  11. Right Click the TabControl (not a tab) and Select Edit Template, Edit a Copy… Give the System_Windows_Controls_Primitives:TabPanel a left margin of 20, but leave the other margins as they are: margin=”20,2,2,-1″ compensating for the negative left margin on the first TabItem.
  12. Use the Visual State Manager to create a state for a MouseOver that is a light gray between the selected and unselected Foreground colors. A transition time of half a second gives the right effect.

        <controls:TabControl x:Name=”TabControl” Width=”500″ Height=”100″
             Style=”{StaticResource TabControlStyle1}”
             ItemsPanel=”{StaticResource ItemsPanelTemplate1}”
             SelectionChanged=”TabControl_SelectionChanged”>
            <controls:TabItem x:Name=”Tab_1″ Header=”Tab 1″
                 Style=”{StaticResource TabItemStyle1}”>
                <Grid Background=”#FFE5E5E5″>
                    <TextBlock Text=”Area 1″ Margin=”20″/>
                </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_2″ Header=”Tab 2″ 
                Style=”{StaticResource TabItemStyle1}”> 
                <Grid Background=”#FFE5E5E5″>
                    <TextBlock Text=”Area 2″ Margin=”20″/>
                </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_3″ Header=”Tab 3″
                 Style=”{StaticResource TabItemStyle1}”>
                 <Grid Background=”#FFE5E5E5″>
                      <TextBlock Text=”Area 3″ Margin=”20″/>
                 </Grid>
            </controls:TabItem>
            <controls:TabItem x:Name=”Tab_4″ Header=”Tab 4″
                 Style=”{StaticResource TabItemStyle1}”>
                 <Grid Background=”#FFE5E5E5″>
                     <TextBlock Text=”Area 4″ Margin=”20″/>
                 </Grid>
            </controls:TabItem>
        </controls:TabControl>

Getting the ZIndex right

All this will create the right shape and states of the the TabControl, but the order of the tabs is still wrong. By default the first tab is selected and a right tab will overlap a left tab. With overlapping tabs this should be the other way around. Setting the Canvas.ZIndex property of the TabItems in XAML won’t help. The control is initialized with the wrong settings at runtime.

A little C# helps. Thanks to Rachel, who solved this problem in WPF, we can set the overlapping tabs in the right order when the SelectionChanged event of the TabControl is fired:

private void TabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)

    TabControl tabControl = sender as TabControl; 
    tabControl.Dispatcher.BeginInvoke( new Action(() =>
            UpdateZIndex(sender as TabControl)));
}

private void UpdateZIndex(TabControl tc)

    if (tc != null)
    {
        foreach (TabItem tabItem in tc.Items)
        {
            tabItem.SetValue(
                        Canvas.ZIndexProperty,
                        (tabItem == tc.SelectedItem ?
                        tc.Items.Count :
                        (tc.Items.Count-1) – tc.Items.IndexOf(tabItem)));
        }
    }
}

The UpdateZIndex method is invoked in a way that updates all the TabItems. This way a left tab will show above a right tab the way you want in overlapping TabItems.

OverlappingTabs

fig 5. Overlapping tabs have a reversed ZIndex.

Working code is at my SkyDrive

Joe Gershgorin has refactored the code to use a Behavior. This also supp0rts longer text in the headers. You can find his version at: http://www.bitspy.com/OverlappingTabs_Silverlight4.zip

Njoy!

Accessing Properties and Objects in Silverlight Datatemplates using Loaded and FindName

Accessing properties and objects in a DataTemplate is not as easy as you would think. Normally the Template is separated from the item that uses it. You can find the DataTemplate as a Resource, but you cannot use the VisualTreehelper on it.

DataTemplate dt = Application.Current.Resources[“TheDataTemplate”] as DataTemplate;
int count = VisualTreeHelper.GetChildrenCount(dt);

This leads to the error: Reference is not a valid visual DependencyObject.

The solution is to use the Loaded event of the outermost container, quite often a Grid and use FindName in the EventHandler to get to Objects inside the DataTemplate and to properties of those:

<DataTemplate x:Key=”TheDataTemplate”>
    <Grid x:Name=”TheGrid” Grid.Row=”0″ Loaded=”TheGrid_Loaded”>
        //Objects and properties are here…
    </Grid>
</DataTemplate>

//enabling buttons in DataTemplate when Oob en Elevated…
private void TheGrid_Loaded(object sender, RoutedEventArgs e)
{
    if (Application.Current.IsRunningOutOfBrowser)
    {
        if (Application.Current.HasElevatedPermissions)
        {
            Grid grd = sender as Grid;

            if (grd != null)
            {
                Button c = grd.FindName(“btnCall”) as Button;
                c.IsEnabled = true;
                Button m = grd.FindName(“btnMail”) as Button;
                m.IsEnabled = true;
                Button p = grd.FindName(“btnCopy”) as Button;
                p.IsEnabled = true;
                Button v = grd.FindName(“btnVCard”) as Button;
                v.IsEnabled = true;

            }
        }
    }
}

Hope this helps! Njoy!

Visual Studio 2008 Design View uitzetten

Omdat ik toch in Blend werk en VS2008 uitsluitend gebruikt voor het Rebuilden van de Solution, heb ik de Design View niet echt nodig. Ik wissel altijd de Panels om en klik de Design View weg. Sterker nog, het stoort erorm als ik even  een XAML file wil openen in VS2008, omdat de XAML code er meteen staat, maar het programma toch de tijd neemt om de XAML te redeneren in een Design View die buiten beeld staat. Ik heb even geklaagt en daarna toch nog even beter gezocht, maar je kan de Design View toch uitzetten. De omschrijving van de optie in VS2008 is een beetje cryptisch, maar met deze afbeelding zou je het zelf moeten kunnen uitvoeren:

 Remove Design View voor XAML in Visual Studio 2008

Succes!