0

I am trying to dynamically create DataGrids in WPF, one below the other in a Grid. the problem is that I don't even know where to start, I saw that it could be done through code but I would like to use XAML correctly.

void PopulateDatagridSQL(string Data, string index, string TabName)
{
   try
   {
      Data = Data.Replace((Char)6, (Char)124);

      List<DataTable> Results = new List<DataTable>();

      Results = JsonConvert.DeserializeObject<List<DataTable>>(Data);
      foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
      {
         if (SingleSQLWindows.MyINDEX == index)
         {
            SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
            {
               foreach (TabItem item in SingleSQLWindows._tabItems)
               {
                  if (item.Name == TabName)
                  {
                     //create multiple datagrids up to results.count
                     ((SQLPage)((Frame)item.Content).Content).DataGrid1.ItemsSource = Results[0].DefaultView;//foreach
                     ((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Records in Datagrid: " + Results[0].Rows.Count;//foreach
                  }
               }
            }));
         }
      }
   }
   catch (Exception asd)
   {

      foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
      {
         if (SingleSQLWindows.MyINDEX == index)
         {
            SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
            {
               foreach (TabItem item in SingleSQLWindows._tabItems)
               {
                  if (item.Name == TabName)
                  {
                     ((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Error in $SqlResponse";
                  }
               }
            }));
         }
      }
   }
}

In this function I receive a list of DataTables and for each DataTable I have to create a DataGrid.

<Page x:Class="Emergency_APP_Server_WPF.Forms.SQLPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:local="clr-namespace:Emergency_APP_Server_WPF.Forms"
  mc:Ignorable="d" x:Name="SQLPageXaml" Loaded="SQLPage_Loaded"
  d:DesignHeight="450" d:DesignWidth="800"
  Title="SQLPage" >

<Grid>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" MinHeight="100"></RowDefinition>
            <RowDefinition Height="0"></RowDefinition>
            <RowDefinition Height="*" MinHeight="150"></RowDefinition>
            <RowDefinition Height="35"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition Width="70"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <avalonEdit:TextEditor Grid.Column="0" Background="White"
            xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"  
            Name="MyAvalonEdit" FontFamily="Consolas"
            FontSize="11pt" Margin="10,10,10,10" ShowLineNumbers="True"
            LineNumbersForeground="Gray" ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.HorizontalScrollBarVisibility="Auto" KeyUp="MyAvalonEdit_KeyUp"/>
            <Button  Grid.Column="1" x:Name="BtnSendSQL" Margin="0,10,10,10" Click="BtnSendSQL_Click">
                <StackPanel>
                    <Image Height="25" Source="..//Resources/send.png"></Image>
                    <TextBlock Margin="0,10,0,0" VerticalAlignment="Top" Foreground="White" Text="SendSQL"></TextBlock>
                </StackPanel>
            </Button>
        </Grid>
        <GridSplitter Margin="0,-10,0,0" Grid.Row="1" HorizontalAlignment="Stretch" Background="Transparent" ResizeBehavior="PreviousAndNext">
        </GridSplitter>
        <DataGrid Grid.Row="2" x:Name="DataGrid1" RowStyle="{StaticResource DataGridRowSql}" Style="{StaticResource DataGridStyleSQL}" >
            <DataGrid.CommandBindings>
                <CommandBinding Command="Copy" Executed="CommandBinding_Executed"></CommandBinding>
            </DataGrid.CommandBindings>
            <DataGrid.InputBindings>
                <KeyBinding Key="C" Modifiers="Ctrl" Command="Copy"></KeyBinding>
            </DataGrid.InputBindings>
        </DataGrid>
        <TextBlock TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" Grid.Row="3" x:Name="TxtSqlLog" Text="Wait For Commands..."
                   FontSize="14" FontFamily="Consolas" Foreground="White"></TextBlock>
    </Grid>
</Grid>

2 Answers 2

1

