Kontrollkästchen für nullbaren Booleschen Wert

97

Mein Modell hat einen Booleschen Wert, der nullwertfähig sein muss

public bool? Foo
{
   get;
   set;
}

also in meinem Razor cshtml habe ich

@Html.CheckBoxFor(m => m.Foo)

außer das funktioniert nicht. Es wird auch nicht mit (bool) gecastet. Wenn ich mache

@Html.CheckBoxFor(m => m.Foo.Value)

Das erzeugt keinen Fehler, bindet aber nicht an mein Modell, wenn es veröffentlicht wird, und foo wird auf null gesetzt. Was ist der beste Weg, um Foo auf der Seite anzuzeigen und es in einem Beitrag an mein Modell zu binden?

DMulligan
quelle
2
Dieser Thread ignoriert das Problem, dass ich in der Lage sein muss, null als dritten Wert zu erkennen. Eine Formularübermittlung mit deaktiviertem Kontrollkästchen und eine Formularübermittlung, bei der das Kontrollkästchen nicht angezeigt wurde, sind zwei verschiedene Szenarien, die berücksichtigt werden müssen.
DMulligan

Antworten:

107

Ich habe es zum Arbeiten

@Html.EditorFor(model => model.Foo) 

und dann eine Boolean.cshtml in meinem EditorTemplates-Ordner erstellen und bleiben

@model bool?

@Html.CheckBox("", Model.GetValueOrDefault())

Innerhalb.

DMulligan
quelle
2
Funktioniert bei mir. Vielen Dank!
Samuel
1
Während ich mit nullbaren booleschen Typen arbeitete, verwendete ich diese '' '@ Html.CheckBoxFor (m => m.Foo.HasValue)' ''
Gaurav Aroraa
9
@GauravKumarArora Sie erstellen ein Kontrollkästchen für die nullfähige HasValueEigenschaft, jedoch nicht für den tatsächlichen booleschen Wert. Dadurch wird ein Kontrollkästchen erstellt, unabhängig vom zugrunde liegenden Wert. Wenn die Nullable einen Wert hat, ist dies immer wahr.
Siewers
1
Dies ist definitiv die sehr saubere und flexible Lösung dieser Seite. +1
T-Moty
2
Nur eine Anmerkung: 1. Sie müssen diese Datei in "Views / Shared / EditorTemplates" speichern. 2. Sie müssen diese Datei "Boolean.cshtml" nennen
Jarrette
46

Antwort in ähnlicher Frage gefunden - Nullable Bool als CheckBox rendern . Es ist sehr einfach und funktioniert einfach:

@Html.CheckBox("RFP.DatesFlexible", Model.RFP.DatesFlexible ?? false)
@Html.Label("RFP.DatesFlexible", "My Dates are Flexible")

Es ist wie eine akzeptierte Antwort von @afinkelstein, außer dass wir keine spezielle 'Editor-Vorlage' benötigen.

resnyanskiy
quelle
1
Der andere Vorteil gegenüber der akzeptierten Antwort besteht darin, dass Sie von Fall zu Fall entscheiden können, ob null gleich wahr oder falsch sein soll, wie es die Logik Ihrer Anwendung vorschreibt, anstatt einen einzigen Standard in der gesamten Anwendung zu haben (oder zwei Editoren zu erstellen) Vorlagen!).
ChrisFox
25

Ich habe bool? IsDisabled { get; set; }in Model. Eingefügt, wenn in Ansicht.

<div class="inputClass" id="disabled">
    <div>
    @if(Model.IsDisabled==null)
    {
        Model.IsDisabled = false;
    }           
    @Html.CheckBoxFor(model => model.IsDisabled.Value)         
    </div>
</div> 
VitalyB
quelle
1
Nicht sehr gut, da Sie den Wert im Modell ändern und in vielen Fällen false nicht gleich null ist. Ich habe dir einen Punkt gegeben, weil es ein schöner Versuch ist. Beachten Sie, dass ich auch Darin zustimme :)
Samuel
7
Es wird davon abgeraten , einen Punkt zu nennen, da dies ein guter Versuch ist, da er anderen Lesern möglicherweise fälschlicherweise anzeigt, dass die Antwort nützlich ist , wenn Probleme auftreten.
Chris
Eine besser aussehende Alternative besteht darin, den Booleschen Wert im Controller auf false zu setzen, wenn er null ist, bevor die Seite gerendert wird. Verwenden Sie jedoch weiterhin die .Value-Idee.
Graham Laight
15

Mein Modell hat einen Booleschen Wert, der nullwertfähig sein muss

