.NET Framework Developer Center > .NET Development Forums > Windows Presentation Foundation (WPF) > getting the position of treeview items RELATIVE TO the top of the parent treeview.
Ask a questionAsk a question
 

Answergetting the position of treeview items RELATIVE TO the top of the parent treeview.

  • Tuesday, May 01, 2007 8:30 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I'm writing a custom control where I must know the offset of treeview items from the parent treeview. I've tried to make the treeview items text, TextBlocks, and have used PointToScreen,   VisualTreeHelper.GetOffset, and adding up margin and height values, but absolutely all of these approaches gives me "zero." (I want the height offset primarily) 

     

    The reason I need this is that I am lining up more controls, on the other side of a grid splitter, with the leaves of my treeview.  I realize I could place these controls IN the treeview, but how would I make a grid splitter separate the treeview from those items that are shown/hidden by the treeview?  Is that even possible?

     

    Thanks,

    -Zom

Answers

  • Wednesday, May 02, 2007 2:11 PMlee dModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I tried the following  and seems to work

    Code Snippet

    <Canvas>

    <TreeView Name="tv1">

    <TreeViewItem Header="root">

    <TreeViewItem Header="child1"></TreeViewItem>

    <TreeViewItem Header="child2"></TreeViewItem>

    <TreeViewItem Header="child3">

    <TreeViewItem Margin="20" Header="child3.1"></TreeViewItem>

    <TreeViewItem Header="child3.2"></TreeViewItem>

    <TreeViewItem Header="child3.3"></TreeViewItem>

    </TreeViewItem>

    <TreeViewItem Header="child4"></TreeViewItem>

    </TreeViewItem>

    </TreeView>

    <TextBlock Text="some text" Name="txt1" Canvas.Left="200" Canvas.Top="200"></TextBlock>

    </Canvas>

     

    void tv1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

    {

    TreeViewItem tvi = e.NewValue as TreeViewItem;

    GeneralTransform myTransform = tvi.TransformToAncestor(tv1);

    Point myOffset = myTransform.Transform(new Point(0, 0));

    txt1.Text = myOffset.ToString();

    txt1.SetValue(Canvas.TopProperty, myOffset.Y);

    txt1.SetValue(Canvas.LeftProperty, myOffset.X + 150);

    }

     

  • Wednesday, May 02, 2007 2:58 PMLesterLobo - MSFTModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    if its the height of the treeviewitems that you are asking, then it should be possible using the actualheight property...

     

