0

In a WPF application, I've a ContentControl. In this ContentControl, I've a View.

Starting from a specific UserControl, I'm trying to go up in its logical tree, and "Activate" every component on the way. By example, if one parent is a Tab, I select it by example.

My issue is that when my UserControl is in a ContentControl, when I call the LogicalTreeHelper.GetParent(...) I receive null:

    private static void Activate(FrameworkElement frameworkElement)
    {
        //Here, in one iteration, I receive null when it's supposed to be the `ContentControl`.
        DependencyObject parent = LogicalTreeHelper.GetParent(frameworkElement); 
        if (parent is FrameworkElement parentFrameworkElement) 
        {
            Activate(parentFrameworkElement);
        }

        if (frameworkElement is DXTabItem tab)
        {
            tab.IsSelected = true;//If it's a  tab, it gets activated
        }

        frameworkElement.Focus();
    }

My Xaml is something like this:

<dx:DXTabControl AllowMerging="True" TabContentCacheMode="None" Margin="0,3,0,0">
    <dx:DXTabItem Header="Some channel">
        <local:SomeControl Channel="{Binding Channel}"/>
    </dx:DXTabItem>
    <dx:DXTabItem Header="Some other view">
        <ContentControl Content="{Binding Channel, Converter={StaticResource SomeModelToViewModelConverter}}" ContentTemplateSelector="{StaticResource ConventionBasedDataTemplateSelector}" />
    </dx:DXTabItem>
</dx:DXTabControl>

So: Any idea how to get the ContentControl from the control inside it?

Edit It seems to be related to the fact that the control is in an unselected tab(the goal of my feature IS to activate the tab in which a usercontrol bound to something is located).

J4N
  • 19,480
  • 39
  • 187
  • 340
  • Why dont you pass the parent object via constructor on child-object creation? – Jonas Aug 29 '19 at 11:15
  • @Jonas In XAML? Have you ever done that? – Clemens Aug 29 '19 at 11:17
  • 2
    How about VisualTreeHelper? – Clemens Aug 29 '19 at 11:17
  • @Jonas For a lot of reasons: I bind a model, which is Converted in a ViewModel, for which we have a ContentTemplateSelector, so I never create myself the View, it's the ContentTemplateSelector(which is a ConventionBased converter, that will take the name and remove the "Model" in it. – J4N Aug 29 '19 at 11:18
  • @Clemens Ahah; I just tried this now, and same result :'( – J4N Aug 29 '19 at 11:18
  • You have to wait until the control is completely loaded. E.g. use a `Loaded` event handler and start traversal from there. And like others have mentioned before use the `VisualTreeHelper` instead. – BionicCode Aug 29 '19 at 11:28
  • @J4N: When and how do you call `Activate`? – mm8 Aug 29 '19 at 11:30
  • @Clemens What is preventing him from creating an ViewModel for his parent & child. and use Binding for his properties? Maybe I am getting the question wrong, then I am sorry – Jonas Aug 29 '19 at 11:40
  • @mm8 It's called, guarantee, but it's quite complexe to explain. It comes from a command in a totally different view, which set a a value in a property of a Singleton, on which is bound an usercontrol attached property. When this value change, and that value is equals to a parameter, then it activate the control. – J4N Aug 29 '19 at 11:46
  • @BionicCode The Activate is not initiated from the same component, so I'm not aware it's initialized. but I can go on this view, wait 30 seconds, trigger the action, and same result, I don't think it's an initialization issue. – J4N Aug 29 '19 at 11:48
  • @Clemens In fact, I was wrong, it kinda works with the VisualTreeHelper, but the issue is that I don't find the parent I was expecting at some point(`DXTabItem`), so I cannot "Select it". Any idea? – J4N Aug 29 '19 at 12:06
  • From where do you start the traversal? Are you sure you are starting from a child element in the branch of `DXTabControl `? Maybe you start from a sibling control on the same level? – BionicCode Aug 29 '19 at 12:21
  • You can use the Live Visual Tree window to inspect the tree and verify the real structure. Just start a debug session and then go to _Debug/Windows/Live Visual Tree_ – BionicCode Aug 29 '19 at 12:26