Warum? Das macht keinen Sinn. Ein Kontrollkästchen hat zwei Status: Aktiviert / Deaktiviert oder Richtig / Falsch, wenn Sie so wollen. Es gibt keinen dritten Zustand.

Oder warten Sie, bis Sie Ihre Domain-Modelle in Ihren Ansichten anstelle von Ansichtsmodellen verwenden? Das ist dein Problem. Die Lösung für mich besteht also darin, ein Ansichtsmodell zu verwenden, in dem Sie eine einfache boolesche Eigenschaft definieren:

public class MyViewModel
{
    public bool Foo { get; set; }
}

Jetzt muss Ihre Controller-Aktion dieses Ansichtsmodell an die Ansicht übergeben und das richtige Kontrollkästchen generieren.

Darin Dimitrov
quelle
26
Es gibt unzählige verschiedene Gründe, warum die foo-Eingabe möglicherweise überhaupt nicht auf der Seite angezeigt wird. Wenn es so aussieht, kann ich davon ausgehen, dass es einen Wert gibt, aber ich muss unterscheiden können, wann das Modell mit foo als deaktiviert gepostet wird und wann foo nicht angezeigt wird.
DMulligan
@KMulligan, wie wäre es, wenn Sie Ihrem Ansichtsmodell eine weitere boolesche Eigenschaft hinzufügen, die angibt, ob Sie ein Kontrollkästchen für die Foo-Eigenschaft rendern sollen und das in einem ausgeblendeten Feld gespeichert werden kann, damit Sie beim Postback wissen, ob Sie den Foo-Wert berücksichtigen sollten oder nicht.
Darin Dimitrov
Ich habe das ursprünglich gemacht, aber ich habe Tonnen dieser primitiven Werttypen erhalten, die aus verschiedenen Gründen in meinen Formularen enthalten sein können oder nicht. Das Hinzufügen von Booleschen Werten für alle fühlte sich albern an. Als ich Nullables googelte, dachte ich, das wäre der richtige Weg.
DMulligan
4
@KMulligan, Sie könnten Helfer, Editorvorlagen usw. schreiben, damit Sie dies nicht für jede Eigenschaft wiederholen müssen, die solche Eigenschaften hat.
Darin Dimitrov
25
@DarinDimitrov, Sie haben die Tatsache, dass das OP sagte, er müsse in der Lage sein, den "Null" -Aspekt des Bool darzustellen, deutlich übersehen oder ignoriert. Sie haben sich darüber aufgehängt, dass er die CheckBoxFor verwenden wollte. Anstatt ihm zu sagen, dass er "schlechten Code" geschrieben hat, weil er sein Domain-Modell verwendet hat (was er möglicherweise getan hat oder nicht) und ihm gesagt hat, dass Kontrollkästchen entweder aktiviert oder deaktiviert sind, sollten Sie versucht haben, seine Frage zu beantworten oder nicht zu antworten.
Andrew Steitz
7

Komplizieren eines Grundelements mit ausgeblendeten Feldern, um zu klären, ob False oder Null nicht empfohlen wird.

Das Kontrollkästchen ist nicht das, was Sie verwenden sollten - es hat wirklich nur einen Status: Aktiviert . Sonst könnte es alles sein.

Wenn Ihr Datenbankfeld ein nullfähiger Boolescher Wert ( bool?) ist, sollte die UX 3 Optionsfelder verwenden, wobei die erste Schaltfläche Ihr "Aktiviert" darstellt, die zweite Schaltfläche "Nicht überprüft" und die dritte Schaltfläche Ihre Null darstellt, unabhängig von der Semantik null bedeutet. Sie können eine <select><option>Dropdown-Liste verwenden, um Immobilien zu speichern, aber der Benutzer muss zweimal klicken und die Auswahlmöglichkeiten sind bei weitem nicht so augenblicklich klar.

  1     0      null 
True  False  Not Set
Yes   No     Undecided
Male  Female Unknown
On    Off    Not Detected

Die RadioButtonList, definiert als Erweiterung mit dem Namen RadioButtonForSelectList, erstellt die Optionsfelder für Sie, einschließlich des ausgewählten / aktivierten Werts, und legt die fest <div class="RBxxxx"> dass Sie mithilfe von CSS Ihre Optionsfelder horizontal (Anzeige: Inline-Block), vertikal oder vertikal schalten können in einer Tabellenform (Anzeige: Inline-Block; Breite: 100px;)

Im Modell (ich verwende Zeichenfolge, Zeichenfolge für die Wörterbuchdefinition als pädagogisches Beispiel. Sie können bool?, Zeichenfolge verwenden)

