4

I've tested some thinks with Dynamic Textboxes and Buttons, Adding Items to my Grid works very well, but if I want to Delete it there are some Bugs, sometimes 1 Row is empty and my Add Button disappears or my Program crashs.

What did I wrong or what did I missed?

C# Code:

    public MainWindow()
    {
        InitializeComponent();
    }

    int Numberic = 0;
    private void NewButton_Click(object sender, RoutedEventArgs e)
    {
        #region Row and Numberic
        Numberic++;
        RowDefinition ROW = new RowDefinition();
        GridLength Height = new GridLength(59);
        ROW.Height = Height;

        MainGrid.RowDefinitions.Add(ROW);
        #endregion

        #region Set new Button Location
        int ButtonLocation = Grid.GetRow(NewButton);
        Grid.SetRow(NewButton, ButtonLocation + 1);
        #endregion

        #region Create TextBox
        TextBox CreateTextBox = new TextBox();
        CreateTextBox.Name = "NewTextBox_" + Numberic;
        CreateTextBox.Width = 438;
        CreateTextBox.Height = 35;
        CreateTextBox.Margin = new Thickness(53, 12, 0, 0);
        CreateTextBox.HorizontalAlignment = HorizontalAlignment.Left;
        CreateTextBox.VerticalAlignment = VerticalAlignment.Top;

        CreateTextBox.FontSize = 15;
        CreateTextBox.HorizontalContentAlignment = HorizontalAlignment.Left;
        CreateTextBox.VerticalContentAlignment = VerticalAlignment.Center;

        MainGrid.Children.Add(CreateTextBox);
        Grid.SetRow(CreateTextBox ,ButtonLocation);
        #endregion

        #region Create Button
        Button CreateButton = new Button();
        CreateButton.Name = "NewButton_" + Numberic;
        CreateButton.Width = 35;
        CreateButton.Height = 35;
        CreateButton.Margin = new Thickness(12, 12, 0, 0);
        CreateButton.HorizontalAlignment = HorizontalAlignment.Left;
        CreateButton.VerticalAlignment = VerticalAlignment.Top;

        CreateButton.Content = "-";
        CreateButton.FontSize = 20;
        CreateButton.FontWeight = FontWeights.Bold;
        BrushConverter BC = new BrushConverter();
        CreateButton.Background = (Brush)BC.ConvertFrom("#FFDB0000");
        CreateButton.Foreground = Brushes.White;
        CreateButton.BorderBrush = Brushes.Transparent;

        CreateButton.Click += new RoutedEventHandler(Delete_OnClick);

        MainGrid.Children.Add(CreateButton);
        Grid.SetRow(CreateButton, ButtonLocation);
        #endregion
    }

    private void Delete_OnClick(object sender, RoutedEventArgs e)
    {
        Button SelectedButton = (Button)sender;
        int SelectedRow = Grid.GetRow(SelectedButton);

        string[] Number = SelectedButton.Name.Split('_');
        string TextBoxName = "NewTextBox" + "_" + Number[1];
        TextBox SelectedTextbox = (TextBox)LogicalTreeHelper.FindLogicalNode(MainGrid, TextBoxName);

        MainGrid.Children.Remove(SelectedTextbox);
        MainGrid.Children.Remove(SelectedButton);

        //Numberic--;
        MainGrid.RowDefinitions.RemoveAt(SelectedRow);
    }

XAML Code:

<Grid Name="MainGrid" ShowGridLines="True" OpacityMask="Black" Background="#FFEDEDED">
    <Grid.RowDefinitions>
        <RowDefinition Height="59" />
    </Grid.RowDefinitions>
    <Button Content="+" Height="35" HorizontalAlignment="Left" Margin="12,12,0,0" Name="NewButton" VerticalAlignment="Top" Width="35" BorderBrush="{x:Null}" Foreground="White" Background="#FF727272" FontWeight="Bold" FontSize="20" Click="NewButton_Click" />
</Grid>

Thank you, Tkay

3
  • 3
    Why in the name of all that's holy, are you dynamically adding/removing controls? Consider wrapping your controls in a Grid, or some other container and change it's Visibility property to Collapsed when the user doesn't need to see it. Commented Jun 15, 2015 at 11:24
  • Do you mean why I'm adding and removing Row`s? I was thinking that would be good to have an Endless adding System. Commented Jun 15, 2015 at 11:42
  • 1
    I believe @MikeEason means Why are you using WPF as if it were WinForms?. In WPF, we typically use XAML and data binding and do not manually add UI elements from code behind. If you had used data binding, then deleting a data bound Textbox/Grid is as simple as removing an object from a collection. Commented Jun 15, 2015 at 13:13

