Create multiple DataGrids in WPF dynamically - c#

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>

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.

<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

Related

WPF hide a row, but empty space doesn't sweep out

I have a wpf window with a grid. I would like to hide rows and I do with this code as shown in other stackoverflow threads:
buttonsGrid.Visibility = Visibility.Collapsed; or buttonsGrid.Height = 0;
dayTimePicker.Visibility = Visibility.Collapsed; or dayTimePicker.Height = 0;
Set Visibility to Collapsed or Height to 0 gives the same effect, but I would like the white space occupied by these Rows to sweep ... I would like the whole window to be occupied by the indexed row 0. .. How can I do it?
This is my window:
<Window x:Class="PerformanceVideoRec.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:DTControl="clr-namespace:DayTimePickControl"
Title="{StaticResource ID_TITLE}" Height="600" Width="900" Loaded="Window_Loaded"> <!--Closing="Window_Closing"> -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1" x:Name="ControlPart" IsEnabled="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" x:Name="LeftGrid">
<Grid.RowDefinitions>
<RowDefinition Height="55*"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<WindowsFormsHost Grid.Row="0" Margin="5" Background="Gray">
<wf:PictureBox x:Name="playWindow"></wf:PictureBox>
</WindowsFormsHost>
<UniformGrid Grid.Row="1">
<Button x:Name="btnShowHideControls" Content="Nascondi Controlli" Click="btnShowHideControls_Click"/>
</UniformGrid>
<UniformGrid Columns="6" Grid.Row="2" x:Name="buttonsGrid">
<Button x:Name="btnPause" Margin="5" Width="50" Content="{StaticResource ID_PAUSE}" Click="btnPause_Click"></Button>
<Button x:Name="btnPlay" Margin="5" Width="50" Content="{StaticResource ID_PLAY}" Click="btnPlay_Click" Visibility="Collapsed"/>
<Button x:Name="btnStop" Margin="5" Width="50" Content="{StaticResource ID_STOP}" Click="btnStop_Click"></Button>
<Button x:Name="btnSlow" Margin="5" Width="50" Content="{StaticResource ID_SLOW}" Click="btnSlow_Click"></Button>
<TextBox x:Name="tbSpeed" IsEnabled="False" Width="50" Height="25" TextAlignment="Center" VerticalContentAlignment="Center" Text="1X" />
<Button x:Name="btnFast" Margin="5" Width="50" Content="{StaticResource ID_FAST}" Click="btnFast_Click"></Button>
<Button x:Name="btnNormal" Margin="5" Width="50" Content="{StaticResource ID_NORMAL}" Click="btnNormal_Click"></Button>
</UniformGrid>
<DTControl:DayTimePick x:Name="dayTimePicker" Grid.Row="3" Width="550" Height="100" Grid.RowSpan="2" OnTimeClick="dayTimePicker_OnTimeClick"></DTControl:DayTimePick>
</Grid>
<Frame x:Name="PlayBackFrame" Grid.Column="1" Background="AliceBlue" ></Frame>
</Grid>
</Grid>
The MVVM way:
make a separate view model for the part of UI you want to collapse. Let's call it PlayViewModel,
make a DataTemplate for it,
expose it as a property (e.g. let's call it Play, don't forget to raise property change event) in the view model which is a DataContext for the whole view
you display it in your grid with ContentPresenter, ContentPresenter.Content bound to Play property
you do hiding by null-ing Play property, you show it by restoring it.
You get testability for free.
You could use Data Binding and bind the the property Visibility of the control inside the row you want to collapse with a converter.
I usually use the NuGet package Mvvm Light, documentation to have some facilities while using Data Binding. (In this case RelayCommand, RaisePropertyChanged implementation)
A very basic example:
Part of the view. Here you can notice the Binding between the Visibility property of the button and the property IsButton1Visible of the ViewModel. Of course bool and Visibility aren't the same type, so I had to create a mapping using an IValueConverter:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="1" Visibility="{Binding IsButton1Visible, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Button Grid.Row="1" Content="2" />
<Button Grid.Row="2" Content="3" />
<Button Grid.Row="3" Content="4" />
<Button Grid.Row="4" Content="Toggle button 1 visibility" Command="{Binding ToggleButton1Visibility}" />
</Grid>
The window's constructor in the code behind (you could also bind the ViewModel via the DataContext property directly into the View), used to associate the View with the ViewModel:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyWindowViewModel();
}
The converter to convert true to Visibility.Visible:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (bool)value ? Visibility.Visible : Visibility.Collapsed;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
App.xaml, Application.Resource part, used to make the View "see" the converter:
<Application.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Application.Resources>
The ViewModel. Here RaisePropertyChanged of set method of IsButton1Visible is very important because it notifies the View that the property is changed, so that the View can refresh itself:
public class MyWindowViewModel : ViewModelBase
{
private bool _isButton1Visibile = true;
public bool IsButton1Visible
{
get => _isButton1Visibile;
set
{
if (_isButton1Visibile == value)
return;
_isButton1Visibile = value;
RaisePropertyChanged(nameof(IsButton1Visible));
}
}
RelayCommand _toggleButton1Visbility;
public RelayCommand ToggleButton1Visibility
{
get
{
return _toggleButton1Visbility ?? (_toggleButton1Visbility = new RelayCommand(
() =>
{
IsButton1Visible = !IsButton1Visible;
}));
}
}
}
LeftGrid.RowDefinitions[2].Height = new GridLength(0);
LeftGrid.RowDefinitions[3].Height = new GridLength(0);
If you want to restore make this
LeftGrid.RowDefinitions[2].Height = new GridLength(5, GridUnitType.Star);
LeftGrid.RowDefinitions[3].Height = new GridLength(20, GridUnitType.Star);