public IEnumerable<SelectListItem> Sexsli { get; set; }
       SexDict = new Dictionary<string, string>()
        {
                { "M", "Male"},
                { "F", "Female" },
                { "U", "Undecided" },

        };

        //Convert the Dictionary Type into a SelectListItem Type
        Sexsli = SexDict.Select(k =>
              new SelectListItem
              {
                  Selected = (k.Key == "U"),
                  Text = k.Value,
                  Value = k.Key.ToString()
              });

<fieldset id="Gender">
<legend id="GenderLegend" title="Gender - Sex">I am a</legend>
    @Html.RadioButtonForSelectList(m => m.Sexsli, Model.Sexsli, "Sex") 
        @Html.ValidationMessageFor(m => m.Sexsli)
</fieldset>

public static class HtmlExtensions
{
public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
    IEnumerable<SelectListItem> listOfValues,
    String rbClassName = "Horizontal")
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var sb = new StringBuilder();

if (listOfValues != null)
{
    // Create a radio button for each item in the list 
    foreach (SelectListItem item in listOfValues)
    {
        // Generate an id to be given to the radio button field 
        var id = string.Format("{0}_{1}", metaData.PropertyName, item.Value);

        // Create and populate a radio button using the existing html helpers 
        var label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text));

        var radio = String.Empty;

        if (item.Selected == true)
        {
            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id, @checked = "checked" }).ToHtmlString();
        }
        else
        {
            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString();

        }// Create the html string to return to client browser
        // e.g. <input data-val="true" data-val-required="You must select an option" id="RB_1" name="RB" type="radio" value="1" /><label for="RB_1">Choice 1</label> 

        sb.AppendFormat("<div class=\"RB{2}\">{0}{1}</div>", radio, label, rbClassName);
    }
}

return MvcHtmlString.Create(sb.ToString());
}
}
Jules Bartow
quelle
4

Für mich bestand die Lösung darin, das Ansichtsmodell zu ändern. Stellen Sie sich vor, Sie suchen nach Rechnungen. Diese Rechnungen können bezahlt werden oder nicht. Ihre Suche hat also drei Optionen: Bezahlt, Unbezahlt oder "Es ist mir egal".

Ich hatte das ursprünglich als Bool eingestellt? Feld:

public bool? PaidInvoices { get; set; }

Dies brachte mich dazu, über diese Frage zu stolpern. Am Ende habe ich einen Aufzählungstyp erstellt und dies wie folgt behandelt:

@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.NotSpecified, new { @checked = true })
@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.Yes) 
@Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.No)

Natürlich hatte ich sie in Etiketten eingewickelt und Text angegeben. Ich meine, hier ist eine weitere Option, die ich in Betracht ziehen sollte.

Paul
quelle
Dies ist eine nützliche Darstellung. Wenn Sie nur über begrenzten Speicherplatz verfügen, können Sie dies auch mit Dropdown / Auswahl erreichen, außer dass die Auswahl einer Option mit zwei Klicks erfolgt. Wie bei Auswahlmöglichkeiten können Optionsfelder zwischen Browsern sehr unterschiedlich aussehen, was Designer-Hölle sein kann.
Savage
4

Das Kontrollkästchen bietet nur 2 Werte (wahr, falsch). Nullable Boolean hat 3 Werte (true, false, null), daher ist es unmöglich, dies mit einem Kontrollkästchen zu tun.

Eine gute Option ist die Verwendung eines Dropdowns.

Modell

public bool? myValue;
public List<SelectListItem> valueList;

Regler

model.valueList = new List<SelectListItem>();
model.valueList.Add(new SelectListItem() { Text = "", Value = "" });
model.valueList.Add(new SelectListItem() { Text = "Yes", Value = "true" });
model.valueList.Add(new SelectListItem() { Text = "No", Value = "false" });

Aussicht

@Html.DropDownListFor(m => m.myValue, valueList)
Gudradain
quelle
1
Wenn Sie nicht möchten, dass der Benutzer alle 3 möglichen Werte festlegen kann, spielt es keine Rolle, dass Sie nicht alle 3 mit nur einem Kontrollkästchen darstellen können.
Dave
@ Dave Ich denke du hast den Punkt komplett verpasst. Das OP hat einen Bool? was bedeutet, dass er einen der 3 möglichen Werte auswählen muss. Wenn er nur wahr oder falsch haben wollte, sollte er einen Bool verwenden und keinen Bool?
Gudradain
1
Tatsächlich weist der Kommentar des OP auf das Gegenteil hin: Wenn das Formular ein Kontrollkästchen enthält, möchte er nicht, dass Null überhaupt eine Option ist, da diese Option abgedeckt ist, wenn es ein anderes Formular ohne Kontrollkästchen gibt.
Dave
Ich habe dieselbe Strategie auf einer Suchseite verwendet. Sie können nach Ja, Nein suchen oder den Booleschen Wert bei Ihrer Suche ignorieren. Es gibt einen Anwendungsfall! : D
Jess
4

