2014-07-25 7 views
7

Continuo a salire la ripida collina del WPF! Quindi voglio creare un'interfaccia utente che consenta all'utente di aggiungere dinamicamente una casella di testo. Per fare questo avrebbero colpito un pulsante.Aggiunta dinamica di TextBox utilizzando un pulsante all'interno del framework MVVM

Sono riuscito a creare questo utilizzando codice dietro ma voglio spostarmi verso una struttura MVVM in modo da non avere alcun codice nella vista. Ho provato ICommand e ObservableCollection ma mi manca qualcosa e non so dove. Ecco il mio semplice esempio.

XAML: molto semplice con un pulsante che aggiunge una riga.

<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# Codice Dietro

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); 

     } 
    } 
} 

Domande: Quale codice (XAML e C#) ho bisogno di essere in grado di spostare il metodo fuori dal codice dietro e in una ViewModel?

È possibile utilizzare i comandi per aggiungere dinamicamente una casella di testo?

Suppongo che le caselle di testo debbano essere conservate in un contenitore che in questo caso rappresenta la griglia. Ma se sto usando un MVVM devo contenere le caselle di testo in un listview o qualche altro contenitore che utilizza ItemsSource?

+0

Questo non è MVVM, che è probabilmente il motivo per cui si sta lottando. In MVVM, avrei un ViewModel con una proprietà di raccolta pubblica osservabile di modelli associati a un oggetto ItemsControl. Avrei un DataTemplate per il tipo di modello. E avrei un ICommand legato al bottone. Quando si fa clic sul pulsante, il comando viene attivato nel modello di visualizzazione, in cui aggiungo un nuovo modello alla raccolta. L'interfaccia utente aggiungerà automaticamente l'interfaccia utente per l'elemento utilizzando DataTemplate. Ecco perché facciamo MVVM: è facile. Quello che stai facendo è Windows Form.Se vuoi creare moduli, fallo. – Will

risposta

11

Seguire questi passi e si è fatto:

  1. Usa ItemsControl e associarlo è ItemsSource a qualche raccolta (preferibilmente ObservableCollection) nel vostro ViewModel.
  2. Definire ItemTemplate per ItemsControl con TextBox in esso.
  3. Creare un ICommand in ViewModel e collegarlo a pulsante.
  4. Al comando esegui aggiungi elemento nella raccolta e vedrai che TextBox viene aggiunto automaticamente.

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); 
    } 
} 

Nota - presumo di sapere come collegare View con il tuo ViewModel impostando DataContext per rendere la magia vincolante per lavorare.

+1

Grazie. Wow, non avrei mai pensato di usare una collezione di modelli "fittizi" per creare controlli dell'interfaccia utente. Una parte impegnativa dell'apprendimento di WPF consiste nel trovare le proprietà, quali oggetti possono utilizzarle e a cosa servono. – user1181337

+0

Esattamente WPF è più guidato dai dati, mentre WinForms è più una tecnologia guidata dall'interfaccia utente. –

0
[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

Generalmente è buona norma fornire alcune spiegazioni con il proprio codice. È possibile modificare la risposta per aggiungere ulteriori informazioni. – Theresa