3

In its simplest form...

I would like to create as many StackPanels as I want and then add Rectangles in them. Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.

Any help would be appreciated.

For example, if our favorite beer wrote the framework I could do it like this:

XAML:

<Page
    x:Class="Test2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Test2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">
            <Button Name="StartButton" Content="Start" Click="StartButton_Click" Height="30" Width="200" Margin="5"/>
        </StackPanel>

        <StackPanel Grid.Row="1" Name="myStackPanel" VerticalAlignment="Top"/>

    </Grid>
</Page>

Code Behind:

namespace Test2
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            for (var i = 0; i < 5; i++) // The 5 here could be any number
            {
                myStackPanel.Children.Add(new StackPanel
                {
                    Name = "myPanel" + i,
                    Orientation = Orientation.Horizontal
                });

                for (var j = 0; j < 10; j++) // The 10 here could be any number
                {
                    ("myPanel" + i).Children.Add(new Rectangle
                    {
                        Name = "myRectangle" + i + "-" + j,
                        Fill = new SolidColorBrush(Colors.Black),
                        Width = 20,
                        Height = 20,
                        Margin = new Thickness(1)
                    });
                }
            }
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            // E.G. To change the Fill color of Rectangle4 in StackPanel2

            ("myRectangle" + 2 + "-" + 4).Fill = new SolidColorBrush(Colors.Red);
        }
    }
}
gaw
  • 157
  • 3
  • 13
  • This should guide you. http://stackoverflow.com/questions/874380/wpf-how-do-i-loop-through-the-all-controls-in-a-window .. You just need to modify it in a way that you qualify the name in adding and modifying properties. And of course, you should use VisualTreeHelper. – tgpdyk Dec 19 '15 at 10:19
  • Ok thanks @tgpdyk, I'll check it out. – gaw Dec 19 '15 at 11:54
  • @tgpdyk I seem to be getting errors trying any of those: Argument 1: cannot convert from 'Windows.UI.Composition.Visual' to 'Windows.UI.Xaml.DependencyObject' - I often find this when referred to another document. Could you (or anyone) be more specific - Novice here : ( – gaw Dec 19 '15 at 12:25
  • @gaw Sorry, I didn't realise it was uwp, I'm not familiar with that platform so this answer may or may not be irrelevant, so I'm deleting it. (I did test this on a normal WPF desktop app and it does work there). – pangabiMC Dec 19 '15 at 13:01

2 Answers2

6

Firstly, to add Rectangle shapes, we can create an instance of StackPanel and manipulate its Children elements:

for (var i = 0; i < 5; i++) // The 5 here could be any number
{
                StackPanel sp = new StackPanel
                {
                    Name = "myPanel" + i,
                    Orientation = Orientation.Horizontal
                };
                myStackPanel.Children.Add(sp);

                for (var j = 0; j < 10; j++) // The 10 here could be any number
                {
                    sp.Children.Add(new Rectangle
                    {
                        Name = "myRectangle" + i + "-" + j,
                        Fill = new SolidColorBrush(Colors.Black),
                        Width = 20,
                        Height = 20,
                        Margin = new Thickness(1)
                    });
                }
}

Then to be able to change the Fill color of any one of the Rectangles when I click the Start Button for instance. All in Code Behind.

As tgpdyk mentioned, we need to use VisualTreeHelper to find the specified rectangle shape.

Helper class:

public static class FrameworkElementExtensions
{
        public static T TraverseCTFindShape<T>(DependencyObject root, String name) where T : Windows.UI.Xaml.Shapes.Shape
        {
            T control = null;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
            {
                var child = VisualTreeHelper.GetChild(root, i);

                string childName = child.GetValue(FrameworkElement.NameProperty) as string;
                control = child as T;

                if (childName == name)
                {
                    return control;
                }
                else
                {
                    control = TraverseCTFindShape<T>(child, name);

                    if (control != null)
                    {
                        return control;
                    }
                }
            }

            return control;
        }
}

How to use it:

private void StartButton_Click(object sender, RoutedEventArgs e)
{
            // E.G. To change the Fill color of Rectangle4 in StackPanel2
            var rec = FrameworkElementExtensions.TraverseCTFindShape<Shape>(myStackPanel, "myRectangle" + 2 + "-" + 4);
            rec.Fill = new SolidColorBrush(Colors.Red);
}

enter image description here

I've uploaded my sample to Github repository

Franklin Chen - MSFT
  • 4,845
  • 2
  • 17
  • 28
2

That is not how you approach this in WPF, at all.

You usually do not concern yourself with any UI components but only the data. In this case you data bind an ItemsControl to a list of rows, each row containing a list of cells. In the ItemsControl definition you then set an ItemTemplate that contains another ItemsControl binding to the cells. In the nested ItemsControl you then can set an ItemTemplate where you bind the Background to a (notifying) property of your cells which you then just need to change in code.

Check out these overviews:

You may also want to look into the Model-View-ViewModel pattern to ensure a suitable application architecture.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Here would be a [UWP specific data binding overview](https://msdn.microsoft.com/en-us/library/windows/apps/mt269383.aspx). Most if not everything should be analogous anyway though. – H.B. Dec 20 '15 at 06:40
  • Thanks for your input here. I marked @Franklin Chen - MSFT as the answer as it very specifically answered my question. However implementing it lead me to realise that the way I'm approaching the problem is probably not the best way. The fact that it iterates through all of the rectangles to find the target is taking too long when there are many rows of many rectangles. I'll look through the references you provided and struggle on. In the meantime if you have any free time it would be interesting to see your solution, both to me and others that are struggling in similar ways. – gaw Dec 20 '15 at 11:26
  • Finding the target you want to interact with efficiently is a rather different problem and one that usually is not an issue if you take my approach; it comes down to two array accesses (one for the row, another one for the cell in said row). Once you understand the WPF basics the implementation of my description is really trivial. Right now i do not have an environment where i could implement this unfortunately. – H.B. Dec 22 '15 at 00:45