Ich würde tatsächlich eine Vorlage dafür erstellen und diese Vorlage mit einem EditorFor () verwenden.

So habe ich es gemacht:

  1. Meine Vorlage erstellen, bei der es sich im Grunde um eine Teilansicht handelt, die ich im EditorTemplates-Verzeichnis unter "Freigegeben" unter "Ansichten" als (zum Beispiel) :: erstellt habe CheckboxTemplate:

    @using System.Globalization    
    @using System.Web.Mvc.Html    
    @model bool?
    
    @{
        bool? myModel = false;
        if (Model.HasValue)
        {
            myModel = Model.Value;
        }
    }
    
    <input type="checkbox" checked="@(myModel)" name="@ViewData.TemplateInfo.HtmlFieldPrefix" value="True" style="width:20px;" />
  2. Verwenden Sie es so (in jeder Ansicht):

    @ Html.EditorFor (x => x.MyNullableBooleanCheckboxToBe, "CheckboxTemplate")

Das ist alles.

Vorlagen sind in MVC so leistungsfähig, verwenden Sie sie. Sie können eine ganze Seite als Vorlage erstellen, die Sie mit dem verwenden würden @Html.EditorFor(). vorausgesetzt, Sie übergeben das Ansichtsmodell im Lambda-Ausdruck.

t_plusplus
quelle
3

Der sauberste Ansatz, den ich finden könnte, besteht darin, die verfügbaren Erweiterungen zu erweitern und HtmlHelpergleichzeitig die vom Framework bereitgestellten Funktionen wiederzuverwenden.

public static MvcHtmlString CheckBoxFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, bool?>> expression, IDictionary<string, object> htmlAttributes) {

    ModelMetadata modelMeta = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
    bool? value = (modelMeta.Model as bool?);

    string name = ExpressionHelper.GetExpressionText(expression);

    return htmlHelper.CheckBox(name, value ?? false, htmlAttributes);
}

Ich habe versucht, den Ausdruck so zu formen, dass ein direkter Durchgang zum Eingeborenen möglich ist, CheckBoxFor<Expression<Func<T, bool>>>aber ich denke nicht, dass dies möglich ist.

Red Taz
quelle
Ich musste die Signatur der Methode ändern, damit dies für mich funktioniert: Ich habe es verwendet public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)und es hat wunderbar funktioniert.
Pierre-Loup Pagniez
1

Ich hatte in der Vergangenheit ein ähnliches Problem.

Erstellen Sie eine Checkbox-Eingabe in HTML und setzen Sie das Attribut name = "Foo". Dies sollte weiterhin ordnungsgemäß veröffentlicht werden.

<input type="checkbox" name="Foo" checked="@model.Foo.Value" /> Foo Checkbox<br />
Chris Lucian
quelle
Dies scheint nicht zu posten, ich werde immer noch null.
DMulligan
2
Dies könnte Probleme mit der Modellbindung haben
yoel halb
0

Überprüfen Sie einfach den Nullwert und geben Sie false zurück:

@{ bool nullableValue = ((Model.nullableValue == null) || (Model.nullableValue == false)) ? false : true; }
@Html.CheckBoxFor(model => nullableValue)
Rodrigo Boratto
quelle
Sie wissen, dass Ihre Eigenschaftsmetadaten verloren gehen, oder? Client-seitige Validierung, Feldname und ID usw.
T-Moty
0

Ich stand auch vor dem gleichen Problem. Ich habe den folgenden Ansatz versucht, um das Problem zu lösen, da ich die Datenbank nicht ändern und den EDMX erneut generieren möchte.

@{

   bool testVar = (Model.MYVar ? true : false);

 }

<label>@Html.CheckBoxFor(m => testVar)testVar</label><br />
Amit Kumar
quelle
0

