Create a modeless window in WPF C# using "new" that mirrors input and display with the original using a UserControl

Levi Para 5 Reputation points
2023-04-02T03:27:39.5633333+00:00


I apologize for the wordy Title of the post. My goal is to create a reusable group of XAML elements (by creating a UserControl) in order to display information either entered manually through text boxes or automatically elsewhere in a CS file. I then want to "duplicate and detach this control" into a new window EXCEPT I don't want to use a new instance of the control (UserControl newControl = new UserControl()) by assigning it (new Window.Content = newControl). I want to reference the element properties that already exists in my MainWindow's UserControl.

So step 1: Create a UserControl and write the XAML that allows the editing and display of the desired information through element property names. (Easy)

Step 2: Use this UserControl in the MainWindow XAML of my application along side other groups of information seperate from this control. (Easy)

Step 3: Allow the user to create a duplicate of this UserControl into a new modeless window where the only thing displayed is this UserControl. (This is where the issue begins)

Step 4: Making changes to this UserControl element properties in EITHER THE MAIN WINDOW OR THE MODELESS WINDOW (both with the same referenced user control) will update and refresh the element properties in BOTH windows.

I am a WPF and XAML beginner and am unsure how to achieve this behavior.

This is currently what I have, but this creates a new instance of the xaml and does not reflect changes made in the MainWindow.

private void Button_Click(object sender, RoutedEventArgs e)
        {
            //Creates an instance
            UserControl1 uc1 = new UserControl1();

            Window newWin = new Window();
            newWin.Content = uc1;
            
            newWin.Show();
        }

And when I do this

private void Button_Click(object sender, RoutedEventArgs e)
        {
            //attempt to reference the element by x:name
            Window newWin = new Window();
            newWin.Content = myUserControl;
            
            newWin.Show();
        }

I get a runtime error of:

System.ArgumentException: 'Must disconnect specified child from current parent Visual before attaching to new parent Visual.'

Thank you for any help in this matter.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,678 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
767 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Hui Liu-MSFT 40,586 Reputation points Microsoft Vendor
    2023-04-03T05:38:34.9966667+00:00

    Hi,@Levi Para. Welcome Microsoft Q&A.

    The whole idea is that if an element already has a logical parent, it cannot be assigned as a child of another element.

    Such as the errors you encounter.

    If you want a new instance identical to an existing control instance you could use XamlWriter and XamlReader to serialize/deserialize the control.

    UserControl:

     <StackPanel Orientation="Horizontal">
            <Label Content="Name" Width="100" Height="50" Background="LawnGreen"/>
            <TextBox Width="100" Height="50" Background="AliceBlue" Text="li"/>
    
        </StackPanel>
    

    MainWindow.xaml:

    <Grid x:Name="grid">
            <local:UserControl1 x:Name="myUserControl"/>
            <Button Content="copy" Width="100" Height="50" Click="Button_Click"/>
        </Grid>
    
     private void Button_Click(object sender, RoutedEventArgs e)
            {
                //attempt to reference the element by x:name
    
                Window newWin = new Window();
                UserControl copy = XamlReader.Parse(XamlWriter.Save(myUserControl)) as UserControl;
               
                newWin.Content = copy;
              
                newWin.Show();
            }
    

    The result:

    4


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


  2. Viorel 112.5K Reputation points
    2023-04-03T09:32:34.44+00:00

    I think that you should use the “Binding”.

    A sample user control that contains a textbox:

    <UserControl . . .>
        <Grid>
            <TextBox Text="{Binding MyText, UpdateSourceTrigger=PropertyChanged}"/>
        </Grid>
    </UserControl>
    

    The first window:

    <Window . . .>
        <Grid>
            <local:UserControl1 x:Name="userControlA" HorizontalAlignment="Left" Margin="40,40,0,0" VerticalAlignment="Top" Width="200"/>
            <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="40,80,0,0" VerticalAlignment="Top" Click="button_Click"/>
        </Grid>
    </Window>
    

    The second window:

    <Window . . .>
        <Grid>
            <local:UserControl1 x:Name="userControlB" HorizontalAlignment="Left" Height="54" Margin="40,40,0,0" VerticalAlignment="Top" Width="200" x:FieldModifier="public"/>
        </Grid>
    </Window>
    

    The code of first window:

    public partial class Window1 : Window
    {
        MyData data = new MyData( );
    
        public Window1( )
        {
            InitializeComponent( );
    
            userControlA.DataContext = data;
        }
    
        private void button_Click( object sender, RoutedEventArgs e )
        {
            var w = new Window2( );
            w.userControlB.DataContext = data;
            w.Show( );
        }
    }
    
    
    public class MyData : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;
    
        private string myText;
        public string MyText
        {
            get
            {
                return myText;
            }
            set
            {
                if( myText != value )
                {
                    myText = value;
    
                    PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( nameof( MyText ) ) );
                }
            }
        }
    }
    

    MyData is a special object that will keep and share your data. (Can be moved to separate file).

    So the idea is to assign the same object to DataContext of user controls and to use the binding. The values of textboxes will be automatically copied.

    0 comments No comments