[Edit #3] - to anyone reading this question: do not under any circumstance use the approach outlined in this question. It is a Coding Horror. I freely admit this, knowing that all programmers have worked themselves into a corner in the past, and (especially when learning a new technology) we all have been led astray by other, well-meaning developers on the interweb. Read the answer by Robert first, then read this question. Please.
[Edit #2b]
I apologize for the length of this question - there is a question in here (at the end!), but I wanted to make sure the source code was explicit. Anyway.
[Edit #2] - question title changed to more accurately reflect the... question.
[Edit] - I've updated some more of the history as to how I ended up at the design / code that I did here: Obligatory Blog Post. If it helps clarify the question below, feel free to read it...
Original question
The application I'm working on uses Prism and WPF, with a number of modules (currently 3), one of which hosts the application menu. Originally, the menu was static with hooks into CompositeCommand / DelegateCommands, which worked great for routing button presses to the appropriate presenter. Each MenuItem used a StackPanel in its header to display the content as a combination of an image and a text label - which was the look I was going for:
<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent">
<MenuItem Name="MenuFile" AutomationProperties.AutomationId="File">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/066.png"/>
<ContentPresenter Content="Main"/>
</StackPanel>
</MenuItem.Header>
<MenuItem AutomationProperties.AutomationId="FileExit" Command="{x:Static local:ToolBarCommands.FileExit}">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/002.png"/>
<ContentPresenter Content="Exit"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</MenuItem>
<MenuItem Name="MenuHelp" AutomationProperties.AutomationId="Help" Command="{x:Static local:ToolBarCommands.Help}">
<MenuItem.Header>
<StackPanel>
<Image Height="24" VerticalAlignment="Center" Source="../Resources/152.png"/>
<ContentPresenter Content="Help"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
Unfortunately, the application has gotten a bit more complex and it is desireable to have other modules register themselves with the menu - hence, I've been looking at making the menu dynamic. The goal is to have other modules (through a service) be able to add commands to the menu at will - for example, Module A will add a menu item in the Toolbar module that calls a handler in Module A. There's a few excellent articles out there on this subject - the two I've looked at are Building a Databound WPF Menu Using a HierarchicalDataTemplate and WPF Sample Series - Databound HierarchicalDataTemplate Menu Sample. Following the advice in the article, I have managed to make a dynamically constructed menu with no obvious data binding problems - it can create a menu with items linked backed to my presentation model, reflecting the structure of an ObservableCollection in the presentation model
Currently, my XAML looks like the following:
<UserControl x:Class="Modules.ToolBar.Views.ToolBarView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:Modules.ToolBar.PresentationModels"
xmlns:local="clr-namespace:Modules.ToolBar">
<UserControl.Resources>
<model:ToolBarPresentationModel x:Key="modelData" />
<HierarchicalDataTemplate DataType="{x:Type model:ToolbarObject}"
ItemsSource="{Binding Path=Children}">
<ContentPresenter Content="{Binding Path=Name}"
Loaded="ContentPresenter_Loaded"
RecognizesAccessKey="True"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource modelData}"/>
</UserControl.DataContext>
<Menu Height="48" Margin="5,0,5,0" Name="MainMenu" VerticalAlignment="Top" Background="Transparent"
ItemsSource="{Binding}">
</Menu>
</Grid>
</UserControl>
The code behind for the view does the heavy lifting in the ContentPresenter_Loaded method:
private void ContentPresenter_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
ContentPresenter presenter = sender as ContentPresenter;
if (sender != null)
{
DependencyObject parentObject = VisualTreeHelper.GetParent(presenter);
bool bContinue = true;
while (bContinue
|| parentObject == null)
{
if (parentObject is MenuItem)
bContinue = false;
else
parentObject = VisualTreeHelper.GetParent(parentObject);
}
var menuItem = parentObject as MenuItem;
if (menuItem != null)
{
ToolbarObject toolbarObject = menuItem.DataContext as ToolbarObject;
StackPanel panel = new StackPanel();
if (!String.IsNullOrEmpty(toolbarObject.ImageLocation))
{
Image image = new Image();
image.Height = 24;
image.VerticalAlignment = System.Windows.VerticalAlignment.Center;
Binding sourceBinding = new Binding("ImageLocation");
sourceBinding.Mode = BindingMode.TwoWay;
sourceBinding.Source = toolbarObject;
image.SetBinding(Image.SourceProperty, sourceBinding);
panel.Children.Add(image);
}
ContentPresenter contentPresenter = new ContentPresenter();
Binding contentBinding = new Binding("Name");
contentBinding.Mode = BindingMode.TwoWay;
contentBinding.Source = toolbarObject;
contentPresenter.SetBinding(ContentPresenter.ContentProperty, contentBinding);
panel.Children.Add(contentPresenter);
menuItem.Header = panel;
Binding commandBinding = new Binding("Command");
commandBinding.Mode = BindingMode.TwoWay;
commandBinding.Source = toolbarObject;
menuItem.SetBinding(MenuItem.CommandProperty, commandBinding);
}
}
}
As you can see, I'm attempting to recreate the StackPanel / Image / Name combination of the original menu, just doing so in the code behind. Attempting to do this has not worked out so well - while the menu objects are certainly being created, they don't "appear" as anything other than blank, clickable objects - the StackPanel, Image, Name, etc. aren't being rendered. Interestingly enough, it also is causing the original text in the ContentPresent in the HierarchicalDataTemplate to be erased.
The question then, is there a way to set a MenuItem's Header property in the Load event such that it will display on the UserControl properly? Is the fact that the items in the header are not being displayed indicative of a DataBinding problem? If so, what would be the proper way to bind the Header to a transient object (the StackPanel that was created in the load event handler)?
I'm open to changing anything in the code above - this is all sort of prototyping along, trying to figure out the best way to handle dynamic menu creation.
Thanks!
I'll confess that I haven't dug quite as deep into your example as maybe I should, but whenever I see code-behind that's searching the visual tree, I think, could this be handled more explicitly in a view model?
It seems to me in this case that you could come up with a pretty straightforward view model - an object exposing Text, Image, Command, and Children properties, for instance - and then create a simple data template that for presenting it as a MenuItem. Then anything that needs to alter the contents of your menus manipulates this model.
Edit:
Having looked at what you're up to in more detail, and the two examples you've linked to in your blog post, I am banging my head against the desk. Both of those developers appear to be under the misapprehension that the way to set properties on the menu items that are being generated by the template is to search through the visual tree in the ContentPresenter.Load event after they're created. Not so. That's is what the ItemContainerStyle is for.
If you use that, it's quite straightforward to create dynamic menus of the type you're describing. You need a MenuItemViewModel class that has INotifyPropertyChanged implemented and exposes these public properties:
string Text
Uri ImageSource
ICommand Command
ObservableCollection<MenuItemViewModel> Children
Using this:
<Menu DockPanel.Dock="Top" ItemsSource="{DynamicResource Menu}"/>
where the ItemsSource is an ObservableCollection<MenuItemViewModel>, and using this template:
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}"
ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding Command}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" />
<Label Content="{Binding Text}" />
</StackPanel>
</HierarchicalDataTemplate>
the menus in the window exactly represent what's in the collection, and are dynamically updated as items are added and removed, both to the top-level items and to the descendants.
There's no clambering about in the visual tree, no manual creation of objects, no code-behind (other than in the view model, and in whatever populates the collection in the first place).
I've built a pretty thoroughly worked example of this; you can download the project here.
Another possible approach could be having the Menu be a region and agree on a convention so all views added to that region have a ViewModel with a property named MenuHeader. That way, the region adapter can simply get the menu header from the View's Data Context, and set it to the item when adding it.
Something similar is done in Prism with views added to a Tab Region. You can read more here.
I hope this provides some useful guidance.
Thanks,
Damian
Related
Example XAML to be loaded dynamically
<Grid xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:usercontrols='clr-namespace:App.Views.UserControls'>
<TextBlock>Why don't you click the button?</TextBlock>
<usercontrols:SuperButton
Command="{Binding DataContext.OpenURLNew,RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type ContentPresenter}}"
CommandParameter="50">
ClickMe</usercontrols:SuperButton>
</Grid>
Loading this fails with 'can not load unknown type usercontrols:superbutton', despite the fact that SuperButton is defined in the same assembly.
I'm guessing this is because SuperButton has associated code-behind? Is there a way to help XamlReader.Load() find what it needs?
What you're doing should work - try using the fully qualified assembly name in xmlns:usercontrols=''.
I did this exact thing a while back (perhaps the netGooey library I wrote might be useful for you). netGooey loads XAML dynamically into the page with support for user-defined controls.
My XAML header looked like this:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="499" Height="579"
xmlns:playback="clr-namespace:inlayShared.ui.controls.playback;assembly=inlayShared"
xmlns:library="clr-namespace:inlayShared.ui.controls.library;assembly=inlayShared">
Control use like:
<playback:volumeSlider Maximum="100" Minimum="0" Margin="42,180,62,0" Height="30" VerticalAlignment="Top" TickFrequency="10" TickPlacement="BottomRight" />
And the dynamic XAML loading like this:
_gSystem.invokeOnLocalThread((Action)(() =>
{
FileStream fileStream = File.OpenRead(_sUIFile);
DependencyObject dependencyObject = XamlReader.Load(fileStream) as DependencyObject;
fileStream.Close();
if (dependencyObject == null)
return;
Content = dependencyObject;
}), true);`
Perhaps I forgot some key part to getting XAML to notice the custom controls but I'm pretty sure it just ended up working.
Good luck. (Hopefully the fully qualified change fixes it for you)
http://inlay.codeplex.com/SourceControl/changeset/view/42822#549758
Here's the question at its most basic: how do I listen for an update of what is changing in a TreeView control modified via a DragDropTarget?
So here's my deal: I have a TreeView that holds agenda items. All are of the same data type (WCFAgendaItem), and are loaded into a hierarchy with children expressed as a property ChildItems. The whole thing is wrapped up in an ObservableCollection and bound to the TreeView using MVVM Light. Works great to view. I also want users to be able to use drag and drop to reorder, reorganize and add new items to this agenda coming from a variety of other sources (one example is a ListView of image slides). All new items would also have the same data type of WCFAgendaItem, for consistency's sake and easy serialization.
Here's my issue: dragging and dropping works beautifully on the UI using the Toolkit's drag drop functionality. But I have no idea how to get the ViewModel to understand changes to the contents of the TreeView.
Code from the view (Agenda.xaml):
(up top)
<UserControl.Resources>
<AHHSTeam_SLClassroomManagerMVVM_Helpers_Converters:BooleanVisibilityConverter x:Key="BooleanVisibilityConverter"/>
<sdk:HierarchicalDataTemplate x:Key="hdtAgenda" ItemsSource="{Binding ChildItems, Mode=TwoWay}" >
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ImageThumbnailWidth}" />
<ColumnDefinition Width="250" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding ThumbnailURL}" Width="{Binding ImageThumbnailWidth}" Height="{Binding ImageThumbnailHeight}" Visibility="{Binding HasImage, Converter={StaticResource BooleanVisibilityConverter}}" >
<ToolTipService.ToolTip>
<Image Source="{Binding ResizedImageURL}" />
</ToolTipService.ToolTip>
</Image>
<TextBlock Grid.Column="1" Text="{Binding Title}" TextWrapping="Wrap" />
</Grid>
</sdk:HierarchicalDataTemplate>
<Style TargetType="sdk:TreeViewItem" >
<Setter Property="IsExpanded" Value="True" />
</Style>
</UserControl.Resources>
(later on)
<controlsToolkit:TreeViewDragDropTarget Grid.Row="1" Grid.Column="0" x:Name="ddtAgenda" AllowDrop="True"
HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<sdk:TreeView Width="375" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Visible" ItemsSource="{Binding DailyAgenda, Mode=TwoWay}" ItemTemplate="{StaticResource hdtAgenda}">
</sdk:TreeView>
</controlsToolkit:TreeViewDragDropTarget>
ViewModel code (AgendaViewModel.cs) --> I tried listening for CollectionChanged, so far that doesn't seem to work
(in constructor)
//add notification of agenda changes
DailyAgenda.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(DailyAgenda_CollectionChanged);
(event)
void DailyAgenda_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
System.Windows.MessageBox.Show("Daily agenda updated, now has " + e.NewItems.Count.ToString() + " top-level elements.");
}
Code from model (WCFAgendaItem.cs)
[ContentProperty("ChildItems")]
public partial class WCFAgendaItem: INotifyPropertyChanged
{
private ObservableCollection<WCFAgendaItem> _childItems = new ObservableCollection<WCFAgendaItem>();
public ObservableCollection<WCFAgendaItem> ChildItems
{
get
{
return _childItems;
}
set
{
_childItems = value;
}
}
...
I am pretty sure that I get that listening for CollectionChanged isn't right in any case, given that this data doesn't just change at the top level. I looked at EventToCommand in Blend (MVVM Light, remember) but the only TreeView-specific event appears to be SelectionChanged, which doesn't seem right either. I looked at putting an EventToCommand trigger on the TreeViewDragDropTarget, but aren't those methods about overriding how the UI interactions happen? I don't think INotifyPropertyChanged on WCFAgendaItem is right for this either: although I'm going to want that later for editing item titles, it doesn't seem like it'll help me when items get moved around.
Maybe what I'm looking for is a stretch, but what I really want to have happen is for Silverlight to understand that the databinding works both ways on the ordering and contents of the WCFAgendaItem collection, and do all the collection reworking itself based on UI interactions. Then I could just listen for an update event after the collection is reworked - after that I can just crawl the modified ObservableCollection bound to the TreeView, and flatten/serialize/update via WCF.
Failing the ideal situation: I'm willing to crawl TreeViewItems if need be, but even if that's what I need to do I'm stuck on when to do it. Plus I need a way to pass all that back to the ViewModel so I'm not writing code behind. Do I need to attach to Drop() and rework the dropping logic? I found several old articles about custom drag drop implementations starting from the Toolkit, but nobody mentions how to save out the modified TreeView, especially in an MVVM situation.
finally {
While typing this out I found this article which may be useful, though that's a fair amount of work in the ViewModel. This is promising and I'll investigate, but I'm still holding out hope for something simpler. Also it looks like the Toolkit events have changed a little since the article was written.
}
I also had issues implementing this type of DragDrop functionality. The root cause seemed to be that neither the ItemDragCompleted event (EventHandler) nor ItemDroppedOnSource (DragEventHandler) pass the index at which the item was dropped.
I ended up subclassing the DragDropTarget in order to expose the protected method:
int GetDropTargetInsertionIndex(TItemsControlType dropTarget, DragEventArgs args)
I then used an attached behavior to assume responsibility for inserting items at the specified index into to the underlying collections.
I'm afraid the underlying code is too expansive to include in a StackOverflow answer (mainly due to extensive decoupling) but it's on my list of subjects to blog about. In the mean time, I hope the information above helps, it was certainly the key to the solution for me.
Ian
For this question, let us assume that we will want to show the face of the employee, title, department, and whether they like PiƱa coladas/getting caught in the rain.
Perhaps it might look something like the following:
http://www.edrawsoft.com/images/examples/Photo-Org-Chart-Full.png
Would you use a...
System.Windows.Control.UserControl?
FrameworkElement?
UIElement?
Canvas
Why? As always, thank you for your advise! I greatly appreciate it!
If I had to create a org chart control with advanced layout I would probably derive from Control, and create a "real" templated control in a similar manner as e.g. the TreeView control. This is probably the most advanced route to create a new control, but also the most powerful.
You may also be able to modify the control template of a TreeView, and make it grow downwards from the center instead of left and down from the upper left corner, but it will probably be difficult or impossible to customize the layout of the various levels as the TreeViewItem doesn't carry any extra information to describe the layout of a particular node.
In fact I did recently some experiments modifying the TreeView control template, but I stumbled upon something I didn't understand. Luckily I figured out what I did wrong, and you can see how it is possible to change the orientation of TreeView child items from vertical to horizontal in my question here on Stack Overflow.
I've seen a website that uses TreeViewItem and ControlTemplates, but I can't find it at the moment, I think it was on CodeProject.
Another idea I was playing with recently is use 2 usercontrols, itemcontrols and stackpanels.
Here's an example of a an OrgBar rectangle with text under it and it renders it's children in OrgGroup control by setting the ItemSource to it's children collection recursively. You can put the root orgbar on a canvas and play around with paths for the arrows. I tried to point out the basics but if you need more I can fill in the blanks.
Public Class OrgBarDataNode
Public Property BarColor as New SolidColorBrush(Colors.Red)
Public Property BarName As String
Public Property Children as New ObservableCollection(Of OrgBarDataNode)
End Class
Class MainPage
...
Public Sub Loaded
Dim Root as New OrgBarDataNode With {.BarName = "Root"}
Dim Child1 as New OrgBarDataNode With {.Barname = "Child1"}
Root.Children.Add(Child1)
LayoutRoot.Children.Add(Root)
End Sub
...
End Class
<UserControl x:Class="OrgBar">
<Grid>
<StackPanel ToolTipService.ToolTip="{Binding BarName}" Cursor="Hand">
<Rectangle Fill="{Binding BarColor}" Style="{StaticResource RecStyle}"/>
<TextBlock Text="{Binding BarName}" HorizontalAlignment="Center"
Margin="0,10,0,0" />
<local:OrgGroup Margin="0,20" HorizontalAlignment="Center"
DataContext="{Binding Children}" />
</StackPanel>
</Grid>
</UserControl>
<UserControl x:Class="OrgGroup">
<Grid>
<!-- this {Binding} to nothing means bind to DataContext}-->
<ItemsControl ItemsSource="{Binding}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:OrgBar Style="{StaticResource OrgBarStyle}"
DataContext="{Binding}" />
<!-- this {Binding} refers to the the child node this time} -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
That's essentially a tree structure, so like Paully suggested, I would start with a TreeView (Silverlight Toolkit) and customize the control template and treeview itself.
I want to build a simple application with the MVVM pattern.
This application will have two main parts:
menu on top
content below
The navigation will be simple:
each menu item (e.g. "Manage Customers" or "View Reports") will fill the content area with a new page that has some particular functionality
I have done this before with code behind where the code-behind event-handler for menu items had all pages loaded and the one that should be displayed was loaded in as a child of a StackPanel. This, however, will not work in MVVM since you don't want to be manually filling a StackPanel but displaying e.g. a "PageItem" object with a DataTemplate, etc.
So those of you who have made a simple click-menu application like this with MVVM, what was your basic application structure? I'm thinking along these lines:
MainView.xaml:
<DockPanel LastChildFill="False">
<Menu
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
where the Menu is filled with a collection of "PageItems" and the DataTemplate displays the Title of each "PageItem object" as the Header of each MenuItem.
And the ContentControl will be filled with a View/ViewModel pair which has full functionality, but am not sure on this.
First, I think you should keep the code-behind event handler, there's no point in changing a simple 2 line event handler to a complex command driven monster for no practical reason (and don't say testebility, this is the main menu, it will be tested every time you run the app).
Now, if you do want to go the pure MVVM route, all you have to do it to make your menu fire a command, first, in some resource section add this style:
<Style x:Key="MenuItemStyle" TargetType="MenuItem">
<Setter Property="Command"
Value="{Binding DataContext.SwitchViewCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Menu}}}"/>
<Setter Property="CommandParameter"
Value="{Binding}"/>
</Style>
This style will make the menu item fire a the SwitchViewCommand on the attached view model with the MenuItem's DataContext as the command parameter.
The actual view is the same as your code with an additional reference to that style as the ItemContainerStyle (so it applies to the menu item and not the content of the DataTemplate):
<DockPanel LastChildFill="False">
<Menu DockPanel.Dock="Top"
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
ItemContainerStyle="{StaticResource MenuItemStyle}"/>
<ContentControl
Content="{Binding SelectedPageItem}"/>
</DockPanel>
Now in the view model you need (I used strings because I don't have your PageItem code):
private string _selectedViewItem;
public List<string> PageItemsMainMenu { get; set; }
public string SelectedPageItem
{
get { return _selectedViewItem; }
set { _selectedViewItem = value; OnNotifyPropertyChanged("SelectedPageItem"); }
}
public ICommand SwitchViewCommand { get; set; }
And use whatever command class you use to make the command call this code:
private void DoSwitchViewCommand(object parameter)
{
SelectedPageItem = (string)parameter;
}
Now, when the user clicks a menu item the menu item will call the SwitchViewCommand with the page item as the parameter.
The command will call the DoSwitchViewCommand that will set the SelectedPageItem property
The property will raise the NotifyPropertyChanged that will make the UI update via data binding.
Or, you can write a 2 line event handler, your choice
i could imagine an ObservableCollection in the VM, that holds all the pages to be callable from the menu.
Then bind an ItemsControl And the ContentControl to it to make the ContentControl always show the CurrentItem from that List.
Of course, the menu will only bind to some Title property
whereas the ContentControl will adopt the whole item and plug in some appropriate view according to the type.
Another option is to use a ListBox instead of a menu, style the ListBox to look like a menu and then you can bind to the selected value, like this:
<DockPanel LastChildFill="False">
<ListBox
ItemsSource="{Binding PageItemsMainMenu}"
ItemTemplate="{StaticResource MainMenuStyle}"
IsSynchronizedWithCurrentItem="True"/>
<ContentControl
Content="{Binding PageItemsMainMenu/}"/>
</DockPanel>
Note the IsSynchronizedWithCurrentItem="True" to set the selected item and the {Binding PageItemsMainMenu/} with the trailing slash to use it.
I have the following code and basically what i am not able to figure out is how to clone the whole grid and make a blank copy of them side by side.... for a clear understanding this is something to do with hospital application and the grid is related to a pregnancy so when said 'ADD CHILD' button a whole new grid should be created during run time, thanks for the help below is a link that might help people cause i tried it but not sure how to display it
How can you clone a WPF object?
You should put the object you are want to "clone" in a DataTemplate and reference this template from an ItemsControl, then when you need another grid add another item to the items control (or even better to the list the control is bound to) and the ItemsControl will create a new grid and bind it the new object.
For an example take a look at this post on my blog.
Here is an example for this application (I left only the relevant parts and I didn't test it, so there are probably some typos there):
<Window ... >
<Window.Resources>
<DataTemplate x:Key="ChildTemplate">
<Grid>
...
<TextBlock Text="Delivery Date:" Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding DeliveryDate}" Grid.Column="1" Grid.Row="0"/>
<TextBlock Text="Delivery Time:" Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding DeliveryTime}" Grid.Column="1" Grid.Row="1"/>
...
</Grid>
</DataTemplate>
</Window.Resources>
...
<Button Content="AddChild" Click="AddChildClick"/>
...
<ScrollViewer>
<ItemsControl ItemsSource="{Binding AllChildren}" ItemsTemplate="{StaticResource ChildTemplate}">
<ItemsControl.PanelTemplate>
<ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemPanelTemplate>
<ItemsControl.PanelTemplate>
</ScrollViewer>
...
</Window>
And in cs:
Set an object with all the form data as the Window's DataContext. I'll call this class PostDelveryData.
Create another class with the repeating data. I'll call it ChildDeliveryData.
Add a property of type ObservableCollection<ChildDeliveryData> called AllChildren to PostDeliveryData; it's important it'll be ObservableCollection and not any other type of collection.
Now, for the magic:
private void AddChildClick(object sender, RoutedEvetnArgs e)
{
((PostDeliveryData)DataContext).AllChildren.Add(new ChildDeliveryData());
}
And when you add the new item to the list another copy of the entire data template will be added.
I'm not sure that you're using the correct approach here. I would approach the problem by creating a "ChildGridControl" with a Child property, and let the Child property handle the databinding. Adding a new child to the GUI would involve creating a new instance of the ChildGridControl.
If I am understanding correctly, you should create a UserControl, which wraps your Grid and subsequent controls inside. And use this User control anywhere you wanted to replicate that UI.