C # лучший способ «проверить» объект, связанный с формой - PullRequest
1 голос
/ 28 августа 2009

Я получил бизнес-объект, связанный с формой (каждое свойство связано с элементом управления). Существуют некоторые бизнес-особенности (например, это поле не должно быть пустым, это поле должно быть больше 0 и т. Д.). Как лучше всего проверить все правила?

У меня в настоящее время есть валидатор на каждом контроллере, поэтому я могу проверить, все ли валидаторы в порядке, но мне не очень нравится это решение. На самом деле правила вызываются, и не все сразу увидеть сразу.

У меня может быть большой метод CheckValidaty, который проверяет все правила, но это приводит к двойной проверке с валидаторами.

Что бы вы сделали, другое решение?

Ответы [ 4 ]

5 голосов
/ 30 августа 2009

Я бы предложил, чтобы BusinessObject реализовал IDataErrorInfo. Я думаю, что это самый чистый способ обработки бизнес-ошибок.

Взгляните на эти ссылки:

  1. http://msdn.microsoft.com/en-us/library/system.componentmodel.idataerrorinfo_members.aspx
  2. http://www.codegod.de/WebAppCodeGod/objectdatasource-and-idataerrorinfo-with-winforms-AID427.aspx
3 голосов
/ 28 августа 2009

Существует два вида проверки: проверка данных (на уровне постоянства) и проверка пользовательского интерфейса. Я предпочитаю ставить валидацию рядом со стороной ввода, потому что обычно вы хотите показать пользователю, что не так, и попытка подключить валидацию данных к пользовательскому интерфейсу добавляет больше косвенности, которая должна соответствовать косвенности привязки данных.

Включение проверки данных в контрольные классы не кажется хорошей идеей. Это в основном означает, что класс управления может использоваться только для одного определенного поля.

Стандартный способ работы Windows Forms - поместить проверку данных в контейнер . Таким образом, проверка может проверять состояние других свойств и подключать определенный элемент управления к объекту (ам) ErrorProvider для отображения соответствующего сообщения об ошибке.

class EmployeeForm : UserControl { EmployeeObject employee; // ... void employeeNameTextBox_Validating (object sender, CancelEventArgs e) { if ( employee.Name.Trim ().Length == 0 ) { errorProvider.SetError (employeeNameTextBox, "Employee must have a name"); e.Cancel = true; } } void employeeHireDateControl_Validating (...) { if ( employee.HireDate < employee.BirthDate ) { errorProvider.SetError (employeeHireDateControl, "Employee hire date must be after birth date"); e.Cancel = true; } } } class ExplorerStyleInterface : ... { // ... bool TryDisplayNewForm (Form oldForm, Form newForm) { if ( ! oldForm.ValidateChildren () ) return false; else { HideForm (oldForm); ShowForm (newForm); return true; } } }

Стандартным способом WF является запуск события Validating для конкретного элемента управления, когда элемент управления теряет фокус или когда ValidateChildren вызывается для контейнера (или контейнера контейнера). Вы устанавливаете обработчик для этого события через свойства события для элемента управления в контейнере; обработчик автоматически добавляется в контейнер.

Я не уверен, почему этот способ не работает для вас, если только вам не нравится поведение по умолчанию отказа от повторной фокусировки на ошибке, которое вы можете изменить, установив свойство AutoValidate контейнера (или контейнер) в EnableAllowFocusChange.

Расскажите нам конкретно, что вам не нравится в стандартном способе работы Windows Forms, и, возможно, мы можем либо предложить альтернативы, либо убедить вас, что стандартный способ будет делать то, что вы хотите.

1 голос
/ 29 октября 2009

Если у вас есть такая ситуация:

   - Many controls in the Winform to
     validate
   - A validation rule for each control
   - You want an overall validation within the Save() command
   - You don't want validation when controls focus changes
   - You also need an red icon showing errors in each control

Затем вы можете скопировать и вставить следующий код, который реализует форму с двумя текстовыми полями и кнопкой Сохранить:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ValidationApp
{
    public  class ValidationTestForm : Form
    {
        private TextBox textBox1;
        private TextBox textBox2;
        private Button btnSave;
        private ErrorProvider errorProvider1;

          /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.textBox1 = new System.Windows.Forms.TextBox();
            this.textBox2 = new System.Windows.Forms.TextBox();
            this.btnSave = new System.Windows.Forms.Button();
            this.errorProvider1 = new System.Windows.Forms.ErrorProvider(this.components);
            ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).BeginInit();
            this.SuspendLayout();
            // 
            // textBox1
            // 
            this.textBox1.Location = new System.Drawing.Point(131, 28);
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(100, 20);
            this.textBox1.TabIndex = 0;
            // 
            // textBox2
            // 
            this.textBox2.Location = new System.Drawing.Point(131, 65);
            this.textBox2.Name = "textBox2";
            this.textBox2.Size = new System.Drawing.Size(100, 20);
            this.textBox2.TabIndex = 1;
            // 
            // btnSave
            // 
            this.btnSave.Location = new System.Drawing.Point(76, 102);
            this.btnSave.Name = "btnSave";
            this.btnSave.Size = new System.Drawing.Size(95, 30);
            this.btnSave.TabIndex = 2;
            this.btnSave.Text = "Save";
            this.btnSave.UseVisualStyleBackColor = true;
            this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
            // 
            // errorProvider1
            // 
            this.errorProvider1.ContainerControl = this;
            // 
            // ValidationTestForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(266, 144);
            this.Controls.Add(this.btnSave);
            this.Controls.Add(this.textBox2);
            this.Controls.Add(this.textBox1);
            this.Name = "ValidationTestForm";
            this.Text = "ValidationTestForm";
            ((System.ComponentModel.ISupportInitialize)(this.errorProvider1)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        public ValidationTestForm()
        {
            InitializeComponent();

            // path validation
            this.AutoValidate = AutoValidate.Disable; // validation to happen only when you call ValidateChildren, not when change focus
            this.textBox1.CausesValidation = true;
            this.textBox2.CausesValidation = true;
            textBox1.Validating += new System.ComponentModel.CancelEventHandler(textBox1_Validating);
            textBox2.Validating += new System.ComponentModel.CancelEventHandler(textBox2_Validating);

        }

        private void textBox1_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (textBox1.Text.Length == 0)
            {
                e.Cancel = true;
                errorProvider1.SetError(this.textBox1, "A value is required.");
            }
            else
            {
                e.Cancel = false;
                this.errorProvider1.SetError(this.textBox1, "");
            }
        }

        private void textBox2_Validating(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (textBox2.Text.Length == 0)
            {
                e.Cancel = true;
                errorProvider1.SetError(this.textBox2, "A value is required.");
            }
            else
            {
                e.Cancel = false;
                this.errorProvider1.SetError(this.textBox2, "");
            }
        }



        private void btnSave_Click(object sender, EventArgs e)
        {
            if (this.ValidateChildren()) //will examine all the children of the current control, causing the Validating event to occur on a control 
            {
                // Validated! - Do something then
            }

        }
    }
}

Примечание:

textBox1_Validating, textBox2_Validating...do the rule check
0 голосов
/ 28 августа 2009

Что не так с подходом валидатора? Это вполне приемлемо, и вы можете написать свои собственные, чтобы реализовать свои собственные правила.

...