1 Answers1

3

Provided that the elements have been loaded and added to the visual tree, you could use the following recursive method to find the parent element:

private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
    var parent = VisualTreeHelper.GetParent(dependencyObject);

    if (parent == null) return null;

    var parentT = parent as T;
    return parentT ?? FindParent<T>(parent);
}

Sample usage:

DXTabItem parent = FindParent<DXTabItem>(frameworkElement); 
mm8
  • 163,881
  • 10
  • 57
  • 88
  • I tested, it doesn't work, in my case: My control is in a TabControl, in a tab that is not currently displayed(the goal of all this is to display the tab that contains a specific control). – J4N Aug 29 '19 at 12:40
  • If the control isn't display, how are you supposed to find in the element tree...? Sorry, this makes no sense. – mm8 Aug 29 '19 at 12:41
  • Just wanted to ask the same question! – BionicCode Aug 29 '19 at 12:41
  • @J4N If the tab is not selected (displayed) it is removed from the visual tree. – BionicCode Aug 29 '19 at 12:42
  • @mm8 I've a tab control with 3 tabs, and it's in one of the 2 other tabs, so if you go from parent to child, you can find it. So why would I not find it from child to parent? – J4N Aug 29 '19 at 12:44
  • @J4N You should have provided this not quite unimportant information _"the goal of all this is to display the tab that contains a specific control"_ in your question. – BionicCode Aug 29 '19 at 12:44
  • @BionicCode Probably, i didn't think it was relevant at first – J4N Aug 29 '19 at 12:44
  • If `FindParent` returns `null`, there is no `DXTabItem` parent element in the visual tree. – mm8 Aug 29 '19 at 12:45
  • @J4N Use the Live Visual Tree Window and see it yourself: the `TabItem` that is not displayed has it's content removed from the visual tree. – BionicCode Aug 29 '19 at 12:47
  • @mm8 my question is not about visual tree, it's about how to get the parent of a ContentControl in not visible tab item. – J4N Aug 29 '19 at 12:48
  • @J4N: There is nothing to get a reference to in a not visible tab. – mm8 Aug 29 '19 at 12:48
  • @J4N You have to trigger on the `Loaded` event which will be raised once the tab is visible. – BionicCode Aug 29 '19 at 12:49
  • @BionicCode Let me explain better: This is used to edit a configuration for some hardware devices. Somewhere totally else, we have a list of error to fix(bit like visual studio), and when I double click, I need to display the exact pane where I can fix this error. The issue is that some "node" that I can edit have several tabs, and I need to display the correct one. But only each of those panes knows what they edit. So it means that I've some cases where I will NEVER go into the `Loaded` event before needing to know in which pane this is used. – J4N Aug 29 '19 at 13:32
  • @J4N: Instead of trying to explain your issue in the comments field, why don't you update your question with a [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) according to the guidelines for how to ask a question on SO? – mm8 Aug 29 '19 at 13:35
  • @J4N Maybe you are coming from the wrong direction. Each control that contains error data must register itself (report the error). Once registered you can directly access it. This means `DXTabItem` has to register itself as an error source e.g. `ErrorController.RegisterError(control, errorMessage)` then the `ErrorController` can activate the control that maps to the error. If you don't have direct access to the control's internals then create an Attached Behavior to handle this. – BionicCode Aug 29 '19 at 14:01
  • @J4N This is just a quick idea which is not the best. The solution depends on how you structured your UI. How do you display devices? How does `DXTabItem` come into play? But I think you need a new approach. It would be a good idea to update your question to provide more concrete details because your initial approach won't work for your scenario. Then we can find a better solution. – BionicCode Aug 29 '19 at 14:02