Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Programmatically create Grid within row of a Grid in Wpf

Tags:

c#

.net

wpf

I am working in WPF -- There is button with click event handler in my application. As i click on button it's event handler generates a new row in grid named as grids. In this new Row i want to add another grid programmatically to add Label, Button and TextBox in this grid in row. As i executed my code it only generates a texboxes! where labels and button shown once! Here code and image is : Please feel free to ask if my query is not clear to you!

 int r =0;
 private void button2_Click(object sender, RoutedEventArgs e)
    {
        TextEdit text1; Button button1; Grid grid1;
        grids.RowDefinitions.Add(new RowDefinition());
        text1 = new TextEdit();
        text1.SetValue(Grid.ColumnProperty, 1);
        text1.SetValue(Grid.RowProperty, r);
        button1 = new Button();
        button1.Content = "Left + " + r;
        button1.Click += new RoutedEventHandler(button1_Click);
        button1.SetValue(Grid.ColumnProperty, 1);
        button1.SetValue(Grid.RowProperty, r);
        grid1 = new Grid();
        grid1.SetValue(Grid.ColumnProperty, 1);
        grids.RowDefinitions.Add(new RowDefinition());
        grid1.SetValue(Grid.RowProperty, r);
        grids.Children.Add(button1);
        grids.Children.Add(text1);
        r = r + 1;
    }

enter image description hereEDIT

 int r =0;
 private void button2_Click(object sender, RoutedEventArgs e)
  {
    db obj = new db();
    var query = from p in obj.TableA select p ;

  foreach(var a in query.ToList())
  {
    TextEdit text1; Button button1; Grid grid1;
    grids.RowDefinitions.Add(new RowDefinition());
    text1 = new TextEdit();
    text1.SetValue(Grid.ColumnProperty, 1);
    text1.SetValue(Grid.RowProperty, r);
    button1 = new Button();
    button1.Content = a.name;
    button1.Click += new RoutedEventHandler(button1_Click);
    button1.SetValue(Grid.ColumnProperty, 1);
    button1.SetValue(Grid.RowProperty, r);
    grid1 = new Grid();
    grid1.SetValue(Grid.ColumnProperty, 1);
    grids.RowDefinitions.Add(new RowDefinition());
    grid1.SetValue(Grid.RowProperty, r);
    grids.Children.Add(button1);
    grids.Children.Add(text1);
    r = r + 1;}
}
like image 284
Tameen Malik Avatar asked Sep 29 '13 17:09

Tameen Malik


2 Answers

Ok. Delete all your code and start all over.

If you're working with WPF, you really need to have The WPF Mentality

As a general rule, you almost never create or manipulate UI elements in procedural code in WPF. That's what XAML is for.

This the right way to do what you're asking in WPF (in a full working example):

<Window x:Class="MiscSamples.ItemsControlSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
        Title="ItemsControlSample" Height="300" Width="300">
    <DockPanel>
        <Button Content="Add New Row" Command="{Binding AddNewRowCommand}"
                DockPanel.Dock="Bottom"/>

        <ItemsControl ItemsSource="{Binding Data}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border  BorderBrush="Black" Background="Gainsboro" BorderThickness="1" Margin="2">
                        <!-- This is the Inner Grid for each element, which is represented in Brown color in your picture -->
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition Width=".2*"/>
                                <ColumnDefinition Width=".2*"/>
                            </Grid.ColumnDefinitions>

                            <Label Content="{Binding Label1Text}"
                                   Margin="2"/>

                            <Button Content="Button1" 
                                    Command="{Binding DataContext.Command1, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                    CommandParameter="{Binding}"
                                    Grid.Column="1" Margin="2"/>

                            <Button Content="Button2" 
                                    Command="{Binding DataContext.Command2, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
                                    CommandParameter="{Binding}"
                                    Grid.Column="2" Margin="2"/>

                            <dxe:TextEdit Text="{Binding Text}"
                                          Grid.Row="1" Grid.ColumnSpan="3"
                                          Margin="2"/>
                        </Grid>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>

            <ItemsControl.Template>
                <ControlTemplate TargetType="ItemsControl">
                    <ScrollViewer CanContentScroll="True">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </DockPanel>
</Window>

Code Behind:

public partial class ItemsControlSample : Window
{
    public ItemsControlSample()
    {
        InitializeComponent();
        DataContext = new ItemsControlSampleViewModel();
    }
}

ViewModel:

public class ItemsControlSampleViewModel
{
    public ObservableCollection<ItemsControlSampleData> Data { get; set; }

    public Command AddNewRowCommand { get; set; }

    public Command<ItemsControlSampleData> Command1 { get; set; }

    public Command<ItemsControlSampleData> Command2 { get; set; }

    public ItemsControlSampleViewModel()
    {
        var sampledata = Enumerable.Range(0, 10)
                                   .Select(x => new ItemsControlSampleData()
                                                {
                                                    Label1Text = "Label1 " + x.ToString(),
                                                    Text = "Text" + x.ToString()
                                                });

        Data = new ObservableCollection<ItemsControlSampleData>(sampledata);
        AddNewRowCommand = new Command(AddNewRow);
        Command1 = new Command<ItemsControlSampleData>(ExecuteCommand1);
        Command2 = new Command<ItemsControlSampleData>(ExecuteCommand2);

    }

    private void AddNewRow()
    {
        Data.Add(new ItemsControlSampleData() {Label1Text = "Label 1 - New Row", Text = "New Row Text"});
    }

    private void ExecuteCommand1(ItemsControlSampleData data)
    {
        MessageBox.Show("Command1 - " + data.Label1Text);
    }

    private void ExecuteCommand2(ItemsControlSampleData data)
    {
        MessageBox.Show("Command2 - " + data.Label1Text);
    }
}

Data Item:

public class ItemsControlSampleData
{
    public string Label1Text { get; set; }

    public string Text { get; set; }
}

Helper classes:

public class Command : ICommand
{
    public Action Action { get; set; }

    public string DisplayName { get; set; }

    public void Execute(object parameter)
    {
        if (Action != null)
            Action();
    }

    public bool CanExecute(object parameter)
    {
        return IsEnabled;
    }

    private bool _isEnabled = true;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            _isEnabled = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler CanExecuteChanged;

    public Command(Action action)
    {
        Action = action;
    }
}

public class Command<T>: ICommand
{
    public Action<T> Action { get; set; }

    public void Execute(object parameter)
    {
        if (Action != null && parameter is T)
            Action((T)parameter);
    }

    public bool CanExecute(object parameter)
    {
        return IsEnabled;
    }

    private bool _isEnabled = true;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            _isEnabled = value;
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }

    public event EventHandler CanExecuteChanged;

    public Command(Action<T> action)
    {
        Action = action;
    }
}

Result:

enter image description here

  • Notice how I'm not dealing with UI in procedural code, but instead I'm using DataBinding and simple, simple properties. That's how you program in WPF. That's what the WPF mentality is about.
  • I'm using an ItemsControl and a DataTemplate defined in XAML to let WPF create the UI for each of my data items.
  • Also notice how my code does nothing except expose the data and define reusable Commands that serve as abstractions to the user actions such as Button clicks. This way you can concentrate in coding your business logic instead of struggling with how to make the UI work.
  • The buttons inside each item are bound to the Commands using a RelativeSource Binding to navigate upwards in the Visual Tree and find the DataContext of the ItemsControl, where the Commands are actually defined.
  • When you need to add a new item, you just add a new item to the ObservableCollection that contains your data and WPF automatically creates the new UI elements bound to that.
  • Though this might seem like "too much code", most of the code I posted here is highly reusable and can be implemented in a Generic ViewModel<T> that is then reusable for any type of data items. Command and Command<T> are also write-once reusable classes that can be found in any MVVM framework such as Prism, MVVM Light or Caliburn.Micro.
  • This approach is really much preferred in WPF, because it enables a great amount of scalability and independence between the UI and the business logic, and it also enables testability of the ViewModel.
  • I suggest you read all the materials linked in the post, most importantly Rachel's WPF Mentality and related blog posts. Let me know if you need further help.
  • WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.
like image 170
Federico Berasategui Avatar answered Oct 23 '22 18:10

Federico Berasategui


It's actually much easier in behind code then in xaml code..

My Xaml code:

<Window x:Class="WpfAddGridWithStackPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid>
        <Grid x:Name="Grid_Grid" Margin="0,0,0,32">

        <Grid>
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <Grid x:Name="Grid_Grid" Margin="0,0,0,32"/>
    </ScrollViewer>
    <Button x:Name="btn_Add" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Add New Row" Click="btn_Add_Click" Width="150" HorizontalAlignment="Left" UseLayoutRounding="True" />
    <Button x:Name="btn_Remove" Height="32" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" Content="Remove last Row" Click="btn_Remove_Click" Width="150" HorizontalAlignment="Right" />
</Grid>
</Window>

And Code behind:

public partial class MainWindow : Window
    {
        int num = 0;
        public MainWindow()
        {
            InitializeComponent();
        }

        void btn1_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        void btn2_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        private void btn_Remove_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                Grid_Grid.RowDefinitions.RemoveAt(Grid_Grid.RowDefinitions.Count - 1);
                Grid_Grid.Children.RemoveAt(Grid_Grid.Children.Count - 1);
                num--;
            }
            catch { }
        }

        private void btn_Add_Click(object sender, RoutedEventArgs e)
        {
            StackPanel stack = new StackPanel();
            DockPanel dock = new DockPanel();
            Label lbl = new Label();
            Button btn1 = new Button();
            Button btn2 = new Button();
            TextBox txt1 = new TextBox();

            stack.Children.Add(dock);
            stack.Children.Add(txt1);
            dock.Children.Add(lbl);
            dock.Children.Add(btn2);
            dock.Children.Add(btn1);

            #region StackPanel Properties
            stack.Background = Brushes.LightGray;
            #endregion

            #region DockPanel Content Properties
            lbl.Content = "Label " + (num + 1).ToString();
            lbl.Height = 32;
            lbl.Width = 100;
            lbl.FontSize = 12;
            lbl.SetValue(DockPanel.DockProperty, Dock.Left);
            lbl.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            btn1.Content = "Butten 1";
            btn1.Height = 32;
            btn1.Width = 100;
            btn1.FontSize = 12;
            btn1.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            btn1.SetValue(DockPanel.DockProperty, Dock.Right);
            btn1.Click += new RoutedEventHandler(btn1_Click);

            btn2.Content = "Butten 2";
            btn2.Height = 32;
            btn2.Width = 100;
            btn2.FontSize = 12;
            btn2.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            btn2.SetValue(DockPanel.DockProperty, Dock.Right);
            btn2.Click += new RoutedEventHandler(btn2_Click);
            #endregion

            #region TextBox Properties
            txt1.Text = "Text " + (num + 1).ToString();
            txt1.Height = 32;
            txt1.Width = double.NaN;
            txt1.FontSize = 12;
            txt1.Padding = new Thickness(0, 7, 0, 7);
            #endregion

            Grid_Grid.RowDefinitions.Add(new RowDefinition());
            Grid_Grid.RowDefinitions[num].Height = new GridLength(66, GridUnitType.Pixel);
            Grid_Grid.Children.Add(stack);
            stack.SetValue(Grid.RowProperty, num);
            num++;
        }
    }
like image 8
ArchAngel Avatar answered Oct 23 '22 17:10

ArchAngel