2

I'm in a bit of trouble here learning C# and mvc4.

The Problem occurs in the Filter part of my application. I have an ViewModel that grabs the list of "Listar_Produtos" of the database, and some fields for searching options.

What I intend to do is make the filter accept any field, even if it's null values. Because i'll make the filter based on these paramters.

I Have an Viewmodel:

using Foolproof;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace Ecommerce.Models.Repository
{
    public class Produto_Repository
    {
        public class Index_Listar_Produtos
        {
            public List<Listar_Produto> Index_List_Produto { get; set; }

            [Display(Name = "Data de Cadastro Inicial")]
            [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
            public Nullable<DateTime> CadastroInicialData { get; set; }

            [Display(Name = "Data de Cadastro Final")]
            [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
            [GreaterThanOrEqualTo("CadastroInicialData", ErrorMessage = "\"Data Inicial\", deve ser maior que \"Data Final\"")]
            public Nullable<DateTime> CadastroFinalData { get; set; }
        }
    }
}

And I have the following View:

<td>
    @Html.LabelFor(Model => Model.CadastroInicialData)<br />
    @Html.TextBoxFor(Model => Model.CadastroInicialData, "{0:dd/MM/yyyy}")
    @Html.ValidationMessageFor(Model => Model.CadastroInicialData)
</td>
<td>
    @Html.LabelFor(Model => Model.CadastroFinalData)<br />
    @Html.TextBoxFor(Model => Model.CadastroFinalData, "{0:dd/MM/yyyy}")
    @Html.ValidationMessageFor(Model => Model.CadastroFinalData)
</td>

In my Controller I have:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Filtro(Produto_Repository.Index_Listar_Produtos ViewModel)
{
    if (!ModelState.IsValid)
    {
        Produto_Repository.Index_Listar_Produtos Model_list = Produto_Repository.GetProdutoByAll();
        ViewModel.Index_List_Produto = Model_list.Index_List_Produto;
        return View("Index", ViewModel);
    }
}

Where "Produto_Repository.GetProdutoByAll();" returns the list of "Produtos" again.

The code works fine and well if I provide dates in the form. The dates are in "pt-BR" format: 23/03/2013.

But if I provide nothing in the Fields (both Datefields in my View), than the "if(!ModelState.IsValid)" returns true and enters the "if", because both "CadastroInicialData" and "CadastroFinalData" comes with null values

The desired behavior is that the ViewModel could accept null or empty values that are granted by "Nullable" or "DateTime?".

I Tryed to insert values to the nullable date fields doing the following:

if (ViewModel.CadastroInicialData == null)
    ViewModel.CadastroInicialData = Convert.ToDateTime("01/01/2013");
if (ViewModel.CadastroFinalData == null)
    ViewModel.CadastroFinalData = Convert.ToDateTime("01/01/2013");

But now the ViewModel returns the following error: "is an invalid date format"

One note is that i'm using the following 'solution' for converting datetimes for pt-BR dateformat on the following question: Format datetime in asp.net mvc 4

How I make the ViewModel accept null values when the text fields are not filled with dates? I'm kinda confused here. I apreciate any help ! Thanks !

Community
  • 1
  • 1
Marcos Santini
  • 103
  • 1
  • 3
  • 11
  • It is your data annotations, please take a look at my response to understand why. – Tommy Nov 27 '13 at 16:39
  • hi @Tommy, The helper of data annotations is a "GreaterThanOrEqualTo" not "GreaterThan". If the datetime are both null, the validation will be success. – Lin Nov 27 '13 at 16:50
  • ...are you 100% on that? What is the error on your model state validation? – Tommy Nov 27 '13 at 16:51
  • I believe the problem comes from "ApplyFormatInEditMode = true". I post a answer just let you work around the problem. If you really want to fix it, take a look this link, it may helps you. http://stackoverflow.com/questions/13253964/displayformat-applyformatineditmode – Lin Nov 27 '13 at 17:16

4 Answers4

3

You can add another properties to your Model class, then use them to determine if the value is null or not. Also implement them in your view. See below code:

Model

public class Index_Listar_Produtos
{
    public List<Listar_Produto> Index_List_Produto { get; set; }

    [Display(Name = "Data de Cadastro Inicial")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    public Nullable<DateTime> CadastroInicialData { get; set; }

    [Display(Name = "Data de Cadastro Final")]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
    [GreaterThanOrEqualTo("CadastroInicialData", ErrorMessage = "\"Data Inicial\", deve ser maior que \"Data Final\"")]
    public Nullable<DateTime> CadastroFinalData { get; set; }


    public string GetStringTypeCadastroInicialData
    {
        get { return CadastroInicialData != null ? CadastroInicialData.Value.ToShortDateString() : DateTime.MinValue.ToShortDateString()(Or empty string ); }
    }

    public string GetStringTypeCadastroFinalData
    {
        get { return CadastroInicialData != null ? CadastroFinalData.Value.ToShortDateString() : DateTime.Now.ToShortDateString(); }
    }

}

View

<td>
    @Html.LabelFor(Model => Model.GetStringTypeCadastroInicialData)<br />
    @Html.TextBoxFor(Model => Model.GetStringTypeCadastroInicialData)
    @Html.ValidationMessageFor(Model => Model.GetStringTypeCadastroInicialData)
</td>
<td>
    @Html.LabelFor(Model => Model.GetStringTypeCadastroFinalData)<br />
    @Html.TextBoxFor(Model => Model.GetStringTypeCadastroFinalData)
    @Html.ValidationMessageFor(Model => Model.GetStringTypeCadastroFinalData)
</td>
Lin
  • 15,078
  • 4
  • 47
  • 49
  • Lin, thanks for the answer. Your code almost save my day, the only thing "wrong" is that the TextBoxFor is being "set" to the default value when I enter the page(because the value is null by that time). The textbox is an input for an search interval and cannot start filled up. Only in controller it'd be awesome if the nulls value could be converted. – Marcos Santini Nov 27 '13 at 17:13
  • @MarcosSantini, I think if the value is null by the time you can use "" instead of use ""01/01/2013". Also you don't need to use "{0:dd/MM/yyyy}" Datetime formater in the TextBoxFor. Let me update this. – Lin Nov 27 '13 at 17:22
  • Lin, just noticed that this is wrong. The "get { return CadastroInicialData != null ? CadastroFinalData.Value.ToShortDateString() : ""; }" will never be touched. Because the view will try to set the "GetStringTypeCadastroInicialData" and the CadastroFinalData will never be changed by the user trying to search anything ! =( – Marcos Santini Nov 27 '13 at 17:35
  • @MarcosSantini, I'm not very clear about your case. but from what you said, it Looks like you can not use empty string for CadastroInicialData and CadastroFinalData. You need to pass a appropriate datetime for both or maybe only for CadastroFinalData.I updated my post again, try it. – Lin Nov 27 '13 at 18:37
2

Instead of

public Nullable<DateTime> CadastroInicialData { get; set; }

try

public DateTime? CadastroInicialData { get; set; }

and then to see if it has a value, you can do:

if (CadastroInicialData.HasValue)
{
    // do your processing here
}
Matt Millican
  • 4,044
  • 4
  • 38
  • 55
  • 6
    This will not help at all ! DateTime? is equal to Nullable, and I have used both sintax without any advancement. And this don't solve my problem that is passing nullable Dates through ModelView in Controlller. =P – Marcos Santini Nov 27 '13 at 16:09
1

Your model state is failing because of the GreaterThan data annotation on your second nullable DateTime value. null is not greater than null, so it is failing. You will either need to modify the GreaterThan data annotation to not compare if the values if they are null or remove that data annotation and do the comparison yourself. An example could be:

if(model.CadastroInicialData.HasValue() && model.CadastroFinalData.HasValue() && model.CadastroInicialData.Value > model.CadastroFinalData.Value){
    ModelState.AddModelError("CadastroInicialData", "CadastroInicialData must be less than CadastroFinalData");
}

if(!modelState.IsValid()){
   Produto_Repository.Index_Listar_Produtos Model_list = Produto_Repository.GetProdutoByAll();
   ViewModel.Index_List_Produto = Model_list.Index_List_Produto;
   return View("Index", ViewModel);
}
Tommy
  • 39,592
  • 10
  • 90
  • 121
0

Instead of using Convert.ToDateTime use DateTime.TryParseExact, and you can specify format and culture. The below code should work and fix the errors:

if (ViewModel.CadastroInicialData == null)
    ViewModel.CadastroInicialData = DateTime.TryParseExact(text, "MM/dd/yyyy",
                           CultureInfo.InvariantCulture,
                           DateTimeStyles.None,
                           out "01/01/2013");
if (ViewModel.CadastroFinalData == null)
    ViewModel.CadastroFinalData = DateTime.TryParseExact(text, "MM/dd/yyyy",
                           CultureInfo.InvariantCulture,
                           DateTimeStyles.None,
                           out "01/01/2013");
apollosoftware.org
  • 12,161
  • 4
  • 48
  • 69
  • Can't get this to work, when I use DateTime.TryParseExact. The return value is bool, and not String value that is expected. Anyways, what I really need is being able to pass "if (!ModelState.IsValid)" with null values to dates. I was trying to "set" the values manually because I could not get the ModelView Pass the "ModelState.isValid". I Really think it has something to do with the "fix" I did in the following link http://stackoverflow.com/questions/11272851/format-datetime-in-asp-net-mvc-4 It's probally a error in dateformat conversion from pt-BR to en-US from ModelView and view. – Marcos Santini Nov 27 '13 at 16:29