ListBox Selected Items are reset each time I switch tabs in may TabControl

I have the following TabControl:
<TabControl Name="tabControl" Grid.Row="0" MinWidth="270" HorizontalAlignment="Stretch" ItemsSource="{Binding Counters}" ContentTemplate="{StaticResource templateForTheContent}"
ItemTemplate="{StaticResource templateForTheHeader}">
</TabControl>
It uses this DataTemplate:
<Window.Resources>
<DataTemplate x:Key="templateForTheContent" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"
ItemsSource="{Binding}"
SelectionMode="Multiple"
BorderThickness="1" BorderBrush="#FF8B8B8B" SelectionChanged="ListBox_SelectionChanged_1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding CounterName, Mode=OneWay}" />
<Run Text="{Binding InstanceName, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Name="RAMSelectAllButton" Margin="0,10,0,0" Grid.Column="0" Grid.Row="1">
<TextBlock Text="SELECT ALL"/>
</Button>
<Button Name="RAMUnSelectAllButton" Margin="0,10,0,0" Grid.Column="1" Grid.Row="1">
<TextBlock Text="UNSELECT ALL"/>
</Button>
</Grid>
</DataTemplate>
<DataTemplate x:Key="templateForTheHeader" >
<TextBlock Text="{Binding CategoryName}"/>
</DataTemplate>
</Window.Resources>
It works as expected, binding works well, everything would be totally fine if this issue wasn't present:
Each time I switch a tab in my TabControl, selected items of a ListBox in my previous tab are reset - so when I go back to that tab - nothing is selected.
How to fix this?
//EDIT
here's my ListBox_SelectionChanged_1 method:
private void ListBox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
System.Windows.Controls.ListBox listBoxTemp = sender as System.Windows.Controls.ListBox;
PerformanceCounter counterTemp = (PerformanceCounter)listBoxTemp.Items[0];
if (!appData.SelectedCounters.ContainsKey(counterTemp.CategoryName))
appData.SelectedCounters.Add(counterTemp.CategoryName, new List<PerformanceCounter>());
appData.SelectedCounters[counterTemp.CategoryName].Clear();
foreach (PerformanceCounter counter in listBoxTemp.SelectedItems)
{
appData.SelectedCounters[counterTemp.CategoryName].Add(counter);
}
}
ListBox is bound to Counters, which is Observable Collection:
public ObservableCollection<ObservableCollection<PerformanceCounter>> Counters
{
get { return _Counters; }
}
ObservableCollection<ObservableCollection<PerformanceCounter>> _Counters = new ObservableCollection<ObservableCollection<PerformanceCounter>>();
(i am not familiar with TabControls, or WPF for that matter, but i would suggest a solution like this:)
Backup the selected items of your ListBox in a Dictionary.
var selectionBackups = new Dictionary<ListBox, IEnumerable<ListBoxItem>>();
I'd keep this field updated in ListBox_SelectionChanged_1(). Whenever you enter a Tab, overwrite the concerned ListBox's selection.
void TabEnter(object sender, TabEventArgs e)
{
ListBox lb = ... //acquire the current Listbox
OverwriteSelection(lb, selectionBackups[lb]); //set the ListBox's selection
}
(You might want to prevent the ListBox_SelectionChanged_1() from triggering when you restore the selection. That could be done like this:
bool auto_select = false; //set this to true while editing selections
private void ListBox_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
if(auto_select)
return;
...
}
)

