none
Get PARENT ContentPresenter from a ContentControl

    Question

  •  <Grid>
        <ContentPresenter Name="MyContent">
              <ContentPresenter.Content>
                  <Button Width="155" Height="20" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="25" Click="Button_Click">Click Me</Button>
               </ContentPresenter.Content>
           </ContentPresenter>
    </Grid>

    Is there a way to get The ContentPresenter from the ContentControl (the button) programmatically.  VisualTreeHelper.GetParent and other GetParent Methods always return null.

    Thanks,

    Saturday, August 18, 2012 7:52 AM

Answers

  • As I said before i'm working on a kind of spy++ (snoop) for WPF.  When a user select a control I need to get all parent controls of the object in order to "identify" it in the GUI Structure of the application. In other words I need to get all the parents of the element to create a kind of recognition path.  the snooped application is not mine and can be any application that use good or wrong approach. 

    Problem WAS :

    When you place a control in a contentPresenter inside an unselected tabPAge TabControl it's only time the visualhelper return no parent for for control.  You can get the child of the contentPresenter with MyContentPresenter.Content but nothing seems to exists to get the contentPresenter used by the control. (I know it's an extremely specific and strange need but we need to know where the control is used in the visualTree for our internal methods)

    ** Problem was fixed ** by creating a method that navigate forward from parent to child , from top level control to the contentPresenter and then look for a "contentPresenter.Content == to my orphan child". Not elegant but it seems that no other workaround exists

    Thanks again :)

    • Marked as answer by MikeQA Tuesday, August 21, 2012 5:07 AM
    Tuesday, August 21, 2012 5:06 AM

