使用 ViewModel 将子项添加到用户控件?

Add children to user control with ViewModel?

问题:我该如何将新的子项添加到我的用户控件中?我被告知视图模型不应该知道任何关于视图的信息,而是使用绑定。现在我创建了一个项目控件并将我的绑定路径设置为包含标签数组的 属性 MyLabels。它不起作用,老实说,我不确定推荐的方法是什么让它起作用。

我的 XAML 的项目控件(在我的用户控件中的某处):

<UserControl x:Class="Test.Main"
             x:Name="this"
             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="600" d:DesignWidth="1000">

        <ScrollViewer Height="400" Width="900">
            <StackPanel Width="900">
                <Grid x:Name="myGrid">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>

                    <ItemsControl ItemsSource="{Binding Path=MyLabels}"/>
                </Grid>

                <Image PreviewMouseDown="AddLabel""/>
            </StackPanel>
        </ScrollViewer>
    </StackPanel>
</UserControl>

这是我的视图模型:

namespace Test {
    public partial class Main {
        public ObservableCollection<string> MyLabels { get; set; } = new ObservableCollection<string>();

        public Main() {
            InitializeComponent();
            DataContext = this;
        }

        public void AddLabel(object sender, RoutedEventArgs e) {
            for (int i = 0; i < 2; i++) {
                MyLabels.Add("eee");
            }
        }
    }
}

将视图的 DataContext 设置为视图模型的实例:

public Main() {
    InitializeComponent();
    DataContext = this;
}

然而,视图模型应该是一个 class 自己的:

public MainWindow() {
    InitializeComponent();
    DataContext = new ViewModel();
}

它不应该创建任何 Label 元素而是 strings:

public class ViewModel
{
    public List<string> MyLabels { get; set; } new List<string>();

    public ViewModel()
    {
        AddLabels();
    }

    public void AddLabels()
    {
        for (int i = 0; i < 5; i++)
        {
            MyLabels.Add("Sample");
        }
    }
}

Label 是用户界面元素类型,它们仅属于视图。

另请注意,您只能绑定到 public properties.

It works now, only problem is im not sure how to add actual labels without violating the MVVM pattern.

您在 ItemsControlItemTemplate 中添加了 Label 个元素。此模板定义源集合中项目的外观,即 string 在这种情况下:

<ItemsControl ItemsSource="{Binding Path=MyLabels}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Label Content="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

通过以下 MVVM 模拟(轻量级风格!),您应该能够看到在视图中创建的标签。根据您的更新,仍然遵循生成字符串。

ViewModel

public class ViewModel
{
        public ObservableCollection<string> MyStrings { get; set; }
        public ICommand AddStringCommand { get; private set; }

        public ViewModel()
        {
            if (MyStrings == null) MyStrings = new ObservableCollection<string>();

            for (int i = 0; i < 5; i++)
            {
                MyStrings.Add("string " + i);
            }

            AddStringCommand = new AddLabelCommand(AddString);

        }

        public void AddString()
        {
            MyStrings.Add("string " + (MyStrings.Count));
        }

 }

public class AddLabelCommand : ICommand
{
        readonly Action _action = null;

        public AddLabelCommand(Action commandToExecute)
        {
            _action = commandToExecute; //Handle null condition
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            _action();
        }
}   

MainWindow.xaml.cs(您视图后面的代码)

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

MainWindow.xaml(内部视图)

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="402" Width="540"
        xmlns:local="clr-namespace:WpfApplication1">
    <Window.Resources>
        <local:ViewModel x:Key="ViewModel"/> <!--Initialize ViewModel inside XAML-->
    </Window.Resources>
    <Grid Height="367" Width="526" DataContext="{StaticResource ViewModel}">
        <ItemsControl ItemsSource="{Binding Path=MyStrings}" Width="100" BorderBrush="Red">
            <ItemsControl.ItemTemplate> <!--If you want to display your content in a control you can use ItemTemplate as suggested by @mm8-->
                <DataTemplate>
                    <Label Content="{Binding}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Content="Add Label" Height="23" Width="100" Command="{Binding Path=AddStringCommand}"/>
    </Grid>
</Window>

希望这能为您提供一个起点!!