How to assign grid row and column index dynamically

I am trying to add user controls to the Grid and I am trying to set the Grid.Column and Grid.Row property in the DataTemplate but it does not have any effect on the rendering. Can someone help to point out what could be wrong in the code below.
I have the main window with the following code:
<ItemsControl ItemsSource="{Binding Controls}">
<ItemsControl.ItemsPanel >
<ItemsPanelTemplate>
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<AppControls:TagInfo Grid.Row="{Binding RowIndex}"
Grid.Column="{Binding ColumnIndex}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
TagInfo.xaml.cs
public partial class TagInfo : UserControl
{
public TagInfo()
{
InitializeComponent();
}
public int RowIndex { get; set; }
public int ColumnIndex { get; set; }
}
TagInfo.xaml
<UserControl x:Class="DataSimulator.WPF.Controls.TagInfo"
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"
mc:Ignorable="d" Height="258" Width="302"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid Margin="2,2,2,2">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Tag"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TagInfo.TagName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="High High"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding TagInfo.HighHigh}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="High"/>
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding TagInfo.High}"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Low Low"/>
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding TagInfo.LowLow}"/>
<TextBlock Grid.Row="4" Grid.Column="0" Text="Low"/>
<TextBox Grid.Row="4" Grid.Column="1" Text="{Binding TagInfo.Low}"/>
<TextBlock Grid.Row="5" Grid.Column="0" Text="Range Start"/>
<TextBox Grid.Row="5" Grid.Column="1" Text="{Binding TagInfo.RangeStart}"/>
<TextBlock Grid.Row="6" Grid.Column="0" Text="Range End"/>
<TextBox Grid.Row="6" Grid.Column="1" Text="{Binding TagInfo.RangeEnd}"/>
<Button Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Content="Save"/>
</Grid>
</UserControl>
ViewModel
private ObservableCollection<Controls.TagInfo> _controls
= new ObservableCollection<Controls.TagInfo>();
public ObservableCollection<Controls.TagInfo> Controls
{
get { return _controls; }
set { _controls = value; }
}
private void AddControl()
{
if(currentRow == 3)
{
currentRow = 0;
}
if(currentColumn == 3)
{
currentColumn = 0;
currentRow++;
}
var tagInfoUserControl = new Controls.TagInfo();
tagInfoUserControl.RowIndex = currentRow;
tagInfoUserControl.ColumnIndex = currentColumn++;
_controls.Add(tagInfoUserControl);
}
I am trying to add user controls to the grid and I am trying to set the Grid.Column and Grid.Row property in the data template but it does not have any effect on the rendering
We can't do the attached property binding in a DataTemplate, creating a style for your UserControl in ItemsControl.ItemContainerStyle can make it work:
<ItemsControl ItemsSource="{Binding Controls}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="AppControls:TagInfo">
<Setter Property="Grid.Row" Value="{Binding RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
Screenshot:
when all columns and rows have to be of equal size and elements are consequtive, it is probably simpler to use UniformGrid. Rows and Columns properties support binding.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Name="MainGrid" Rows="4" Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
here is a nice example of Grid ItemsPanel here: WPF Grid as ItemsPanel for a list dynamically bound to an ItemsControl