Wenn Sie eine EditorTemplate für ein Modell erstellen, das einen nullbaren Bool enthält ...

  • Teilen Sie den nullbaren Bool in zwei Boolesche Werte auf:

    // Foo is still a nullable boolean.
    public bool? Foo 
    {
        get 
        { 
            if (FooIsNull)
                return null;
    
            return FooCheckbox;  
        }
        set
        {
            FooIsNull   = (value == null);
            FooCheckbox = (value ?? false);
        }
    }
    
    // These contain the value of Foo. Public only so they are visible in Razor views.
    public bool FooIsNull { get; set; }
    public bool FooCheckbox { get; set; }
  • Innerhalb der Editorvorlage:

    @Html.HiddenFor(m => m.FooIsNull)
    
    @if (Model.FooIsNull)
    {
        // Null means "checkbox is hidden"
        @Html.HiddenFor(m => m.FooCheckbox)
    }
    else
    {
        @Html.CheckBoxFor(m => m.FooCheckbox)
    }
  • Postbacken Sie nicht die ursprüngliche Eigenschaft Foo, da diese jetzt aus FooIsNull und FooCheckbox berechnet wird.


quelle
0

Alle oben genannten Antworten hatten ihre eigenen Probleme. Der einfachste / sauberste Weg IMO ist es, einen Helfer zu erstellen

MVC5 Rasiermesser

App_Code / Helpers.cshtml

@helper CheckBoxFor(WebViewPage page, string propertyName, bool? value, string htmlAttributes = null)
{
    if (value == null)
    {
        <div class="checkbox-nullable">
            <input type="checkbox" @page.Html.Raw(htmlAttributes)>
        </div>
    }
    else if (value == true)
    {
        <input type="checkbox" value="true" @page.Html.Raw(htmlAttributes) checked>
    }
    else
    {
        <input type="checkbox" value="false" @page.Html.Raw(htmlAttributes)>
    }
}

Verwendung

@Helpers.CheckBoxFor(this, "IsPaymentRecordOk", Model.IsPaymentRecordOk)

In meinem Szenario bedeutet ein nullbares Kontrollkästchen, dass ein Mitarbeiter dem Kunden die Frage noch nicht gestellt hat. Sie ist daher in ein Kontrollkästchen eingeschlossen, damit Sie den .checkbox-nullableStil entsprechend gestalten und dem Endbenutzer helfen können, zu erkennen, dass es weder truenoch istfalse

CSS

.checkbox-nullable {
    border: 1px solid red;
    padding: 3px;
    display: inline-block;
}
Zanderwar
quelle
0

Erweiterungsmethoden:

        public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression)
        {
            return htmlHelper.CheckBoxFor<TModel>(expression, null);
        }
        public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            bool? isChecked = null;
            if (metadata.Model != null)
            {
                bool modelChecked;
                if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
                {
                    isChecked = modelChecked;
                }
            }
            return htmlHelper.CheckBox(ExpressionHelper.GetExpressionText(expression), isChecked??false ,  htmlAttributes);
        }
Jim
quelle
0

Optionsfelder sind nützlich, aber Sie können die Auswahl nicht aufheben . Wenn Sie dieses Verhalten wünschen, sollten Sie ein Dropdown-Menü verwenden. Der folgende Code generiert das SelectListund bindet erfolgreich an einen nullbaren Booleschen Wert:

public static SelectList GetBoolTriState()
{
    var items = new List<SelectListItem>
    {
        new SelectListItem {Value = "", Text = ""},
        new SelectListItem {Value = "True", Text = "Yes"},
        new SelectListItem {Value = "False", Text = "No"},
    };

    return new SelectList(items, "Value", "Text");
}
Wild
quelle
-1
@{  bool testVar = ((bool)item.testVar ? true : false); }
  @Html.DisplayFor(modelItem => testVar)
edrdesigner
quelle
1
Dies löst eine Ausnahme aus, wenn testVar null ist.
Danwyand
-1

Sie können so versuchen

@Html.TextBoxFor(m => m.foo, new {   type = "checkbox" })
Yağmur Bilgin
quelle
-4

Dies ist eine alte Frage, und die vorhandenen Antworten beschreiben die meisten Alternativen. Aber es gibt eine einfache Option, wenn Sie Bool haben? in Ihrem Ansichtsmodell, und Sie interessieren sich nicht für null in Ihrer Benutzeroberfläche:

@Html.CheckBoxFor(m => m.boolValue ?? false);
Jeff Dege
quelle
2
Hast du das versucht? Da die xxxFor-Methoden einen Member-Ausdruck erwarten, würde ich gutes Geld darauf wetten, dass dies zu einem Laufzeitfehler führt. Es wird versucht, die Zieleigenschaft oder das Zielfeld zu bestimmen, ohne an dieser Stelle etwas auszuwerten.
JRoughan
2
Laufzeitfehler "Vorlagen können nur mit Feldzugriff, Eigenschaftszugriff, eindimensionalem Array-Index oder benutzerdefinierten Indexerausdrücken mit einzelnen Parametern verwendet werden."
ePezhman