All Replies

  • Tuesday, May 01, 2007 9:22 PMDrew MarshModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    To answer your first question, the reason GetOffset is returning 0 is because that's relative to the immediate parent of the TextBlcok which is most certainly not the TreeView. Use Visual::TransformToAncestor instead and then calculate the offset as a Point... something like this:

     

    Code Snippet

    GeneralTransform myTransform = myTextBlock.TransformToAncestor(myTreeView);

     

    Point myOffset = myTransform.Transform(new Point(0, 0));

     

    // myOffset.Y contains your distance from the top of the treeview now

     

     To answer your second question, I can't try this out right now, but I think you should be able to pull this off by adding Grid.IsSharedSizeScope to your TreeView and then, in the template for your tree view items you define a grid giving each GridViewColumn a SharedSizeGroup name. The template also includes the splitter. Then because all the tree view items are within the scope of your treeview and share the same group names, when the splitter resizes the local grid's column it should cause all the columns to be resized.

     

    HTH,
    Drew

  • Wednesday, May 02, 2007 12:55 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Thanks for your reply,

     

    I tried your above suggestion, however I keep getting an exception which says: "The specified Visual is not an ancestor of this Visual."   The structure of my tree view is as follows:

     

    TreeView

        TreeViewItem

                TreeviewItem.Header = the text block I want the offset of.

     

    Apparently this header field does not make the tree view an ancestor of the text block. I'm not really sure why. Is there a way around this?

     

    Thanks,

    -Zom.

  • Wednesday, May 02, 2007 1:30 PMlee dModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    In which event are you trying to get the offset?
  • Wednesday, May 02, 2007 1:48 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Okay, here's a program where I try to use the above suggestion.  I thought my problem in my actual app was that I was making everything a treeview item, so in this example I tried adding textblocks to the Items collection of a treeviewitem, however this gave me the same problem. when this is run, I get "The specified Visual is not an ancestor of this Visual."

     

     

     

     

    using System;

    using System.Collections.Generic;

    using System.Text;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Data;

    using System.Windows.Documents;

    using System.Windows.Input;

    using System.Windows.Media;

    using System.Windows.Media.Imaging;

    using System.Windows.Shapes;

    using System.Diagnostics;

     

    namespace TreeViewblah

    {

    /// <summary>

    /// Interaction logic for Window1.xaml

    /// </summary>

    public partial class Window1 : System.Windows.Window

    {

    public Window1()

    {

    InitializeComponent();

    Loaded += new RoutedEventHandler(Window1_Loaded);

    }

    TreeView treeView;

    void Window1_Loaded(object sender, RoutedEventArgs e)

    {

    treeView = new TreeView();

    Content = treeView;

    TreeViewItem t = new TreeViewItem();

    t.Header = "stuff";

    TextBlock a = new TextBlock(); a.Text = "A";

    TextBlock b = new TextBlock(); b.Text = "B";

    TextBlock c = new TextBlock(); c.Text = "C";

     

    t.Items.Add(a);

    t.Items.Add(b);

    t.Items.Add(c);

    treeView.Items.Add(t);

    GeneralTransform myTransform = a.TransformToAncestor(treeView);

    Point myOffset = myTransform.Transform(new Point(0.0, 0.0));

    Debug.WriteLine(myOffset.Y.ToString());

    }

    }

    }

  • Wednesday, May 02, 2007 2:05 PMZhou Yong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Replace the offset calculation code with the following:
    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new System.Threading.ThreadStart(delegate
    {

    GeneralTransform myTransform = a.TransformToAncestor(treeView);

    Point myOffset = myTransform.Transform(new Point(0.0, 0.0));

    Debug.WriteLine(myOffset.Y.ToString());


    });

    Sheva
  • Wednesday, May 02, 2007 2:07 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Here's an actual example of what I'm trying to accomplish, in xaml.  I would like the grid splitter to separate the treeview from the ellipse item that appears when you click on the "propertyname" category.

     

    <Window
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="UntitledProject3.Window1"
     x:Name="Window"
     Title="Window1"
     Width="640" Height="480">

     <Grid x:Name="LayoutRoot">
      <Grid.ColumnDefinitions>
       <ColumnDefinition Width="0.291*"/>
       <ColumnDefinition Width="0.709*"/>
      </Grid.ColumnDefinitions>
      <TreeView Margin="0,0,72,30" Grid.ColumnSpan="2">
       <TreeViewItem Header="CategoryName" IsExpanded="True">
        <TreeViewItem Header="InstanceName" IsExpanded="True">
         <TreeViewItem Header="PropertyName">
          <Canvas Width="100">
           <Ellipse Fill="#FFFFFFFF" Stroke="#FF000000" Width="48" Height="16" Canvas.Left="233" Canvas.Top="22.17"/>
          </Canvas>
         </TreeViewItem>
        </TreeViewItem>
       </TreeViewItem>
      </TreeView>
      <GridSplitter Margin="0,0,0,30" Width="7.912"/>
     </Grid>
    </Window>

  • Wednesday, May 02, 2007 2:11 PMlee dModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I tried the following  and seems to work

    Code Snippet

    <Canvas>

    <TreeView Name="tv1">

    <TreeViewItem Header="root">

    <TreeViewItem Header="child1"></TreeViewItem>

    <TreeViewItem Header="child2"></TreeViewItem>

    <TreeViewItem Header="child3">

    <TreeViewItem Margin="20" Header="child3.1"></TreeViewItem>

    <TreeViewItem Header="child3.2"></TreeViewItem>

    <TreeViewItem Header="child3.3"></TreeViewItem>

    </TreeViewItem>

    <TreeViewItem Header="child4"></TreeViewItem>

    </TreeViewItem>

    </TreeView>

    <TextBlock Text="some text" Name="txt1" Canvas.Left="200" Canvas.Top="200"></TextBlock>

    </Canvas>

     

    void tv1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

    {

    TreeViewItem tvi = e.NewValue as TreeViewItem;

    GeneralTransform myTransform = tvi.TransformToAncestor(tv1);

    Point myOffset = myTransform.Transform(new Point(0, 0));

    txt1.Text = myOffset.ToString();

    txt1.SetValue(Canvas.TopProperty, myOffset.Y);

    txt1.SetValue(Canvas.LeftProperty, myOffset.X + 150);

    }

     

  • Wednesday, May 02, 2007 2:39 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    That works great! I just added a splitter, and this example achieves exactly what I need:

     

    Thanks so much.

     

    <Window
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     x:Class="UntitledProject5.Window1"
     x:Name="Window"
     Title="Window1"
     Width="640" Height="480">

     <Grid x:Name="LayoutRoot">
      <Grid.ColumnDefinitions>
       <ColumnDefinition Width="0.241*"/>
       <ColumnDefinition Width="0.759*"/>
      </Grid.ColumnDefinitions>
      <Canvas HorizontalAlignment="Right" Width="456" Grid.Column="1">
       <TextBlock Text="some text" Name="txt1" Canvas.Left="200" Canvas.Top="200"></TextBlock>
      </Canvas>
      <TreeView x:Name="tv1" Margin="0,0,8,0" SelectedItemChanged="tv1_SelectedItemChanged">
       <TreeViewItem Header="root">
        <TreeViewItem Header="child1"/>
        <TreeViewItem Header="child2"/>
        <TreeViewItem Header="child3">
         <TreeViewItem Margin="20" Header="child3.1"/>
         <TreeViewItem Header="child3.2"/>
         <TreeViewItem Header="child3.3"/>
        </TreeViewItem>
        <TreeViewItem Header="child4"/>
       </TreeViewItem>
      </TreeView>
      <GridSplitter Width="8" ShowsPreview="True"/> 
     </Grid>
    </Window>

  • Wednesday, May 02, 2007 2:53 PMZomCoder Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Applying what I've learned from this example, it appears that I cannot get the height of those items from the top of the treeview unless it is in the selected item changed event.  Why is that?   Note that my problem is solved, I just remain curious as to why I can't use that technique anywhere else but that SelectedItemChanged event.   thanks
  • Wednesday, May 02, 2007 2:58 PMLesterLobo - MSFTModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    if its the height of the treeviewitems that you are asking, then it should be possible using the actualheight property...

     

  • Thursday, May 03, 2007 1:16 AMDrew MarshModeratorUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    There must be some other issue with the timing your measurement, because Visual::TransformToAncestor is a much lower level API and applies to way more than just TreeView/TreeViewItem. It should work just about everywhere. If you can explain where it's not working for you maybe we can help you understand the problem.

     

    Cheers,
    Drew