All replies

  • This is a possible solution: access 'Button as sender'.Tag property in Click event

    <Grid>
            <ContentPresenter Name="MyContent">
                <ContentPresenter.Content>
                    <Button Width="155" Height="20" 
                            VerticalAlignment="Top" HorizontalAlignment="Left" Margin="25" 
                            Click="Button_Click" Tag="{Binding ElementName=MyContent}">Click Me</Button>
                </ContentPresenter.Content>
            </ContentPresenter>
        </Grid>

    Saturday, August 18, 2012 10:18 AM
  • Hi,

    maybe you don't use the VTH correct (some invalid "as" casting?). I tried your code and the parent from this code

    private void Button_Click(object sender, RoutedEventArgs e)
    {
       Button button = sender as Button;
       DependencyObject visualParent = VisualTreeHelper.GetParent(button);
    }

    is the ContentPresenter with the Name "MyContent". The LogicalTreeHelper.GetParent indeed returns null.

    Cheers
    Jürgen

    Saturday, August 18, 2012 2:38 PM
  • Thanks for the help, at least I can underrstand the problem better now :

    The parent is null WHEN the object is inside a tabcontrol (not in selected tab)  Until you navigate to the right tab page, GetParent will return null.

    Ex:

    (XAML)

    <Window x:Class="WPFControlPresenterTest1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="188" Width="428">
        <Grid>
            <TabControl Margin="12,22,160,22" Name="tabControl1">
                <TabItem Header="Tab1" Name="tabItem1" IsEnabled="True">
                    <Grid />
                </TabItem>
                <TabItem Header="Tab2">
                    <Grid>
                        <ContentPresenter Name="MyContent">
                            <ContentPresenter.Content>
                                <Button  Name="b1" Width="155" Height="20" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="25">blabla</Button>
                            </ContentPresenter.Content>
                        </ContentPresenter>
                    </Grid>
                </TabItem> 
            </TabControl>
            <Button Height="23" HorizontalAlignment="Right" Margin="0,73.44,36,0" Name="button1" VerticalAlignment="Top" Width="65" Click="button1_Click">GetParent</Button>
        </Grid>
    </Window>

    .cs :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    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.Navigation;
    using System.Windows.Shapes;
    
    namespace WPFControlPresenterTest1
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
            }
            private void button1_Click(object sender, RoutedEventArgs e)
            {
               DependencyObject dobj= VisualTreeHelper.GetParent(b1);
               if (dobj == null)
                   MessageBox.Show("Parent is null");
               else
                   MessageBox.Show(dobj.ToString());
            }
        }
    }

    Is there a way to "Force" the update of the tree....just like tabcontrol does when selecting the proper tab page.  Note that once you click the second tab it will always return the parent
    • Edited by MikeQA Sunday, August 19, 2012 2:35 AM
    Sunday, August 19, 2012 2:32 AM
  • Hi,

    sorry, but you are a bit unspecific of what you really need.

    In your first post, the ContentPresenter is available, when the VisualTreeHelper is executed, as the Button used to call the method is available only, when the user has selected the TabItem.

    In your last post, you don't need the VTH as you can address the ContentPresenter by this.MyContent - though it still will be null as long as the TabItem is not selected.

    Additionally: what are you doing with a ContentPresenter in a Grid???

    So, if you want your problem solved, show it here (if it's too large, show a relevant abstraction) but not things, that can be solved, but have nothing to do with your problem.

    Cheers
    Jürgen

    Sunday, August 19, 2012 10:54 AM
  • I didnt even know that the problem was releated to current selected Tab TabControl...that's why I was not so specific about the problem.  Here is some addition information :

    I'm working on a kind of snoop for WPF.  The application with the ContentPresenter is not mine it's any possible WPF application, My code is "injected" in an existing application.  My demo application was just to show the problem :

    PROBLEM : The parent contentPresenter IS NOT available in VisualTree until you select the right tab page. (see previous code) 

    QUESTION : Is there a way to FORCE the "parenting update" of contentPresenter and its content?  (Just like when selecting the right tab) 

    Thanks


    • Edited by MikeQA Sunday, August 19, 2012 8:10 PM
    Sunday, August 19, 2012 8:06 PM
  • Hi Mike,

    So this is the issue with the Visual Tree. Only the selected tab creates it's visual tree, other tabs are torn down when unselected. If you have something like a ViewModel or business object behind the tab, residing in code, that will still be operating. For example I have written several a server admin tools that monitor and manage multiple servers, one per tab.

    The answer to your specific question has been asked before, here is the answer from one of our resident moderators Sheldon _Xiao, regarding LoadContent

    http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/bf691a3d-c77e-4faa-b5c5-0e6a4f0f559f/

     

    I would like to point out one more thing though. WPF controls are generally just the visual representation of data that is already available in the code-behind or ViewModel. In your example, I would bind ContentPresenter.Content to a property in code that I can change code-side on the click of the button.

    <Window x:Class="WpfApplication64.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="370*" />
                <ColumnDefinition Width="133*" />
            </Grid.ColumnDefinitions>
            <TabControl Margin="12,22,27,22" Name="tabControl1">
                <TabItem Header="Tab1" Name="tabItem1" IsEnabled="True">
                    <Grid />
                </TabItem>
                <TabItem Header="Tab2">
                    <Grid>
                        <ContentPresenter Name="MyContent" Content="{Binding MyContentItem}" />
                    </Grid>
                </TabItem>
            </TabControl>
            <Button Margin="0,20,0,0" HorizontalAlignment="Center" Name="button1" VerticalAlignment="Top" Width="90" Click="button1_Click" Grid.Column="1">Load Button2</Button>
        </Grid>
    
    </Window>
    

     

    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApplication64
    {
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
            UIElement _MyContent;
            public UIElement MyContentItem
            {
                get
                {
                    return _MyContent;
                }
                set
                {
                    if (_MyContent != value)
                    {
                        _MyContent = value;
                        RaisePropertyChanged("MyContentItem");
                    }
                }
            }
    
            public MainWindow()
            {
                InitializeComponent();
                MyContentItem = new Button { Content = "Button1" };
                DataContext = this;
            }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                MyContentItem = new Button { Content = "Button2" };
            }
    
            void RaisePropertyChanged(string prop)
            {
                if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
            }
            public event PropertyChangedEventHandler PropertyChanged;
    
        }
    }
    

    What I'm saying is that you should not usually need to navigate visual trees to perform actions in your application. Design changes can easily break it.

    If this is the kind of thing you are trying to do, may I recommend you have a look over this MSDN Samples project:

    http://code.msdn.microsoft.com/How-to-Build-Manage-and-fdd0074a

    Which has a TNWiki companion article here:

    http://social.technet.microsoft.com/wiki/contents/articles/12927.how-to-build-manage-and-navigate-the-user-interface-of-a-wpf-application.aspx

    The last example is a TabControl doing exactly this.

     

    I hope that answers your question, or is at least helpful :)

     

    Regards,
    Pete


    #PEJL

    Sunday, August 19, 2012 11:25 PM
  • Hi,

    @Pete: Are you really suggesting to put a Control into the business object??? I think inserting a User- or CustomControl into the Grid (or maybe even the ContentPresenter) and setting it's DataContext to the desired business object is more WPF-like.

    @Mike: Sorry, if I sounded offending. But from Pete's answer - which apart from my mentioned concern I regard as completely right - you can see, that your problem is not the unselected TabItem, but the chosen approach, which has to be corrected. That's why I asked for the real problem.

    Cheers
    Jürgen

    Monday, August 20, 2012 6:53 AM
  • As I said before i'm working on a kind of spy++ (snoop) for WPF.  When a user select a control I need to get all parent controls of the object in order to "identify" it in the GUI Structure of the application. In other words I need to get all the parents of the element to create a kind of recognition path.  the snooped application is not mine and can be any application that use good or wrong approach. 

    Problem WAS :

    When you place a control in a contentPresenter inside an unselected tabPAge TabControl it's only time the visualhelper return no parent for for control.  You can get the child of the contentPresenter with MyContentPresenter.Content but nothing seems to exists to get the contentPresenter used by the control. (I know it's an extremely specific and strange need but we need to know where the control is used in the visualTree for our internal methods)

    ** Problem was fixed ** by creating a method that navigate forward from parent to child , from top level control to the contentPresenter and then look for a "contentPresenter.Content == to my orphan child". Not elegant but it seems that no other workaround exists

    Thanks again :)

    • Marked as answer by MikeQA Tuesday, August 21, 2012 5:07 AM
    Tuesday, August 21, 2012 5:06 AM