2 Answers 2

1

WPF's ItemsControl is the correct way to show items in your views when the number of items can change.

Many controls inherit from ItemsControl, like the ComboBox itself, or the DataGrid, or the ListBox... But in this example I'll use ItemsControl directly, since what you're trying to do doesn't need any extra functionality.

First of all, the XAML:

    <Grid Name="MainGrid" OpacityMask="Black" Background="#FFEDEDED">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <ItemsControl x:Name="MyItemsControl">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Button Content="-" Height="35" HorizontalAlignment="Left" Margin="12,12,0,0" Name="NewButton" VerticalAlignment="Top" Width="35" BorderBrush="{x:Null}" Foreground="White" Background="#FFDB0000" FontWeight="Bold" FontSize="20" Click="Delete_OnClick" />
                        <TextBox Width="438" Height="35" Margin="6,12,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="15" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" />
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <Button Grid.Row="1" Content="+" Height="35" HorizontalAlignment="Left" Margin="12,12,0,0" Name="NewButton" VerticalAlignment="Top" Width="35" BorderBrush="{x:Null}" Foreground="White" Background="#FF727272" FontWeight="Bold" FontSize="20" Click="NewButton_Click" />
    </Grid>

I've put your definitions of the "-" button and the TextBox inside an ItemTemplate. This template will be reused by the ItemsControl for each item we add to it.

Now, the code-behind:

public partial class MainWindow : Window
{
    int Numberic = 0;

    private void NewButton_Click(object sender, RoutedEventArgs e)
    {
        var item = new Item();
        item.Id = ++Numberic;
        MyItemsControl.Items.Add(item);
    }

    private void Delete_OnClick(object sender, RoutedEventArgs e)
    {
        Button SelectedButton = (Button)sender;
        var item = SelectedButton.DataContext;
        MyItemsControl.Items.Remove(item);
    }
}

As you can see, this is A LOT simpler and cleaner than your previous approach. Most of the logic is handled by the ItemsControl and you only need to Add and Remove items to/from it.

I've used a custom class (Item) for the items of our ItemsControl, but it could be anything you want. In this case, the definition of Item is just:

public class Item
{
    public int Id { get; set; }
}

Check that in the Delete_OnClick method I'm using the DataContext property to retrieve the current Item instance.

That's because when you add an object to an ItemsControl, it creates the corresponding "visual item" with it's visual representation (that we've defined in our template, in this case), and assigns the added object as DataContext of this "visual item" and controls. That way you can create Bindings directly to its properties, etc.

Sign up to request clarification or add additional context in comments.

4 Comments

The next step would be using MVVM and data Bindings :P Look for information about Bindings and how to use them to populate your ItemsControl, set Commands for your Buttons (instead of using the Click event) and connect your TextBlocks and TextBoxes to actual data.
This worked very well, and like you said, its much less Code the my one. Thanks for helping me, looking forward to the Data-Bidings. :)
Do you suggest an Website for learning C# WPF? With my Knowledge and bad English I can't find something about Data-Bindings or Commands. Maybe I'm blind.
I suggest you start here: wpftutorial.net/GettingStarted.html ;) and here for DataBinding: wpftutorial.net/DataBindingOverview.html
0

First, I feel compelled to ask you to consider using an ItemsControl for this kind of stuff. It's a lot easier and cleaner. If you don't know how to do so, tell me and I'll try to post an example.

But answering your original question... What's happening is that you're removing the RowDefinition from the Grid, but you're not updating the Grid.Row property of the remaining controls accordingly.

For instance, if you've created 3 rows, your original "+" button is now on Grid.Row number 4.

If you delete one of the rows you've created, the Grid now has only 4 row definitions, but your "+" button is still assigned to Grid.Row 4 (which doesn't exist). Hence, it is put on the closest match, Grid.Row number 3. It doesn't disappear, it's just right under the "-" button in row 3.

2 Comments

Okay, now I know where my Button is, but how could I move them correctly? And I dont know about ItemsControl, it would be great from you to see an Example, maybe this helps me.
Ok, I'll add another answer with the correct way to do this in WPF using an ItemsControl :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.