I do not know the exact layout where you want to put all the DataGrids, so I just assume you want to place them in row 2 of your Grid. As you want to stack them, you can use an ItemsControl in a ScrollViewer. The latter is just there, so you can scroll through the DataGrids in case the page is too small to display them all. So this is the basic layout for the solutions below.

<Grid>
   <Grid.RowDefinitions>
      <!-- ...your row definitions. -->
   </Grid.RowDefinitions>
   <!-- ...your other controls. -->
   <ScrollViewer Grid.Row="2">
      <ItemsControl/>
   </ScrollViewer>
</Grid>

Code-behind solution

In code-behind, you need to access the ItemsControl, so assign it a name.

<ItemsControl x:Name="DataGridContainer"/>

Then just create DataGrids in a loop from your DataTable list.

foreach (var dataTable in Results)
{
   var dataGrid = new DataGrid { ItemsSource = dataTable.DefaultView };
   DataGridContainer.Items.Add(dataGrid);
};

By the way, in this scenario, you could also use a StackPanel instead of an ItemsControl.

MVVM solution

You can create a property in your view model that exposes the default views of your DataTables.

public ObservableCollection<DataView> DataViews { get; }

Make sure you instantiate the collection in the constructor or implement INotifyPropertyChanged, so that the collection is available in XAML. Then bind the ItemsControl to this collection.

<ItemsControl ItemsSource="{Binding DataViews}"/>

Next create a DataTemplate for the DataView type. As each data view in the bound collection represents a data table that should be displayed as a DataGrid, it would look like this.

<DataTemplate DataType="{x:Type data:DataView}">
   <DataGrid ItemsSource="{Binding}"/>
</DataTemplate>

Then you have to assign this data template as ItemTemplate in the ItemsControl. I just inline it here, but you can also put it in any ResourceDictionary and reference it via StaticResource.

<ItemsControl ItemsSource="{Binding DataViews}">
   <ItemsControl.ItemTemplate>
      <DataTemplate DataType="{x:Type data:DataView}">
         <DataGrid ItemsSource="{Binding}"/>
      </DataTemplate>
   </ItemsControl.ItemTemplate>
</ItemsControl>

Now, you just have to add the default views to the DataViews collection. As this collection is an ObservableCollection, it will notify the ItemsControl to update on each add.

foreach (var dataTable in Results)
{
   DataViews.Add(dataTable.DefaultView);
};

As an item is added, the ItemsControl will get notified and creates an item using the DataTemplate.

I recommend you to use the MVVM solution, as it separates the presentation from your data and is much easier to realize and customization of the DataGrids via the DataTemplate is much easier, convenient and maintainable in XAML.

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

1 Comment

