![](/img/trans.png)
[英]WPF MVVM add row with two controls to Grid/DataGrid dynamically
[英]Dynamically add User Controls based on List in WPF with MVVM pattern
我的应用程序有两个视图。 查看第一个视图 UpdaterMainView,您可以看到我创建了一个包含两列的 Grid。 左列被进一步拆分为 5 行。 我想要做的是从存储在 UpdaterMainViewModel, TaskList中的对象列表中填充五行。 我创建了一个名为 TaskView 的自定义用户控件,它是我希望如何显示列表、按钮和文本框信息的布局。
当我实例化 UpdaterMainViewModel 时,我的应用程序会扫描目录中的一些内容并创建要完成的任务列表。 总共只有五个可能的任务,因此我创建了五行。 如果不满足条件并且不需要运行任务 2,我不想显示 UserControl 和它下面的那个,应该向上移动。 我不想使用代码隐藏,除非它仍然符合 MVVM 的指导方针。
为了测试,我将<local:TaskView Grid.Column="0" Grid.Row="0"/>
添加到 UpdaterMainView 并创建了一个默认构造函数。 但是,我需要的是为 TaskList 中的每个项目添加 TaskView。 为了让它工作,我还必须使用不同的构造函数创建 UserControls。 使用 DataContext 时,它使用无参数构造函数。 我不知何故需要使用带有 enum 参数的构造函数来用每个适当的 TaskView 填充网格。
更新主视图:
<UserControl x:Class="POSUpdaterGUI.View.UpdaterMainView"
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"
xmlns:local="clr-namespace:POSUpdaterGUI.View" xmlns:local1="clr-namespace:POSUpdaterGUI.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<local1:UpdaterMainViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="60" />
<RowDefinition Height="60" />
<RowDefinition Height="60" />
<RowDefinition Height="60" />
</Grid.RowDefinitions>
<local:TaskView Grid.Column="0" Grid.Row="0"/>
<local:TaskView Grid.Column="0" Grid.Row="1"/>
<local:TaskView Grid.Column="0" Grid.Row="2"/>
<local:TaskView Grid.Column="0" Grid.Row="3"/>
</Grid>
<TextBox Grid.Column="1" Margin="10" TextWrapping="Wrap" Text="{Binding OutputText}"/>
</Grid>
</UserControl>
更新主视图模型:
using POSUpdaterLibrary;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace POSUpdaterGUI.ViewModel
{
public class UpdaterMainViewModel : INotifyPropertyChanged
{
private string outputText;
public string OutputText
{
get => outputText;
set
{
outputText = value;
NotifyPropertyChanged("OutputText");
}
}
public UpdateTaskList TaskList { get; set; }
public UpdaterMainViewModel()
{
Log("Application Starting.");
TaskList = new UpdateTaskList();
TaskList.LoggerEvent += TaskList_LoggerEvent;
TaskList.GetList(AppContext.BaseDirectory);
}
private void TaskList_LoggerEvent(object sender, LoggerEventArgs e)
{
Log(e.LogString);
}
private void Log(string message)
{
if (OutputText == null)
{
OutputText = DateTime.Now.ToString() + " - " + message;
return;
}
OutputText += "\n" + DateTime.Now.ToString() + " - " + message;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
任务视图:
<UserControl x:Class="POSUpdaterGUI.View.TaskView"
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"
xmlns:local="clr-namespace:POSUpdaterGUI.View" xmlns:local1="clr-namespace:POSUpdaterGUI.ViewModel"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="500">
<UserControl.DataContext>
<local1:TaskViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" Height="50" Width="50" Command="{Binding RunTaskCommand}">
<Image Source="{Binding StatusImage}"/>
</Button>
<Label Grid.Column="1" Content="{Binding TaskName}" HorizontalAlignment="Left" Margin="15,0,0,0" VerticalAlignment="Center"/>
</Grid>
任务视图模型:
using POSUpdaterLibrary;
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace POSUpdaterGUI.ViewModel
{
class TaskViewModel : INotifyPropertyChanged
{
public string TaskName { get; set; }
public ICommand RunTaskCommand { get; set; }
public string StatusImage { get; set; }
public TaskViewModel()
{
TaskName = "undefined";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
}
public TaskViewModel(UpdaterConstants.TaskType taskType)
{
switch (taskType)
{
case UpdaterConstants.TaskType.KillProcesses:
TaskName = "Processes Killed";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
break;
case UpdaterConstants.TaskType.FileCopy:
TaskName = "Files Copied";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
break;
case UpdaterConstants.TaskType.DatabaseUpdates:
TaskName = "Database Updated";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
break;
case UpdaterConstants.TaskType.COMDLLsRegistered:
TaskName = "COM DLLs Registered";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
break;
case UpdaterConstants.TaskType.FileCleanup:
TaskName = "Files Cleaned";
RunTaskCommand = new RelayCommand(new Action<object>(DefaultMethod));
StatusImage = STARTIMAGE;
break;
default:
break;
}
}
public enum Status
{
START,
COMPLETE,
FAILED,
WAIT
}
internal const string STARTIMAGE = "/data/start.png";
internal const string COMPLETEIMAGE = "/data/complete.png";
internal const string FAILEDIMAGE = "/data/failed.png";
internal const string WAITIMAGE = "/data/wait.png";
public event PropertyChangedEventHandler PropertyChanged;
private void DefaultMethod(object obj)
{
// Shouldn't be shown. Just a hold-over
throw new NotImplementedException();
}
}
}
UpdaterMainView 是应用程序的主视图,并且始终显示。 为了证明这一点,这是我的MainWindow.xaml :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:POSUpdaterGUI"
xmlns:View="clr-namespace:POSUpdaterGUI.View" x:Class="POSUpdaterGUI.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<View:UpdaterMainView/>
</Grid>
感谢@Clemens 为我指明了正确的方向! 我不需要单独的视图。 我确实需要使用数据模板。
这是我最终得到的UpdaterMainView :
<UserControl x:Class="POSUpdaterGUI.View.UpdaterMainView"
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"
xmlns:local="clr-namespace:POSUpdaterGUI.View" xmlns:local1="clr-namespace:POSUpdaterGUI.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<local1:UpdaterMainViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" Margin="10" ItemsSource="{Binding TaskList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center" Height="50" Width="50" Command="{Binding Command}">
<Image Source="{Binding StatusImage}"/>
</Button>
<Label Grid.Column="1" Content="{Binding TaskName}" HorizontalAlignment="Left" Margin="15,0,0,0" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Grid.Column="1" Margin="10" TextWrapping="Wrap" Text="{Binding OutputText}"/>
</Grid>
</UserControl>
这是我最终得到的UpdaterMainViewModel :
using POSUpdaterGUI.Models;
using POSUpdaterLibrary;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace POSUpdaterGUI.ViewModel
{
public class UpdaterMainViewModel : INotifyPropertyChanged
{
private UpdaterLogger _logger;
private string outputText;
public string OutputText
{
get => outputText;
set
{
outputText = value;
NotifyPropertyChanged("OutputText");
}
}
private List<UpdateTaskWrapper> taskList;
public List<UpdateTaskWrapper> TaskList
{
get => taskList;
set
{
taskList = value;
NotifyPropertyChanged("TaskList");
}
}
public UpdaterMainViewModel()
{
_logger = UpdaterLogger.Instance;
_logger.LoggerEvent += Logger_LoggerEvent;
Log("Application Starting.");
TaskList = GetTaskList();
}
private void Logger_LoggerEvent(object sender, LoggerEventArgs e)
{
Log(e.LogString);
}
private List<UpdateTaskWrapper> GetTaskList()
{
UpdateTaskList list = new UpdateTaskList();
var myList = list.GetList(AppContext.BaseDirectory);
// Create one out of the Wrapper class.
List<UpdateTaskWrapper> wrappedList = new List<UpdateTaskWrapper>();
foreach (var task in myList)
{
wrappedList.Add(new UpdateTaskWrapper(task));
}
return wrappedList;
}
private void Log(string message)
{
if (OutputText == null)
{
OutputText = DateTime.Now.ToString() + " - " + message;
return;
}
OutputText += "\n" + DateTime.Now.ToString() + " - " + message;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
最重要的部分是我删除了 TaskView,添加了数据模板(在标签之间),并将 List 从自定义 UpdaterTaskList 更改为 List。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.