Home > Blockchain >  Creating dynamic data entry form from the list of fields chosen by User at run time
Creating dynamic data entry form from the list of fields chosen by User at run time

Time:09-08

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>
  • Related