wpf 使用 MVVM 框架中的按钮动态添加文本框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24960476/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:13:41  来源:igfitidea点击:

Dynamically Adding TextBox using a Button within MVVM framework

c#wpfxamlmvvm

提问by rsgmon

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.

我一直在爬陡峭的 WPF 山!所以我想创建一个允许用户动态添加文本框的UI。要做到这一点,他们会按下一个按钮。

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.

我已经设法使用背后的代码创建了这个,但我想转向 MVVM 结构,所以我在视图中没有任何代码。我试过 ICommand 和 ObservableCollection,但我遗漏了一些东西,我不知道在哪里。这是我的简单示例。

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

XAML:非常基本的一个按钮,可以添加一行。

<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

背后的 C# 代码

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?

问题:我需要什么代码(XAML 和 C#)才能将方法从后面的代码中移到视图模型中?

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?

我假设文本框必须保存在一个容器中,在这种情况下,这就是网格的用途。但是,如果我使用的是 MVVM,是否需要在列表视图或其他使用 ItemsSource 的容器中包含文本框?

回答by Rohit Vats

Follow these steps and you are done:

按照以下步骤操作即可完成:

  1. Use ItemsControland bind it's ItemsSourceto some collection (preferably ObservableCollection) in your ViewModel.
  2. Define ItemTemplatefor ItemsControl with TextBox in it.
  3. Create an ICommandin ViewModel and bind it to button.
  4. On command execute add item in the collection and you will see TextBox gets added automatically.
  1. 使用ItemsControl它并将其绑定ItemsSource到 ViewModel 中的某个集合(最好是 ObservableCollection)。
  2. ItemTemplate为带有 TextBox 的 ItemsControl定义。
  3. ICommand在 ViewModel 中创建一个并将其绑定到按钮。
  4. 在命令中执行在集合中添加项目,您将看到 TextBox 被自动添加。


XAML:

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.

注意- 我假设您知道如何通过设置 DataContext 使绑定魔法起作用来将 View 插入 ViewModel。

回答by Meysam Chegini

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