9

I keep climbing the steep WPF hill! So I want to create a UI that allows the user to dynamically add a text box. To do this they would hit a button.

I've managed to create this using code behind but I want to move towards an MVVM structure so I don't have any code in the view. I've tried ICommand and ObservableCollection but I'm missing something and I don't know where. Here is my simple example.

XAML: Very basic with one button that adds a row.

<Window x:Class="WPFpractice072514.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFpractice072514"
        Title="MainWindow" Height="350" Width="525">
    <Grid Name="mymy" >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Grid.Row="0" Name="ButtonUpdateArtist"
                Content="Add TextBox" Click="ButtonAddTexboxBlockExecute" />

    </Grid>
</Window>

C# Code Behind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFpractice072514
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region members
        int count = 0;
        #endregion

        public MainWindow()
        {
            InitializeComponent();
        }

        private void ButtonAddTexboxBlockExecute(Object Sender, RoutedEventArgs e)
        {
            TextBox t = new TextBox();
            t.Height = 20;
            t.Width = 20;
            t.Name = "button";

            RowDefinition rowDef1;
            rowDef1 = new RowDefinition();
            mymy.RowDefinitions.Add(rowDef1);

            ColumnDefinition colDef1;
            colDef1 = new ColumnDefinition();
            mymy.ColumnDefinitions.Add(colDef1);
            ++count;

            mymy.Children.Add(t);

            Grid.SetColumn(t, 1);
            Grid.SetRow(t, count);

        }
    }
}

Questions: What code (XAML and C#) do I need to be able to move the method out of the code behind and into a viewmodel?

Can you use commands to dynamically add a textbox?

I'm assuming that the textboxes must be kept in a container which in this case is what grid is for. But if I'm using an MVVM do I need to contain the textboxes in a listview or some other container that uses ItemsSource?

1
  • This is not MVVM, which is probably why you are struggling. In MVVM, I'd have a ViewModel with a public observable collection property of models bound to an ItemsControl. I'd have a DataTemplate for the model type. And I'd have an ICommand bound to the button. When the button is clicked, the command fires in the view model, where I add a new model to the collection. The UI would automatically add UI for the element using the DataTemplate. That's why we do MVVM--it's easy. What you're doing is windows forms. If you want to create forms, do so. Commented Jul 25, 2014 at 17:27

2 Answers 2

15

Follow these steps and you are done:

  1. Use ItemsControl and bind it's ItemsSource to some collection (preferably ObservableCollection) in your ViewModel.
  2. Define ItemTemplate for ItemsControl with TextBox in it.
  3. Create an ICommand in ViewModel and bind it to button.
  4. On command execute add item in the collection and you will see TextBox gets added automatically.

XAML:

<StackPanel>
    <Button Content="Add TextBox" Command="{Binding TestCommand}"/>
    <ItemsControl ItemsSource="{Binding SomeCollection}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Path=.}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</StackPanel>

ViewModel:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public ObservableCollection<string> SomeCollection { get; set; }
    public ICommand TestCommand { get; private set; }

    public MainWindowViewModel()
    {
        SomeCollection = new ObservableCollection<string>();
        TestCommand = new RelayCommand<object>(CommandMethod);
    }

    private void CommandMethod(object parameter)
    {
        SomeCollection.Add("Some dummy string");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

RelayCommand:

public class RelayCommand<T> : ICommand
{    
    readonly Action<T> _execute = null;
    readonly Predicate<T> _canExecute = null;

    public RelayCommand(Action<T> execute)
        : this(execute, null)
    {
    }    

    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute((T)parameter);
    }    

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }
}

Note - I assume you know how to plug View with your ViewModel by setting DataContext to make the binding magic to work.

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

2 Comments

Thanks. Wow, never would have thought of using a collection of "dummy" models to create UI controls. One challenging part of learning WPF is just finding the properties, what objects can use them and what they are for.
Exactly WPF is more data driven whereas WinForms is more of UI driven technology.
3
[link][1]

 class TestViewModel : BindableBase  
    {  
        private TestModel testModel;  

        public ICommand AddCommand { get; private set; }  
        public TestViewModel(StackPanel stkpnlDynamicControls)  
        {  
            testModel = new TestModel();  
            TestModel.stkPanel = stkpnlDynamicControls;  
            AddCommand = new DelegateCommand(AddMethod);  
        }  
        public TestModel TestModel  
        {  
            get { return testModel; }  
            set { SetProperty(ref testModel, value); }  
        }  
        private void AddMethod()  
        {  
            Label lblDynamic = new Label()  
            {  
                Content = "This is Dynamic Label"  
            };  
            TestModel.stkPanel.Children.Add(lblDynamic);  
        }  
    }

1 Comment

It is generally good practice to provide some explanation with your code. You can edit your answer to add more information.

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.