-1

I know this question has been asked countless number of times, but I don't understand half of what their problem is or how to follow their example. The one I did find her name Rachel, posted a blog about it but her explanation was too brief..

http://rachel53461.wordpress.com/2011/07/17/wpf-error-specified-element-is-already-the-logical-child-of-another-element-disconnect-it-first/

Here was what I had before trying to follow here example:

<Window x:Class="Graph.View.MainView.Main"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:lc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
    Title="Main" Height="350" Width="525" 

    xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking">

<DockPanel LastChildFill="True">
       <DockPanel>
        <Label  Content="{Binding ScreenContent}" Grid.Row="1" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"/>
        </DockPanel>
    </DockPanel>

Because ScreenContent is having a logical parent I can't reuse it. After trying to follow Rachel's example:

<Window x:Class="Graph.View.MainView.Main"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:lc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol"
        Title="Main" Height="350" Width="525" 
       >
    <Window.Resources>
        <Style     TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate"> 
                <Setter.Value>
                    <DataTemplate>
                        <DockPanel>
                            <Label   Content="{Binding ScreenContent}" Grid.Row="1" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"/>
                        </DockPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <DockPanel LastChildFill="True">            
        <Label Style="{StaticResource MyCustomContentControl}"/>
    </DockPanel>
</Window>

How can I fix it? It isn't showing anything at all... Thanks.

akjoshi
  • 15,374
  • 13
  • 103
  • 121
Lews Therin
  • 10,907
  • 4
  • 48
  • 72

3 Answers3

1

Please answer me what is ScreenContent property ... is it string? or some GUI element?

If it is a String then the follow the soltuion below ...

Also there are a few concepts that you must understand...

  1. Label inside Label is a bad UI design.
  2. ContentControls such as Label, Button etc. follow a particular XAML pattern when we want to set their ControlTemplate and DataTemplate.
  3. In your case you want to assign a data context based property ScreenContent. So the DataTemplate way is correct.

However this will not flow to the Label inside your DataTemplate as the outer Label (to which you have applied the Style to) isnt set on its own Content property.

So basically a ContentControl.ContentTemplate works ONLY if a not null Content is set on that ContentControl and inside that DataTemplate (ContentTemplate) the Content works as the DataContext for all the items!

Notice the {Binding} expression below... It is self explanatory!!

    <DockPanel LastChildFill="True">
        <DockPanel.Resources>
            <Style x:Key="MyCustomContentControl"
                   TargetType="{x:Type ContentControl}">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <DockPanel>
                                    <ContentPresenter
                                             Content="{Binding}"
                                             HorizontalAlignment="Stretch"
                                             VerticalAlignment="Stretch"/>
                                </DockPanel>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DockPanel.Resources>
        <Label Style="{StaticResource MyCustomContentControl}"
               Content="{Binding ScreenContent}" 
               HorizontalContentAlignment="Stretch"
               VerticalContentAlignment="Stretch">
        </Label>
    </DockPanel>

If it is a UserControl follow the solution below...

Are you assigning it only once (to the given Label)? or you are attempting to assign it as content of such various elements?

If its assigned ONLY once, then the solution above should work.

But if you are planing to assign it as a Content of multiple target elements then you need to follow the Model / ViewModel approach.

  1. All your GUI elements should be hosted in XAML.
  2. Code Behind should not deal with GUI elements.
  3. Code behind works only on the soft copy of the GUI elements i.e. a data specific to fields that GUI (UserControl) represents.

E.g.

Example 1...

Suppose you have a TitleControl which is like a bold, italic WPF Label showing the text of a title. Your DataContext has such TitleControl object held which has some title text set to it.

Then in the ViewModel approach, you should create an instance of TitleControlViewModel as part of your DataContext... something like this...

YourDataContext.MyTitleControlViewModel = new TitleControlViewModel();

Inside the TitleControlViewModel class we should have a public property called MyTitle of type string.

YourDataContext.MyTitleControlViewModel.MyTitle = "My Title";

Now your template specific XAML should look like this...

    <DataTemplate x:Key="MyTitleControlDataTemplate">
         <local:TitleControl Title="{Binding MyTitle}"/>
    </DataTemplate>

and your host panel should look like this...

    <ContentControl ToolTip="Title is shown here..." 
                    Content="{Binding MyTitleControlViewModel}"
                    Contenttemplate="{StatiocResource MyTitleControlDataTemplate}"/>

    <ContentControl ToolTip="Same title control is shown here also !!!"
                    Content="{Binding MyTitleControlViewModel}"
                    Contenttemplate="{StatiocResource MyTitleControlDataTemplate}"/>

So this way same title control seems to be hosted on the tow content controls above. But if you think really, there are two different instances of TitleControl who are merely representing same MyTitleControlViewModel thus looking like a same control!

