WinForms and WPF Spreadsheet – How to Create a Data Entry Form

Office-Inspired Products
16 August 2019

Our WinForms and WPF Spreadsheet controls can help you create intuitive data editing user experiences within your next desktop application. Built-in features allow you to process spreadsheet documents as needed. You can load and export workbooks using different formats, create templates, apply password protection, retrieve data from various data sources, perform simple or complex calculations, embed custom data editors within individual cells, validate cell values, etc.

In this blog post, we will show you how to leverage the flexibility of our WinForms and WPF Spreadsheet controls to create a payroll data entry form. You could replicate similar functionality using standard controls, but the ubiquity of Microsoft Excel in the business world (users are familiar with both its UI and its core functionality) should help lower the overall learning curve of your next desktop app (the ultimate aim of this demo is to show you how to create spreadsheet powered data entry forms).

As you’ll discover, this sample allows users to enter payroll information within the Spreadsheet (regular and overtime hours worked, sick leave and vacation hours, overtime pay rate and deductions). Once data is entered, the Spreadsheet automatically calculates an employee’s pay and payroll taxes. The data navigator at the bottom of the application allows you to switch between employees.

Note: Though this example uses our WinForms Spreadsheet control, the same approach can be used for our WPF Spreadsheet.

How to Create a Payroll Calculator with a Data Entry Form

  1. Create a new application and add the Spreadsheet control and Data Navigator to your form.

  2. Create a Payroll Calculator document template and protect the workbook to prevent modifications. Users can only change values in the highlighted cells.

    Feel free to download the PayrollCalculatorTemplate.xslx file to proceed.

  3. Load the generated template into the WinForms Spreadsheet control.

    spreadsheetControl1.LoadDocument("PayrollCalculatorTemplate.xlsx");
  4. Assign our custom in-place editors (we chose to use spin editors) to the editable cells to facilitate user input.

    private void BindCustomEditors() {
        var sheet = spreadsheetControl1.ActiveWorksheet;
        sheet.CustomCellInplaceEditors.Add(sheet["D8"], CustomCellInplaceEditorType.Custom, 
                                           "RegularHoursWorked");
        sheet.CustomCellInplaceEditors.Add(sheet["D10"], CustomCellInplaceEditorType.Custom, 
                                           "VacationHours");
        sheet.CustomCellInplaceEditors.Add(sheet["D12"], CustomCellInplaceEditorType.Custom, 
                                           "SickHours");
        sheet.CustomCellInplaceEditors.Add(sheet["D14"], CustomCellInplaceEditorType.Custom, 
                                           "OvertimeHours");
        sheet.CustomCellInplaceEditors.Add(sheet["D16"], CustomCellInplaceEditorType.Custom, 
                                           "OvertimeRate");
        sheet.CustomCellInplaceEditors.Add(sheet["D22"], CustomCellInplaceEditorType.Custom, 
                                           "OtherDeduction");
    }
  5. Handle the SelectionChanged and CustomCellEdit events to display a spin editor when a user clicks on an editable cell.

    // Activate a cell editor when a user selects an editable cell.
    private void SpreadsheetControl1_SelectionChanged(object sender, EventArgs e) {
        var sheet = spreadsheetControl1.ActiveWorksheet;
        if (sheet != null) {
            var editors = 
                sheet.CustomCellInplaceEditors.GetCustomCellInplaceEditors(sheet.Selection);
            if (editors.Count == 1)
                spreadsheetControl1.OpenCellEditor(CellEditorMode.Edit);
        }
    }
    
    // Display a custom in-place editor for a cell.
    private void spreadsheetControl1_CustomCellEdit(object sender, 
                                                    SpreadsheetCustomCellEditEventArgs e) {
        if (e.ValueObject.IsText)
            e.RepositoryItem = CreateCustomEditor(e.ValueObject.TextValue);
    }
    
    private RepositoryItem CreateCustomEditor(string tag) {
        switch (tag) {
            case "RegularHoursWorked": return CreateSpinEdit(0, 184, 1);
            case "VacationHours": return CreateSpinEdit(0, 184, 1);
            case "SickHours": return CreateSpinEdit(0, 184, 1);
            case "OvertimeHours": return CreateSpinEdit(0, 100, 1);
            case "OvertimeRate": return CreateSpinEdit(0, 50, 1);
            case "OtherDeduction": return CreateSpinEdit(0, 100, 1);
            default: return null;
        }
    }
    
    private RepositoryItemSpinEdit CreateSpinEdit(int minValue, int maxValue, int increment) 
        => new RepositoryItemSpinEdit {
            AutoHeight = false,
            BorderStyle = BorderStyles.NoBorder,
            MinValue = minValue,
            MaxValue = maxValue,
            Increment = increment,
            IsFloatValue = false
        };

