Page view counter

Dynamically creating and destroying containers

Ben H. asked a question within a comment to a previous blog post.

Suppose, there is a button on the main form and when the user clicks on the button, a new container gets instantiated and the new container appears on the top of the main form. However, there is also a "Close" button on that container that when the user clicks on, the container closes. And here is my question. When the user clicks on the the "Close" button, I'll set the visibility of that Container to Collapsed, however I need to raise some kind of event in that Close button that the main form (who originally created the Container), will respond to this event that the container has been closed, therefore I need to remove it from the Children.Remove(Container) and then set the container to null.

I don't know how to raise that event in the "Close" button and how to respond to it in the main form.

A sample or explanation or snippet or pointer to some info is much appreciated!

I fully admit that I may not have answered this completely, but after a few minutes of noodling with it, here is what I have... which may be a good start or may be off point but perhapsinteresting anyway.

I created a page.xaml with a grid (one row, two columns) and a button,

<UserControl x:Class="DynamicControls.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Button x:Name="Create" Content="Create" 
              Width="50" Height="30" Grid.Row="0" 
              Grid.Column="0" />
    </Grid>
</UserControl>

In the code behind I created an event handler for the button,

private int id = 0;
public Page()
{
    InitializeComponent();
    Loaded += new RoutedEventHandler(Page_Loaded);
}

void Page_Loaded(object sender, RoutedEventArgs e)
{
    Create.Click +=new RoutedEventHandler(Create_Click);
}

The job of the event handler is to dynamically create a container that holds two buttons, one of which is the close button.

void  Create_Click(object sender, RoutedEventArgs e)
{

    StackPanel sp = new StackPanel();
    sp.Orientation = Orientation.Horizontal;
    sp.VerticalAlignment = VerticalAlignment.Center;
    sp.Width = 300;
    sp.Height = 500;
    sp.SetValue(Grid.RowProperty, 0);
    sp.SetValue(Grid.ColumnProperty, 1);
    sp.Tag = (++id).ToString();
    sp.Background = new SolidColorBrush(Colors.Cyan);

    Button btn = new Button();
    btn.Width = 50;
    btn.Height = 30;
    btn.Content = "Close";
    btn.Margin = new Thickness(5.0, 0, 0, 0);
    btn.Tag = id.ToString();
    btn.Click += new RoutedEventHandler(btn_Click);
    sp.Children.Add(btn);

    btn = new Button();
    btn.Width = 50;
    btn.Height = 30;
    btn.Content = "Hello";
btn.Margin = new Thickness(5.0, 0, 0, 0); btn.Click += new RoutedEventHandler(otherBtn_click); sp.Children.Add(btn); LayoutRoot.Children.Add(sp); }

 

Note that the stack panel is assigned a unique ID in its TAG property as is the button

TagProperty

We'll come back to what this is for in just a moment.  After adding the stack panel we add two buttons. One to close the stack panel, and another just to have something else in the stack panel (in this case a button that knows how to change its background color.

CreatedStackPanel

Note that each of the buttons is added to the stack panel's children collection and the stack panel itself is added to the grid's children collection.

sp.Children.Add(btn);
LayoutRoot.Children.Add(sp);

Events

Each of the buttons has its own event, and its own event handler. The second button has an event handler cleverly named "otherBtn_click" (it was late, I was tired...)

btn.Click += new RoutedEventHandler(otherBtn_click);

That event handler picks one of six colors and sets the background for the button,

void otherBtn_click(object sender, RoutedEventArgs e)
{
    int randomNumber;
    Random r = new Random();
    randomNumber = r.Next(0,5);
    List<Color> myColors = new List<Color>();
    myColors.Add(Colors.Magenta);
    myColors.Add(Colors.Purple);
    myColors.Add(Colors.Red);
    myColors.Add(Colors.Gray);
    myColors.Add(Colors.Green);
    myColors.Add(Colors.Blue);
    Button btn = sender as Button;
    if ( btn != null )
    {
        btn.Background = new SolidColorBrush(myColors[randomNumber]);
        btn.Foreground = new SolidColorBrush(Colors.Black);
    }
}

 

