Showing posts with label Visual Tree. Show all posts
Showing posts with label Visual Tree. Show all posts

Monday, September 26, 2011

How to get Visual Tree using VisualTreeHelper class in WPF?


You might be aware about Visual Tree and Logical Tree in WPF. If you are new to WPF and don’t know about visual tree and logical tree please go through my post on Visual Tree vs. Logical Tree first.

In this post I will demonstrate how to get Visual Tree using VisualTreeHelper class. VisualTreeHelper class is very useful class to navigate through element’s children. It provides some methods to get child element, to get parent element, total children etc. First let’s have a look on below code snippet.

<Window.Resources>
    <Style TargetType="TreeViewItem">
        <Setter Property="IsExpanded" Value="True" />
    </Style>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.2*" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <TreeView Name="treeView" VerticalAlignment="Top"
                Grid.Row="1" HorizontalAlignment="Left"
                ScrollViewer.HorizontalScrollBarVisibility="Auto"
                ScrollViewer.VerticalScrollBarVisibility="Auto"
                Height="Auto" Width="Auto"/>
    <Button Name="getVisualTree" Content="Get Visual Tree"
            Height="35" Width="150"
            Click="getVisualTree_Click"/>
</Grid>

private void getVisualTree_Click(object sender, RoutedEventArgs e)
{
    treeView.Items.Clear();
    AddElementToTree(this, null);
}

public void AddElementToTree(DependencyObject parent, TreeViewItem treeViewItem)
{

    TreeViewItem newTreeViewItem = new TreeViewItem();

    newTreeViewItem.Header = parent.GetType().ToString();

    if (treeViewItem == null)
    {
        treeView.Items.Add(newTreeViewItem);
    }
    else
    {
        treeViewItem.Items.Add(newTreeViewItem);
    }

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var childElement = VisualTreeHelper.GetChild(parent, i);
        AddElementToTree(childElement, newTreeViewItem);
    }
}



In above example, I have added one treeview control to XAML and set its TreeViewItem’s style to Expanded by default. I also added one Button named getVisualTree and its onclick event i am adding elements to treeview control.

AddElementToTree(this, null);

This method will retrieve visual tree of given dependency object. In our example i have provided ‘this’ as parent element. This will retrieve visual tree of Window. You can specify other elements if you want to retrieve specific element’s visual tree.

public void AddElementToTree(DependencyObject parent, TreeViewItem treeViewItem)
{

    TreeViewItem newTreeViewItem = new TreeViewItem();

    newTreeViewItem.Header = parent.GetType().ToString();

    if (treeViewItem == null)
    {
        treeView.Items.Add(newTreeViewItem);
    }
    else
    {
        treeViewItem.Items.Add(newTreeViewItem);
    }

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var childElement = VisualTreeHelper.GetChild(parent, i);
        AddElementToTree(childElement, newTreeViewItem);
    }
}

This method takes two arguments first one is dependency object and another is treeview item. This method uses help of VisualTreeHelper class to get child.

VisualTreeHelper.GetChildrenCount(parent)

Above method returns total number of children of parent element. Below method return child element of parent at specified number.

VisualTreeHelper.GetChild(parent, i)

So the above for loop will get all the children of an element. This method is called recursively until all elements are added to the tree.


See also –

Thursday, August 11, 2011

Routed Events in WPF


WPF introduced new type of event called Routed Event. Routed events are designed to work inside trees of elements. Routed Events are used to navigate top or bottom through the Visual Tree based on navigation strategy. Check out my post on Logical Tree vs. Visual Tree and WPF class hierarchy to get idea about WPF control tree.

<Window x:Class="WpfApplication1.RoutedEventDemo"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RoutedEvents in WPF" Height="300" Width="300">
<Grid>
    <StackPanel>
        <Button Content="Click Me!" Height="35" Width="150"
                Margin="5" Click="Button_Click" />
    </StackPanel>
</Grid>
</Window>

private void Button_Click(object sender, RoutedEventArgs e)
{
}

Routed event handler provides two parameters one is object sender and another is RoutedEventArgs object. Let’s have a look on below table for more details.







Routing Strategies:
Bubbling


Event is raised from bottom control (leaf element) and navigates to top control (root element) called as bubbling event. Event bubbling can be stopped in between by setting e.handed = true (called as event handled). Bubbling events are written like normal event without prefix for e.g. MouseDown, MouseUp, MouseDoubleClick etc. Bubbling events are raised after Tunneling events.


Tunneling