i have a problem :(
0
<Page x:Class="Emergency_APP_Server_WPF.Forms.SQLPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
  xmlns:z="clr-namespace:System.Data;assembly=System.Data"
  xmlns:local="clr-namespace:Emergency_APP_Server_WPF.Forms"
  mc:Ignorable="d" x:Name="SQLPageXaml" Loaded="SQLPage_Loaded"
  d:DesignHeight="450" d:DesignWidth="800"
  Title="SQLPage" >

<Grid>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" MinHeight="100"></RowDefinition>
            <RowDefinition Height="0"></RowDefinition>
            <RowDefinition Height="*" MinHeight="150"></RowDefinition>
            <RowDefinition Height="35"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition Width="70"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <avalonEdit:TextEditor Grid.Column="0" Background="White"
            xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"  
            Name="MyAvalonEdit" FontFamily="Consolas"
            FontSize="11pt" Margin="10,10,10,10" ShowLineNumbers="True"
            LineNumbersForeground="Gray" ScrollViewer.VerticalScrollBarVisibility="Auto"
            ScrollViewer.HorizontalScrollBarVisibility="Auto" KeyUp="MyAvalonEdit_KeyUp"/>
            <Button  Grid.Column="1" x:Name="BtnSendSQL" Margin="0,10,10,10" Click="BtnSendSQL_Click">
                <StackPanel>
                    <Image Height="25" Source="..//Resources/send.png"></Image>
                    <TextBlock Margin="0,10,0,0" VerticalAlignment="Top" Foreground="White" Text="SendSQL"></TextBlock>
                </StackPanel>
            </Button>
        </Grid>
        <GridSplitter Margin="0,-10,0,0" Grid.Row="1" HorizontalAlignment="Stretch" Background="Transparent" ResizeBehavior="PreviousAndNext">
        </GridSplitter>

        <ScrollViewer Grid.Row="2">
            <ItemsControl ItemsSource="{Binding DataViews}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="{x:Type z:DataView}">
                        <DataGrid RowStyle="{StaticResource DataGridRowSql}" Style="{StaticResource DataGridStyleSQL}" ItemsSource="{Binding}">
                            <DataGrid.CommandBindings>
                                <CommandBinding Command="Copy" Executed="CommandBinding_Executed"></CommandBinding>
                            </DataGrid.CommandBindings>
                            <DataGrid.InputBindings>
                                <KeyBinding Key="C" Modifiers="Ctrl" Command="Copy"></KeyBinding>
                            </DataGrid.InputBindings>
                        </DataGrid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
        <TextBlock TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Bottom" Grid.Row="3" x:Name="TxtSqlLog" Text="Wait For Commands..."
                   FontSize="14" FontFamily="Consolas" Foreground="White"></TextBlock>
    </Grid>
</Grid>
 public ObservableCollection<DataView> _DataViews = new ObservableCollection<DataView>();
    public ObservableCollection<DataView> DataViews
    {
        get
        {
            return _DataViews;
        }
        set
        {
            _DataViews = value;
            OnPropertyChanged();
        }

    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string Param = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Param));
    }


foreach (SQLWindow SingleSQLWindows in StaticVar.MySQLWindows)
            {
                if (SingleSQLWindows.MyINDEX == index)
                {
                    SingleSQLWindows.Dispatcher.Invoke(new Action(() =>
                    {
                        foreach (TabItem item in SingleSQLWindows._tabItems)
                        {
                            if (item.Name == TabName)
                            {
                                foreach (DataTable itemTable in Results)
                                {
                                    //SingleSQLWindows.DataViews.Add(itemTable.DefaultView);

                                    ////create more datagrid while results.count
                                    ///
                                    ((SQLPage)((Frame)item.Content).Content).DataViews.Add(itemTable.DefaultView);
                                    //((SQLPage)((Frame)item.Content).Content).TxtSqlLog.Text = "Records in Datagrid: " + Results[0].Rows.Count;//foreach
                                }



                            }
                        }
                    }));
                }
            }

the visual studio does not report any errors but the scrollviewer remains empty, I cannot understand why

16 Comments

Did it work before or did you not get it working at all? Please check at runtime, if the DataViews collection has any items, e.g. add a breakpoint after you fill the list. Check your ouput if there are any binding errors. I am not sure if you add to the DataViews collection correctly in this line ((SQLPage)((Frame)item.Content).Content).DataViews.Add(itemTable.DefaultView);, it looks suspicious. Furthermore you apply styles which may cause an issue. Try without them, too.
never work , I thought it would work - i remove RowStyle="{StaticResource DataGridRowSql}" Style="{StaticResource DataGridStyleSQL}" but nothing....... and in this screen you can see DataViews count, i have also tryed to stop on return _DataViews; and it stop! then the code passes there. any suggestion? link to image DataView IMAGE
What about the output, does it show any errors like binding errors?
nothing , only empty, nothing errors :| see image IMAGE
Try to put only the ScrollViewer with its controls in the Grid or on the Page to see if it is not displayed due to a wrong layout. As there are no binding errors and the DataViews property is popuplated, it just does not seem to be displayed.
|

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.