Close The Door!

 

The first button is the one that answers the question. Its job is to close the form.

To be explicit, when the user clicks on the Close button, we want the page to be alerted. To accomplish this, we assign an event to the button

btn.Click += new RoutedEventHandler(btn_Click);

The event handler looks through all the elements in the children of the page to find any of type stack panel. If it finds a stack panel it looks to see if the stack panel's Tag holds an ID that matches the ID of the button that fired the event. If so, it has the right stack panel and it removes it from the page's children collection and <poof> it's gone.

 

void btn_Click(object sender, RoutedEventArgs e)
{
    foreach (UIElement uie in LayoutRoot.Children)
    {
        StackPanel sp = uie as StackPanel;
        Button btn = sender as Button;
        if (sp != null && btn != null)
        {
            if (sp.Tag.ToString().Equals(btn.Tag.ToString()))
            {
                LayoutRoot.Children.Remove(uie);
                break;  
            }
        }
    }           // end foreach
}               // end btn_Click

There may be easier ways, but this works quite cleanly. As an exercise for the reader I suggest implementing this so that each click of the Create button creates a stack panel in a different grid location, so that you can have a few open and see that each close button closes the correct one.

 

 

 

Here's the complete source code

 

 

Page.xaml

<UserControl x:Class="DynamicControls.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Button x:Name="Create" Content="Create" Width="50" Height="30" Grid.Row="0" Grid.Column="0" />
    </Grid>
</UserControl>

Page.xaml.cs

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections.Generic;

namespace DynamicControls
{
    public partial class Page : UserControl
    {
        private int id = 0;
        public Page()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            Create.Click +=new RoutedEventHandler(Create_Click);
        }

        void  Create_Click(object sender, RoutedEventArgs e)
        {

            StackPanel sp = new StackPanel();
            sp.Orientation = Orientation.Horizontal;
            sp.VerticalAlignment = VerticalAlignment.Center;
            sp.Width = 300;
            sp.Height = 500;
            sp.SetValue(Grid.RowProperty, 0);
            sp.SetValue(Grid.ColumnProperty, 1);
            sp.Tag = (++id).ToString();
            sp.Background = new SolidColorBrush(Colors.Cyan);

            Button btn = new Button();
            btn.Width = 50;
            btn.Height = 30;
            btn.Content = "Close";
            btn.Margin = new Thickness(5.0, 0, 0, 0);
            btn.Tag = id.ToString();
            btn.Click += new RoutedEventHandler(btn_Click);
            sp.Children.Add(btn);

            btn = new Button();
            btn.Width = 50;
            btn.Height = 30;
            btn.Content = "Hello";
            btn.Margin = new Thickness(5.0, 0, 0, 0);
            btn.Click += new RoutedEventHandler(otherBtn_click);
            sp.Children.Add(btn);

            LayoutRoot.Children.Add(sp);

        }

        void btn_Click(object sender, RoutedEventArgs e)
        {
            foreach (UIElement uie in LayoutRoot.Children)
            {
                StackPanel sp = uie as StackPanel;
                Button btn = sender as Button;
                if (sp != null && btn != null)
                {
                    if (sp.Tag.ToString().Equals(btn.Tag.ToString()))
                    {
                        LayoutRoot.Children.Remove(uie);
                        break;  
                    }
                }
            }           // end foreach
        }               // end btn_Click

        void otherBtn_click(object sender, RoutedEventArgs e)
        {
            int randomNumber;
            Random r = new Random();
            randomNumber = r.Next(0,5);
            List<Color> myColors = new List<Color>();
            myColors.Add(Colors.Magenta);
            myColors.Add(Colors.Purple);
            myColors.Add(Colors.Red);
            myColors.Add(Colors.Gray);
            myColors.Add(Colors.Green);
            myColors.Add(Colors.Blue);
            Button btn = sender as Button;
            if ( btn != null )
            {
                btn.Background = new SolidColorBrush(myColors[randomNumber]);
                btn.Foreground = new SolidColorBrush(Colors.Black);
            }
        }

    }                   // end class
}
 
