-1

I cannot find a similar question or answer to what I am asking but if there is one out there I do apologize. What I am looking to achieve is to simply loop through all my controls in a user control and then write to a string builder in the order of the loop.

The code below outlines a simple example and I did try some limited things based on me being knew to C# etc.

<Window x:Class="CopyandPaste.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CopyandPaste"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid x:Name="MainGrid">
        <Button x:Name="CopyButton" Content="Copy Button" HorizontalAlignment="Left" Margin="10,34,0,0" VerticalAlignment="Top" RenderTransformOrigin="-4.265,-0.098" Height="41" Width="120" Click="CopyButton_Click"/>

        <Grid x:Name="MasterGrid" HorizontalAlignment="Left" Height="378" Margin="146,10,0,0" VerticalAlignment="Top" Width="572">

            <TextBox HorizontalAlignment="Center" Margin="0,21,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="408" Height="23" Grid.Column="1"/>
            <RadioButton x:Name="RadioButton" Content="RadioButton" HorizontalAlignment="Left" Margin="82,55,0,0" VerticalAlignment="Top" Height="15" Width="85"/>
            <ComboBox x:Name="ComboBox" HorizontalAlignment="Left" Margin="82,95,0,0" VerticalAlignment="Top" Width="369" Height="22">
                <ComboBoxItem>THIS IS IT 1</ComboBoxItem>
                <ComboBoxItem>THIS IS IT 2</ComboBoxItem>
                <ComboBoxItem>THIS IS IT 3</ComboBoxItem>
            </ComboBox>
            <TextBox HorizontalAlignment="Center" Margin="0,140,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="408" Height="23"/>
            <TextBox HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" Width="408" RenderTransformOrigin="0.333,0.782" Height="28"/>


        </Grid>

    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CopyandPaste
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();            
        }

        private void CopyButton_Click(object sender, RoutedEventArgs e)
        {
            bool first = true;

            StringBuilder value = new StringBuilder();

            // some loop here

            Clipboard.SetText(value.ToString());
        }
    }
}

I played with some foreach loops that were nested but of course got some results that were unwanted:

1
RadioButton
THIS IS IT 2
2
RadioButton
THIS IS IT 2
3
RadioButton
THIS IS IT 2

I understand what is going on here where the text box is causing the loop to continue. Trying to get some ideas or a point in the right direction to achieve an output like:

1
RadioButton
THIS IS IT 2
2
3

  • Do you want to enumerate all of your controls in the exact order you declared them ? – aybe Jul 13 '20 at 17:59
  • It's not clear what your expected output is? – Sach Jul 13 '20 at 17:59
  • You can do a recursive loop in a function call whose parameter is the main control and which in turn loops for its child controls. The function call will return a string containing the names of all the sub controls of the control. – Kaveesh Jul 13 '20 at 18:02
  • Does this answer your question? [Find all controls in WPF Window by type](https://stackoverflow.com/questions/974598/find-all-controls-in-wpf-window-by-type) – Sach Jul 13 '20 at 18:12
  • @Aybe, yes, if there is text entered, drop down selected or radio button selected that would go to the string builder in the order of how the controls are laid out in mainwindow. If no text or selection then skip that control and move to the next one. – m.nelson0100 Jul 13 '20 at 18:14
  • @Sach Expected/desired output is at the end of the post: 1 RadioButton THIS IS IT 2 2 3 Thanks – m.nelson0100 Jul 13 '20 at 18:15
  • Yes, but you haven't explalined why its the expected output. What does 1, 2, 3 refer to? Are they control names? Why is only THIS IS IT 2 an expected result, but not the other combo items? – Sach Jul 13 '20 at 18:18
  • @Sach, oh sorry. That output is based on what a user has typed into the text boxes or what radio button was selected and so on. I just need it order as its laid out in the main window. I can grab the controls and loop them but I need to use string builder to order them as they are ordered in main window. – m.nelson0100 Jul 13 '20 at 18:50

1 Answers1

1

Any WPF UIElement has a Children property which will give you all its child controls. So you can use it to enumerate children and then loop through them. Something like;

foreach (var child in MasterGrid.Children)
{
    // string builder code
}

You haven't quite clearly explained what your criteria to display a control is, but from the limited explanation you've given it looks like you want to grab the Text property if it's a TextBox, the SelectedItem if it is a ComboBox, and the name of the control if it is a RadioButton. These are quite different things when it comes to a control, so inside the loop you will have to check the type of each child control and get the right information.

private void CopyButton_Click(object sender, RoutedEventArgs e)
{
    StringBuilder value = new StringBuilder();
    foreach (var child in MasterGrid.Children.OfType<Control>())
    {
        if (child.GetType() == typeof(TextBox))
        {
            value.Append(((TextBox)child).Text + Environment.NewLine);
        }
        else if (child.GetType() == typeof(RadioButton))
        {
            var rb = (RadioButton)child;
            if (rb.IsChecked == true)
            {
                value.Append(rb.Name + Environment.NewLine);
            }
        }
        else if (child.GetType() == typeof(ComboBox))
        {
            value.Append(((ComboBox)child).Text + Environment.NewLine);
        }
    }
    MessageBox.Show(value.ToString());
}

Above is for the types of controls you've mentioned, you'll have to figure out what to do with other control types.

Next when it comes to order, there can be different meanings to the 'order' in controls in a WPF GUI.

  • Do you want the order in which they are listed in the XAML code?
  • Or do you want the order in which they are displayed when the program is run?

For example, in your code if you moved the last text box XAML code above the second-to-last, they will still appear in the original order when you run the program because you've hard-coded their locations (which in itself is a bad idea; you should use grids, stack panels etc to do your layout in WPF).

I think if you care about the order, best option is to modify your XAML and specify the Tag property of each control.

E.g.

<TextBox Grid.Column="1"
    Width="408"
    Height="23"
    Margin="0,21,0,0"
    HorizontalAlignment="Center"
    VerticalAlignment="Top"
    Tag="1"
    TextWrapping="Wrap"/>

Then when you enumerate the children, do an order by tag before iterating through them.

This way you have firm control over whatever it is that you mean by order.

var children = MasterGrid.Children.OfType<Control>().OrderBy(x => x.Tag);
foreach (var child in children)
{
    // Same as before
}
Sach
  • 10,091
  • 8
  • 47
  • 84
  • Sach, thanks a ton. This actually cleared up all the confusion I was having and definitely got me in the right direction. I was not aware of the tag capability! – m.nelson0100 Jul 13 '20 at 19:50