Google for how Data Templates, MVVM fit together.

WPF-it
  • 19,625
  • 8
  • 55
  • 71
  • That didn't work :( ScreenContent is a UserControl. I assigned it to a Label content which I think is OK to do but of course I can't reuse that UserControl(screen) anymore. I thought this method would have worked! – Lews Therin Jun 27 '12 at 09:32
  • 1
    Yeah but both scenarios are different. But let's say you have a tabbed Usercontrol. In the main tab you have an EmployeeUserControl instance that is fine as long as you don't have another EmployeeUserControl instance in a second tab, in fact it won't let you create a new EmployeeUserControl to act as a second tab. I think I know what the problem is, but no idea how to solve it. I basically copied part of the code that makes a screen into a SubScreenView. I removed the bindings since it would display static data. But I decided on a whim to reuse the ViewModel I had. – Lews Therin Jun 27 '12 at 10:32
  • When I used the ViewModel it threw that same exception even though SubScreenView doesn't have any logical parent. So the ViewModel here is the cause, why? I don't know. – Lews Therin Jun 27 '12 at 10:32
  • 1
    I think you are doing something fundamentally wrong! `ViewModel` and `ContentControls` with `DataTemplate` are provided for exactly the same reason, to display similar controls in mutliple instances. Can you provide your source code? – WPF-it Jun 27 '12 at 11:21
  • @LewsTherin You have tagged this question with devexpress! are you using any devexpress control? how? – akjoshi Jun 27 '12 at 11:38
  • I can't right now, but when I get home I will paste the link here. For the sake of it I assume you are right. So what I have done is in the ViewModel I have created an ObservationCollection of UserControls. That way I can make as many screens (usercontrols) as I want. The first element being the main screen, and the other elements, sub screens. How does that sound? – Lews Therin Jun 27 '12 at 11:43
  • @akjoshi Yeah I am. I am using DockLayoutManager for the screens – Lews Therin Jun 27 '12 at 11:49
  • @Lews, I still think you need revise your design... holding array of usercontrols is always a heavy option on memory and performance... idea in MVVM is to have UserControls, Windows, GUI elemnts to be solely and purely represented in View layer and VM merely holds all the field data for it. – WPF-it Jun 27 '12 at 12:13
  • I have no choice about creating those new screens. So either way I will have to use memory. This is a big PITA to say the least lol. – Lews Therin Jun 27 '12 at 12:32
0

In case you want to reuse ScreenContent (although its already a child of some other element) then you will have to first clone it and use the cloned control.

You can clone a control by first serializing it using XamlWriter and then create a new control by deserializing it using XamlReader, something like this -

string screenContentXml = XamlWriter.Save(ScreenContent );

//Load it into a new object:
StringReader stringReader = new StringReader(screenContentXml );
XmlReader xmlReader = XmlReader.Create(stringReader);
UIElement screenContentClone = (UIElement)XamlReader.Load(xmlReader);

use this screenContentClone in your window.

But, you may find yourself applying workarounds to make it work as there are some limitations in using XamlWriter.Save (like with bindings) - Serialization Limitations of XamlWriter.Save

Here are some other approaches for serialization -

An XAML Serializer Preserving Bindings

XamlWriter and Bindings Serialization

Although your design doesn't look correct as AngelWPF mentiond and you should try and re-design your window, and you might not require doing all this.

Community
  • 1
  • 1
akjoshi
  • 15,374
  • 13
  • 103
  • 121
  • The easier would be to use another UserControl. I was hoping to reuse the View, so I can make little screens whenever I want. This limitation sucks. Thanks anyways. – Lews Therin Jun 27 '12 at 09:53
  • I don't think there is any limitation here! You will have to use the features in correct way, how they are supposed to be used else you will always face limitations. If you just want use the view multiple times then directly add that in XAML(` `; but if you want to use the same view instance multiple times then you will have to clone it first. – akjoshi Jun 27 '12 at 09:59
0

UserControls (or any other type of UI control) do not belong in the ViewModel

It would be far better to have an object that you are binding to, and to use a DataTemplate to tell WPF how to draw that object using your UserControl

For example, if you had

ScreenModel ScreenContent { get; set; }

where ScreenModel is a custom class that looked something like this

public class ScreenModel
{
    public string Name { get; set; }
    ...
}

Then you would bind to it using something like this:

<DockPanel LastChildFill="True">
    <DockPanel>
        <ContentControl Content="{Binding ScreenContent}" Grid.Row="1" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"/>
    </DockPanel>
</DockPanel>

And use a DataTemplate to tell WPF how to draw the ScreenModel class