Issues creating a flipview on ItemDetailPage

I am trying to create a flipview on the an ItemDetailpage. I am using the default template provided in visual studio when creating a grid app.
Pages in the App: GroupItemsPage, GroupDetailPage, ItemDetailPage
The problem is, I am getting this error when I click on an item on the GroupItemsPage or GroupDetailPage:
Object reference not set to an instance of an object.
Error details:
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=AppError
StackTrace: at AppError.ItemDetailPage.<navigationHelper_LoadState>d__0.MoveNext()
This is my code:
GroupItemsPage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.GroupedItemsPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<x:String x:Key="ChevronGlyph"></x:String>
<!--
Collection of grouped items displayed by this page, bound to a subset
of the complete item list because items in groups cannot be virtualized
-->
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="Items"
d:Source="{Binding Groups, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="0,0,0,2">
<Button Foreground="{ThemeResource ApplicationHeaderForegroundThemeBrush}"
AutomationProperties.Name="Group Title"
Click="Header_Click"
Style="{StaticResource TextBlockButtonStyle}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Margin="0,-11,10,10" Style="{StaticResource SubheaderTextBlockStyle}" TextWrapping="NoWrap" />
<TextBlock Text="{StaticResource ChevronGlyph}" FontFamily="Segoe UI Symbol" Margin="0,-11,0,10" Style="{StaticResource SubheaderTextBlockStyle}" TextWrapping="NoWrap" />
</StackPanel>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{StaticResource AppName}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
GroupItemsPage.xaml.cs
namespace AppError
{
public sealed partial class GroupedItemsPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public GroupedItemsPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
}
void Header_Click(object sender, RoutedEventArgs e)
{
// Determine what group the Button instance represents
var group = (sender as FrameworkElement).DataContext;
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
this.Frame.Navigate(typeof(GroupDetailPage), ((SampleDataGroup)group).UniqueId);
}
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
}
GroupDetailpage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.GroupDetailPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<!-- Collection of items displayed by this page -->
<CollectionViewSource
x:Name="itemsViewSource"
Source="{Binding Items}"
d:Source="{Binding Groups[0].Items, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding Group}"
d:DataContext="{Binding Groups[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Horizontal scrolling grid -->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Items In Group"
TabIndex="1"
Grid.RowSpan="2"
Padding="120,126,120,50"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="110" Width="480" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap"/>
<TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextBlockStyle}" MaxHeight="60"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.Header>
<StackPanel Width="480" Margin="0,4,14,0">
<TextBlock Text="{Binding Subtitle}" Margin="0,0,0,20" Style="{StaticResource SubheaderTextBlockStyle}" MaxHeight="60"/>
<Image Source="{Binding ImagePath}" Height="400" Margin="0,0,0,20" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,0,0" Style="{StaticResource BodyTextBlockStyle}"/>
</StackPanel>
</GridView.Header>
<GridView.ItemContainerStyle>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="52,0,0,2"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
GroupDetailPage.xaml.cs
namespace AppError
{
public sealed partial class GroupDetailPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public GroupDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var group = await SampleDataSource.GetGroupAsync((String)e.NavigationParameter);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Items"] = group.Items;
}
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
}
ItemDetailPage.xaml
<Page
x:Name="pageRoot"
x:Class="AppError.ItemDetailPage"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppError"
xmlns:data="using:AppError.Data"
xmlns:common="using:AppError.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="itemViewSource"
Source="{Binding Items}"
d:Source="{Binding Groups[0].Items, Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"/>
</Page.Resources>
<!--
This grid acts as a root panel for the page that defines two rows:
* Row 0 contains the back button and page title
* Row 1 contains the rest of the page layout
-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
DataContext="{Binding Item}"
d:DataContext="{Binding Groups[0].Items[0], Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--
TODO: Content should be placed within the following grid
to show details for the current item
-->
<FlipView
Grid.Row="1"
x:Name="flipView"
Margin="50,0,0,0"
ItemsSource="{Binding Source={StaticResource itemViewSource}}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="500"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel>
<Border>
<Image Source="{Binding ImagePath}"/>
</Border>
<TextBlock Text="{Binding Description}" Padding="0,30,0,0" TextWrapping="Wrap"/>
</StackPanel>
<Grid Grid.Column="1" Margin="30,0,0,0">
<TextBlock Text="{Binding Content}" TextWrapping="Wrap"/>
</Grid>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0" Command="{Binding NavigationHelper.GoBackCommand, ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"/>
<TextBlock x:Name="pageTitle" Text="{Binding Title}" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40"/>
</Grid>
</Grid>
ItemDetailPage.xaml.cs
namespace AppError
{
public sealed partial class ItemDetailPage : Page
{
private NavigationHelper navigationHelper;
private ObservableDictionary defaultViewModel = new ObservableDictionary();
public NavigationHelper NavigationHelper
{
get { return this.navigationHelper; }
}
public ObservableDictionary DefaultViewModel
{
get { return this.defaultViewModel; }
}
public ItemDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var group = await SampleDataSource.GetGroupAsync((String)e.NavigationParameter);
var item = await SampleDataSource.GetItemAsync((String)e.NavigationParameter);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Items"] = group.Items;
this.flipView.SelectedItem = item;
}
}

Adding multiple stack panels through C# code

I have a mom with multiple children. Each child has a name already assigned and needs user input for the value.
I am trying to generate a StackPanel with a TextBlock and TextBox for each child through code. The number of children are unknown so I need a loop to create each StackPanel and them to a "mom" StackPanel which is added to a Border control. When I run the code it runs through the loop fine but it does not show anything in the display.
Here is the code:
var momStackPanel = new StackPanel() { Orientation = Windows.UI.Xaml.Controls.Orientation.Vertical};
foreach (ChildItem item in ChildList)
{
var childItemSP = new StackPanel();childItemSP.Orientation = Orientation.Horizontal;
TextBlock nameTB = new TextBlock();
name.Text = item.Name;
childItemSP.Children.Add(nameTB);
TextBox valueTB = new TextBox();
valueTB .Text = item.Value;
childItemSP.Children.Add(valueTB);
}
BorderSection.Child = momStackPanel;
BorderSection.Visibility = Windows.UI.Xaml.Visibility.Visible;
Here is the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="t4" Text="t4" Grid.Row="0"/>
<Border Grid.Row="1" HorizontalAlignment="Stretch">
<StackPanel >
<TextBlock x:Name="r1" Text="{Binding r1}" HorizontalAlignment="Center" />
<TextBlock x:Name="r2" Text="r2" Foreground="DarkGray"/>
</StackPanel>
</Border>
<Border x:Name="BorderSection" Grid.Row="2">
</Border>
<StackPanel x:Name="CommentPanel" Orientation="Vertical" Grid.Row="3" Margin="6,6,6,6">
<TextBox x:Name="Comment" Text="" MinHeight="100"/>
</StackPanel>
<StackPanel Orientation="Horizontal" >
<Button x:Name="Submit" Content="Submit" Click="Submit_Click_1"/>
<Button x:Name="Cancel" Content="Cancel" Click="Cancel_Click_1"/>
</StackPanel>
</Grid>
Any ideas how to deal with this?
The ItemsControl control acts like a StackPanel with a variable number of items. You can bind it to an ObservableCollection, so you don't have to mess with the view at all in the code-behind.
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.Resources>
<ResourceDictionary Source="ChildTemplate.xaml"/>
</ItemsControl.Resources>
</ItemsControl>
Then you can provide the child's view in a separate file (ChildTemplate.xaml).
<DataTemplate DataType="{x:Type myNamespace:ChildViewModel}">
<StackPanel >
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
Don't forget you will have to call OnPropertyChanged for the ObservableCollection whenever you change it.

Categories

Resources