I have a WPF application where I have view which lets User choose list of fields at run-time and I am storing in a Text file and I am trying to create a Data entry form based on the list of fields that user created Run-time.
I have developed an solution using code behind but I am trying to implement this using MVVM.
Approach 1: I can create the textBlock and Textbox in the code-behind and bind it to the properties in the Viewmodel. The viewmodel will have all the possible field property.
<TabControl Margin="335,10,10,71" TabStripPlacement="Bottom">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,0,0"
Background="Transparent"
BorderBrush="Black"
BorderThickness="1,1,1,1"
CornerRadius="5">
<ContentPresenter
x:Name="ContentSite"
Margin="12,2,12,2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True">
<ContentPresenter.LayoutTransform>
<RotateTransform Angle="0" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Height" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Panel.ZIndex" Value="200" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Product Data 1">
<Grid x:Name="Grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column_1" Width="*" />
<ColumnDefinition x:Name="Column_2" Width="*" />
</Grid.ColumnDefinitions>
<Border
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="MainStack"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<Border
Grid.Column="1"
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="SecondStack"
Grid.Column="1"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<!--<StackPanel x:Name="ThirdStack" x:FieldModifier="public" Grid.Column="2" Orientation="Vertical" Grid.IsSharedSizeScope="True" Width="auto" Height="476" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Border BorderBrush="Black" BorderThickness="1,1,1,1" Grid.Column="2" Margin="0,0,0,10"/>-->
</Grid>
</TabItem>
</TabControl>
Code-Behind=
string[] Data = File.ReadAllLines("Field.txt");
foreach (var part in Data)
{
Grid mytxtBStack = new Grid();
MainStack.Children.Add(mytxtBStack);
ColumnDefinition column_1 = new ColumnDefinition();
column_1.SharedSizeGroup = "FirstColumn";
ColumnDefinition column_2 = new ColumnDefinition();
column_2.SharedSizeGroup = "SecondColumn";
mytxtBStack.ColumnDefinitions.Add(column_1);
mytxtBStack.ColumnDefinitions.Add(column_2);
txtBlock = new TextBlock();
txtBlock.Name = "MyBlock" i;
txtBlock.Text = part;
txtBlock.FontSize = 14;
txtBlock.FontWeight = FontWeights.DemiBold;
txtBlock.Foreground = Brushes.Black;
txtBlock.Margin = new Thickness(0, 20, 0, 0);
mytxtBStack.Children.Add(txtBlock);
txtBox = new TextBox();
txtBox.Name = "MyText" i.ToString();
txtBox.FontSize = 12;
txtBox.Height = 25;
txtBox.Width = 250;
txtBox.BorderThickness = new Thickness(1, 1, 1, 1);
txtBox.Margin = new Thickness(130, 20, 0, 0);
txtBox.Foreground = Brushes.Black;
txtBox.BorderBrush = Brushes.Black;
txtBox.Background = Brushes.White;
txtList.Add(txtBox);
mytxtBStack.Children.Add(txtBox);
}
Approach 2: I can create a dynamic class using the Expandoobject based on the Textfile and create a itemscontrols to bind with view and mentioned all the possibile field properties in the Viewmodel. I am thinking about implementing this part with inside the TabControl,
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<TextBox Width="300" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Can anyone please suggest me a way to do this with MVVM wpf?
CodePudding user response:
I implemented this using the CommunityToolkit.Mvvm NuGet package.
MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace WpfApp1;
public class Field
{
public string Block { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
}
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
// CommunityToolkit's source generator will create a "Field" property.
private ObservableCollection<Field> fields = new();
[RelayCommand]
// CommunityToolkit's source generator will create a "LoadFieldsCommand" command.
private void LoadFields()
{
for (int i = 0; i < 10; i )
{
Fields.Add(new Field() { Block = $"Block#{i 1}", Text = $"Text{i 1}" });
}
}
}
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindowViewModel ViewModel { get; } = new();
}
MainWindow.xaml
<Window
x:Class="WpfApp1.MainWindow"
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:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="ThisWindow"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Command="{Binding ElementName=ThisWindow, Path=ViewModel.LoadFieldsCommand}"
Content="Load fields" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding ElementName=ThisWindow, Path=ViewModel.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Field">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Block}" />
<TextBox Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>