Create a Data Model

  1. Create the PayrollModel class. This entity class implements the INotifyPropertyChanged interface, and exposes basic properties for this project: EmployeeName, RegularHoursWorked, HourlyWage, etc.

    public class PayrollModel : INotifyPropertyChanged {
        private string employeeName;
        private double hourlyWage;
        
        // ...
    
        public string EmployeeName {
            get => employeeName;
            set {
                if (employeeName != value) {
                    employeeName = value;
                    NotifyPropertyChanged();
                }
            }
        }
    
        public double HourlyWage {
            get => hourlyWage;
            set {
                if (hourlyWage != value) {
                    hourlyWage = value;
                    NotifyPropertyChanged();
                }
            }
        }
        
        // ...
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
  2. Create a list of PayrollModel objects with sample data. This list will be used to supply data to the Spreadsheet control.

    readonly List payrollData = new List();
        
    // ... 
        
    payrollData.Add(new PayrollModel() {
        EmployeeName = "Linda Brown",
        HourlyWages = 10.0,
        RegularHoursWorked = 40,
        VacationHours = 5,
        SickHours = 1,
        OvertimeHours = 0,
        OvertimeRate = 15.0,
        OtherDeduction = 20.0,
        TaxStatus = 1,
        FederalAllowance = 4,
        StateTax = 0.023,
        FederalIncomeTax = 0.28,
        SocialSecurityTax = 0.063,
        MedicareTax = 0.0145,
        InsuranceDeduction = 20.0,
        OtherRegularDeduction = 40.0
    });
        
    // ...

Bind the Spreadsheet to Data

The data binding mechanism in this example is powered by our SpreadsheetBindingManager component. It includes an AddBinding method that allows you to bind the PayrollModel object’s properties to cells within the Payroll Calculator template:

spreadsheetBindingManager1.SheetName = "Payroll Calculator";
spreadsheetBindingManager1.AddBinding("EmployeeName", "C3");
spreadsheetBindingManager1.AddBinding("HourlyWage", "D6");
spreadsheetBindingManager1.AddBinding("RegularHoursWorked", "D8");
    
// ...

When a user edits a cell value, the SpreadsheetBindingManager updates the value of the corresponding data source property. All changes in data source property values are immediately reflected in bound cells.

Bind the list of PayrollModel objects to the SpreadsheetBindingManager and DataNavigator control via the BindingSource component.

// Bind the list of PayrollModel objects to the BindingSource.  
bindingSource1.DataSource = payrollData;

// Attach the BindingSource to the SpreadsheetBindingManager and DataNavigator control.  
spreadsheetBindingManager1.DataSource = bindingSource1;
dataNavigator1.DataSource = bindingSource1;

You can download a complete sample projects from the following repositories:

Additional Examples

Our installation includes other examples of data entry forms built with DevExpress Spreadsheet. Please refer to the following blog post to see how we built an invoice form with our spreadsheet component.

If you've installed the DevExpress Demo Center, the following links will allow you to load our Spreadsheet-powered data entry demos:

Your Feedback Matters

If you have implemented similar functionality in your desktop app (using our Spreadsheet control for data entry), please post your comments below. We’d love to learn more about your solution and how you’re using DevExpress Spreadsheet within your app.

Should you have technical questions, feel free to contact us via the DevExpress Support Center.

no comments
No Comments

Please login or register to post comments.