Published Wednesday, May 07, 2008 1:33 PM by jesseliberty

Comments

# re: Dynamically creating and destroying containers

Hi Jesse;

First thank you for your help. You asked me a series of questions and I answered them all to clarify an important point that you had missed in my first question and also sent you an email with screen shots that further described what I was looking for.

The solution you gave wasn't what I was asking. I was mainly asking about "Dynamically creating USER CONTROLS" from the main page and destroying them from main page. You created the stackpanel with two buttons on the same page. That's easy to close or destroy.

What I'm asking is, your create button should Instantiate a User Control that is in a separate Xaml file and separate class. Inside this User Control, there is a button that says "Close". When the user Clicks on this "Close" Button inside of the User Control, I want this close button to raise an event that the "MainPage" will respond to the event and then destroy the User Control.

If you go back to my last answers, I think I tried to clarify that the close button is in a different class and file than the one who created it.

That's where I got the challenge!

Hope thic clarifies it now!

Wednesday, May 07, 2008 2:10 PM by BenHayat

# re: Dynamically creating and destroying containers

Entirely my fault, I didn't take the time to fully understand the problem (though I did enjoy working through the problem I did understand!).

Okay, what you want is a bit harder (only in that I haven't thought through how to do it).  I have two suggestions: (1) definately post it to the forums so that you are not waiting for me and (2) I'll try to work on that as well.

Thanks and sorry for missing the boat.

-j

Wednesday, May 07, 2008 3:32 PM by jesseliberty

# re: Dynamically creating and destroying containers

>>Entirely my fault, I didn't take the time to fully understand the problem<<

This is what I like about you, your honesty and being humble!!!

Not a problem... Trust me, this was posted on the forum a while back and the Only response I got, was to raise an event when the User Control's Close button was pressed.

Then in the main page, I have to have an event handler (who has subscribed to the raised event) to remove it from the Grid's children and set the form to null.

I'm getting closer to what I want to do, and that's why I thought I get a lift or hand holding on this last part.

I appreciate your effort and if you do get a chance, I'll still welcome your input!

..Ben

Wednesday, May 07, 2008 5:03 PM by BenHayat

# re: Dynamically creating and destroying containers

hi Ben,Jesse,

If I understand right, this is what you need:

instantiate a control from a separate xaml and show it from Page.xaml.cs, click on Close button and then, Page.xaml.cs will know about the click and act by hiding the control then removing it.

When you click close on the instantiated control, you call a method that activates an event:

public delegate void this_IsClosing();

public event this_Close isClosing;

private void ButtonClose_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

       {

           isClosing();

           //you can hide it here if you wish

       }

then, in Page.xaml.cs

//this instantiates your control

//make sure is known in the class

//so that you can remove it later

MyControl c= new MyControl();

c.isClosing+=new MyControl.this_Close(doTheTrickHere);

void doTheTrickHere()

{

   //remove the control here

   c.Visibility = Visibility.Collapsed;

   LayoutRoot.Remove(c);

}

I have this implemented in my own application. If my explanation is not good, please let me know. nick At fastcom.ie

Wednesday, May 07, 2008 7:29 PM by Radiox

# re: Dynamically creating and destroying containers

Nick, sending you an email with my question

Thank you!

..Ben

Wednesday, May 07, 2008 7:54 PM by BenHayat

# Dew Drop - May 8, 2008 | Alvin Ashcraft's Morning Dew

Pingback from  Dew Drop - May 8, 2008 | Alvin Ashcraft's Morning Dew

Thursday, May 08, 2008 9:18 AM by Dew Drop - May 8, 2008 | Alvin Ashcraft's Morning Dew