0

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:

enter image description here

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:

  1. I Couldn't figure from which "myUserControl" I got the event.
  2. Even if I did know which button was click and by that where to create the new UserControl, I didn't know how to insert in the middle of the StackPanel (maybe a different panel is needed?)

Also, this solution used code behind. Is there a way to do all that in MVVM?

ASh
  • 34,632
  • 9
  • 60
  • 82
Noam Ohana
  • 186
  • 14
  • in code behind you should add your usecontrol as child to parent e.g yourStackpanelName.Children.Add(yourChildUsercontrol); – Usman Ali Jun 28 '18 at 06:31
  • in MVVM you can play with visibility – Usman Ali Jun 28 '18 at 06:33
  • But if I do StackpanelName.Children.Add(yourChildUsercontrol); it will always add to the END of the stackpanel. I want to add after the object that its button was clicked. – Noam Ohana Jun 28 '18 at 06:43
  • i think you need page not usercontrol. could you please share description of question what do you actually want – Usman Ali Jun 28 '18 at 07:01
  • As I wrote in my question:" 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. " – Noam Ohana Jun 28 '18 at 08:58

1 Answers1

1

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" } }
        };
    }
}
ASh
  • 34,632
  • 9
  • 60
  • 82