<DataTemplate DataType="{x:Type models:ScreenModel}">
    <views:ScreenContentUserControl />
</DataTemplate>

or

<DataTemplate DataType="{x:Type models:ScreenModel}">
    <Label Content="{Binding Name}" />
</DataTemplate>

Because you are using a Template, WPF will create a new copy of the UserControl whenever its needed, and won't try to use the same UserControl in multiple spots.

Your error is occurring because you are adding the same UserControl (ScreenContent) to the VisualTree more than once through the Content="{Binding ScreenContent}" binding.

Even doing something like switching tabs will probably cause this error because you will be unloading all the UI objects by switching away from the tab, then loading new UI objects by switching back, however your ScreenContent user control already has it's parent set to an old object that no longer exists.

Rachel
  • 130,264
  • 66
  • 304
  • 490
  • This looks like it might be a good solution. But can you explain further because I am not getting anywhere at all. If I have a user control that has 3 labels. How will I create that user control in xaml, but be able to fill out those labels with data? – Lews Therin Jun 27 '12 at 13:43
  • Or would it know how to do it automagically? I will try and see what happens. What is models: ? – Lews Therin Jun 27 '12 at 13:44
  • @LewsTherin I added a second `DataTemplate` example that uses a `Label` instead of a custom `UserControl`. The `ScreenModel` object is a custom class you create, and in my example it has a property called `Name`. Whenever WPF encounters an object of type `ScreenModel` in the `VisualTree` (in this example, in `ContentControl.Content`), it will draw it using the `DataTemplate` defined for it. – Rachel Jun 27 '12 at 13:56
  • Ok you said it is wrong to create ui controls in the viewmodel. What of the case where Textboxes for example are created in the ViewModel and you can't avoid it. Because with the third party library I am using I can bind to an array of elements. And those elements require me to create ui controls in the VM... In that case what can I do? – Lews Therin Jun 27 '12 at 14:21
  • @LewsTherin That's correct, the `ViewModel` should not contain any reference to any UI Controls. If you want a `TextBox`, you should create a `string` property on your `ViewModel` containing the text for the `TextBox`, and then bind the `Text` property of your `TextBox` from your View (the XAML) to it. `` – Rachel Jun 27 '12 at 14:27
  • I have been reading since your last reply and to do what you are suggesting with the way the tool I have here (can't change the tool) will make the code notoriously difficult to understand. But I think I should be able to minimize the amount of user controls created in the VM. It is a good idea but it can't always be possible :( Or I'm too much of a novice to think so lol – Lews Therin Jun 27 '12 at 16:28
  • Link to say it isn't bad: http://stackoverflow.com/questions/9910131/creating-usercontrols-with-viewmodels-in-wpf I am just trying to understand why it is such a bad idea. What if there aren't alternatives? – Lews Therin Jun 27 '12 at 16:34
  • @LewsTherin That question is asking if its a bad idea to create a `ViewModel` specially for a `UserControl`, and have that `UserControl` load with the `DataContext` preset to an instance of the `ViewModel`. The MVVM design pattern was meant to keep your UI and application logic completely separate. Your `ViewModels` are the application, while your Views (UserControls and UI components) are just a user-friendly interface that is used to interact with the ViewModels. Breaking the pattern by mixing the two makes the code much harder to test, debug, and maintain. – Rachel Jun 27 '12 at 16:51
  • @LewsTherin If I absolutely *had* to use a 3rd party class which contains UI components, I would create a `ViewModel` with properties that get/set the important pieces of the `UserControl`, such as `3rdPartyObject.SomeTextBox.Text`, and create a separate XAML interface that binds to those properties. I would not try and insert the existing UserControls directly into the View from the `ViewModel`. – Rachel Jun 27 '12 at 16:53
  • If you are around in an hrs time we could move this discussion to a room if you don't mind? For example, can you imagine a Usercontrol that consists of 2 labels. One label for a username and the other for the age. For the sake of it. Let's say I want to **have dynamically**, these instances of these UserControl. If I can't create these instances in my ViewModel how do I do that in my View and also be able to set the values? Thanks. Sorry if I am being slow – Lews Therin Jun 27 '12 at 18:14
  • @LewsTherin No problem, although I'm not sure I'll be around in an hour. Can you modify your UserControl at all? Does it expose properties so you can pass it the Name and Age? Typically a WPF UserControl like that would expose two properties for the `Name` and `Age`, so I'd have a `User` class object containing `Name` and `Age` properties, and create a `DataTemplate` that tells WPF to draw the `User` object using my `UserControl`, with the `Name` and `Age` properties being passed through the databindings. `` – Rachel Jun 27 '12 at 18:23
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/13126/discussion-between-lews-therin-and-rachel) – Lews Therin Jun 27 '12 at 19:04