I have a simple UserControl that contains a TextBox and a Button. I would like that clicking the button will create a new user control that will be positioned right after (below) the one That its button was clicked.
For example:
This is the code for the UserControl:
<UserControl x:Class="LearnWPF.MyUserControl"
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"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".9*"/>
<RowDefinition Height=".1*"/>
</Grid.RowDefinitions>
<TextBox ></TextBox>
<Button Grid.Row="1" Content="Create New UserControl" FontSize="20"/>
</Grid>
This is the main window:
<Window x:Class="LearnWPF.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myUserControl="clr-namespace:LearnWPF"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<myUserControl:MyUserControl />
</StackPanel>
</Window>
I did succeed to add new "myUserControls" to the StackPanel in the main window by doing as advised here. The 2 main problems with this implementation were:
Also, this solution used code behind. Is there a way to do all that in MVVM?
if you want to manage a list of items from a view model, then you need to use an ItemsControl
instead of inserting elements to StackPanel from code-behind. Items appearance can be changed via ItemsControl.ItemTemplate
. Action to add new item should be triggered by a Command bound to a button:
<Window x:Class="XamlApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" WindowStartupLocation="CenterScreen"
Height="300" Width="300">
<Grid>
<ScrollViewer>
<ItemsControl ItemsSource="{Binding Path=MyItems}"
Margin="5"
Background="Wheat">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".9*" />
<RowDefinition Height=".1*" />
</Grid.RowDefinitions>
<TextBox Text="{Binding Path=MyText}" />
<Button Grid.Row="1"
Content="Create New UserControl"
FontSize="20"
Command="{Binding Path=DataContext.AddCmd,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
view model should have a collection to store the items and a command to add/insert items:
public class StringItem
{
public string MyText { get; set; }
}
public class MyViewModel
{
public MyViewModel()
{
MyItems = new ObservableCollection<StringItem>();
AddCmd = new RelayCommand<StringItem>(Add);
}
public ObservableCollection<StringItem> MyItems { get; private set; }
public ICommand AddCmd { get; private set; }
private void Add(StringItem current)
{
var item = new StringItem { MyText = "new item " + (MyItems.Count + 1) };
int idx = MyItems.IndexOf(current);
if (idx < 0)
MyItems.Add(item);
else
MyItems.Insert(idx + 1, item);
}
}
RelayCommand is an implementation of ICommand from this lightweight MVVM package
ViewModel and View are wired together in the View constructor:
public partial class MyWindow : Window
{
public MyWindow()
{
InitializeComponent();
DataContext = new MyViewModel
{
MyItems = { new StringItem { MyText = "hello world" } }
};
}
}