Tunneling events are opposite to Bubbling events. Event is raised from top control (root element) and navigates to bottom control (leaf element). Event tunneling can be stopped in between by setting e.handed = true (called as event handled). Tunneling events are written with ‘Preview’ prefix for e.g. PreviewMouseDown, PreviewMouseUp, PreviewMouseDoubleClick etc. Tunneling events are raised before bubbling events.

Direct
Direct event is raised from the source element and must be handed on the source element. This is similar to normal CLR events.

How to create custom routed event?

The following example shows how to create and raise custom routed event.

XAML
<StackPanel>
    <Button Content="Click Me!" Height="35" Width="150"
            Margin="5" Click="Button_Click" />
</StackPanel>

Code behind
//Registering the routed event
public static readonly RoutedEvent PopupOpenedEvent = EventManager.RegisterRoutedEvent("PopupOpened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(RoutedEventDemo));

//Wrapper for Routed event
public event RoutedEventHandler PopupOpened
{
    add
    {
        AddHandler(PopupOpenedEvent, value);
    }
    remove
    {
        RemoveHandler(PopupOpenedEvent, value);
    }
}

//Raise the routed event from button click
private void Button_Click(object sender, RoutedEventArgs e)
{
    RaiseEvent(new RoutedEventArgs(RoutedEventDemo.PopupOpenedEvent));
}


//Subscribe routed event
this.PopupOpened += new RoutedEventHandler(RoutedEventDemo_PopupOpened);

void RoutedEventDemo_PopupOpened(object sender, RoutedEventArgs e)
{
    MessageBox.Show("PopupOpened routed event has been raised");
}


See also –





Sunday, May 15, 2011

Visual State Manager - New Features in WPF 4.0



Visual state manager introduced as new feature in WPF4 but the same feature was available in Silverlight since long time. In this post i explained some basics about Visual State Manager.


What is Visual State Manager in WPF?

Visual State Manager is used to change appearance of control based on Visual State. Visual States Manager can be defined inside Control Template. Visual State Manager manages different states of control. You can customize the appearance of control according to its visual state. Visual State Manager contains Visual State Groups and inside Visual State Groups you can add Visual States. Visual State contains Storyboard and can be used to animate control on state change. Triggers in WPF are also used to change the appearance of control but it changes appearance based on properties of control (for eg. IsMouseOver, IsDefault, IsEnabled etc.).

When you specify Visual State Manager in Control Template it uses specified Visual States to change appearance of control. You can also specify Transition duration while changing form one Visual State to another. You can easily apply visual states to any control using Microsoft Expression Blend.  

Visual Sates and Parts of Control

Visual State Manager supports parts (As per MSDN, Parts are named elements in Control Templates) and visual states model (As per MSDN, a visual state represents the appearance of the control under a given set of circumstances). 

Each control has its own states and all states are grouped into state groups. As per below figure, Button has two State Group one is Common States and another is Focus States. Now common states has four states Normal, MouseOver, Pressed, disabled and Focus State has two states unfocused and focused. So at a time button has only one state from each group.



Simple example using Visual State Manager

<Window x:Class="WpfApplication1.VisualStateManagerClass"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="VisualStateManager" Height="150" Width="150">
    <Window.Resources>
    <Style x:Key="MyButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="{x:Type Button}">
    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <VisualTransition GeneratedDuration="0:0:0.5"/>
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimationUsingKeyFrames
                            Storyboard.TargetProperty=
                              "(Shape.Fill).(SolidColorBrush.Color)"
                            Storyboard.TargetName="ellipse">
                            <EasingColorKeyFrame KeyTime="0"
                              Value="#FF20F50E"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Pressed">
                    <Storyboard>
                        <ColorAnimationUsingKeyFrames
                            Storyboard.TargetProperty=
                              "(Shape.Fill).(SolidColorBrush.Color)"
                            Storyboard.TargetName="ellipse">
                            <EasingColorKeyFrame KeyTime="0"
                              Value="#FFF5230E"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Disabled"/>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Ellipse x:Name="ellipse" Fill="#FF0E0EF5" Stroke="Black"/>
        <ContentPresenter
  HorizontalAlignment=
       "{TemplateBinding HorizontalContentAlignment}"
         RecognizesAccessKey="True"
         SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
         VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>
    </Window.Resources>
    <Grid>
        <Button Content="Click Me!"
                Style="{StaticResource MyButton}"
                Height="100" Width="100" />
    </Grid>
</Window>


Output

Normal State















MouseOver State















Pressed State















Hope, you like this post and have better understanding about Visual State Manager.


Please post your suggestion/queries/feedback in comments if any.