Флажок для логического значения, допускающего значение NULL

97

Моя модель имеет логическое значение, которое должно допускать значение NULL

public bool? Foo
{
   get;
   set;
}

поэтому в моем Razor cshtml у меня есть

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

кроме этого не работает. Также не выполняется приведение с помощью (bool). Если я сделаю

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

это не создает ошибки, но не привязывается к моей модели при публикации, а для foo установлено значение null. Как лучше всего отобразить Foo на странице и привязать его к моей модели в публикации?

DMulligan
источник
См .: stackoverflow.com/questions/2490790/…
Майкл Мэддокс
2
Этот поток игнорирует проблему, потому что я должен иметь возможность определять null как третье значение. Отправка формы со снятым флажком и отправка формы, где флажок не отображался, - это два разных сценария, которые необходимо учитывать.
DMulligan 01

Ответы:

107

Я получил это работать с

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

а затем создайте Boolean.cshtml в папке EditorTemplates и вставьте

@model bool?

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

внутри.

DMulligan
источник
2
Работает для меня. Спасибо!
Самуэль
1
При работе с логическими типами, допускающими значение NULL, я использовал это '' '@ Html.CheckBoxFor (m => m.Foo.HasValue)' ''
Гаурав Арораа
9
@GauravKumarArora Вы создаете флажок для HasValueсвойства, допускающего значение NULL , но не для фактического логического значения. Это создаст установленный флажок, если независимо от базового значения. Если обнуляемый объект имеет значение, оно всегда будет истинным.
Siewers
1
Это, безусловно, очень чистое и гибкое решение этой страницы. +1
T-moty
2
просто примечание: 1. вы должны сохранить этот файл в "Views / Shared / EditorTemplates" 2. вы должны назвать этот файл "Boolean.cshtml"
Jarrette
46

Нашел ответ в аналогичном вопросе - Отображение Nullable Bool как CheckBox . Это очень просто и просто работает:

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

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

Реснянский
источник
1
Другое преимущество этого по сравнению с принятым ответом заключается в том, что вы можете решить, должно ли значение null приравниваться к истине или ложь в каждом конкретном случае, как того требует логика вашего приложения, вместо того, чтобы иметь одно значение по умолчанию для всего приложения (или сделать два редактора шаблоны!).
ChrisFox
25

У меня bool? IsDisabled { get; set; }в модели. Вставляется, если в просмотре.

<div class="inputClass" id="disabled">
    <div>
    @if(Model.IsDisabled==null)
    {
        Model.IsDisabled = false;
    }           
    @Html.CheckBoxFor(model => model.IsDisabled.Value)         
    </div>
</div> 
ВиталийБ
источник
1
Не очень хорошо, потому что вы меняете значение в модели, и во многих случаях false не равно нулю. Я поставил вам точку, потому что это хорошая попытка. Обратите внимание, что я также согласен с Дарином :)
Самуэль
7
Не рекомендую ставить балл, потому что это хорошая попытка, так как это может неверно указать другим читателям, что ответ полезен, когда есть проблемы.
Крис
Более привлекательной альтернативой является установка логического значения false в контроллере, если оно равно null, перед рендерингом страницы, но все же использовать идею .Value.
Грэм Лэйт
15

Моя модель имеет логическое значение, которое должно допускать значение NULL

Зачем? В этом нет смысла. Флажок имеет два состояния: установлен / не установлен или True / False, если хотите. Третьего состояния нет.

Или подождите, вы используете модели своей предметной области в своих представлениях вместо моделей представлений? Это твоя проблема. Поэтому решение для меня - использовать модель представления, в которой вы определите простое логическое свойство:

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

и теперь действие вашего контроллера передаст эту модель представления представлению и сгенерирует соответствующий флажок.

Дарин Димитров
источник
26
Существует множество различных причин, по которым ввод foo может вообще не отображаться на странице. Если это действительно появляется, я могу предположить, что есть значение, но мне нужно иметь возможность различать, когда модель публикуется с foo как не отмеченная, и когда foo не отображается.
DMulligan
@KMulligan, как насчет включения другого логического свойства в вашу модель представления, указывающего, следует ли отображать флажок для свойства Foo и который можно сохранить в скрытом поле, чтобы при обратной передаче вы знали, следует ли вам учитывать значение Foo или не.
Дарин Димитров
Я сделал это изначально, но у меня есть тонны этих примитивных типов значений, которые могут быть включены или не включены в мои формы по разным причинам. Добавление логических значений для всех из них начало казаться глупым. Когда я искал значения с нулевым значением в Google, я решил, что это правильный путь.
DMulligan
4
@KMulligan, вы можете написать помощников, шаблоны редакторов, ... чтобы вам не приходилось повторять это для каждого свойства, имеющего такие свойства.
Дарин Димитров
25
@DarinDimitrov, вы явно упустили или проигнорировали тот факт, что ОП сказал, что он должен быть в состоянии представить "нулевой" аспект bool. Вы зациклились на том, что он хотел использовать CheckBoxFor. Вместо того, чтобы сказать ему, что он написал «плохой код», потому что он использовал свою модель предметной области (что он мог или не мог сделать), и сказать ему, что флажки либо отмечены, либо не отмечены, вы должны были попытаться ответить на его вопрос или не ответить.
Эндрю Стейтц
7

Усложнение примитива со скрытыми полями, чтобы выяснить, не рекомендуется ли значение False или Null.

Галочка не то , что вы должны использовать - это действительно имеет только одно состояние: Проверено . В противном случае это могло быть что угодно.

Когда поле вашей базы данных является логическим ( bool?), допускающим значение NULL , пользовательский интерфейс должен использовать 3-Radio Buttons, где первая кнопка представляет ваш «Checked», вторая кнопка представляет «Not Checked», а третья кнопка представляет ваш NULL, независимо от семантики нулевое означает. Вы можете использовать <select><option>раскрывающийся список, чтобы сохранить недвижимость, но пользователь должен щелкнуть дважды, и выбор не так мгновенно понятен.

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

RadioButtonList, определенный как расширение с именем RadioButtonForSelectList, создает для вас переключатели, включая выбранное / отмеченное значение, и устанавливает <div class="RBxxxx"> чтобы вы могли использовать css, чтобы переключать переключатели горизонтально (display: inline-block), вертикально или в виде таблицы (display: inline-block; width: 100px;)

В модели (я использую строку, строку для определения словаря в качестве педагогического примера. Вы можете использовать bool ?, строка)

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());
}
}
Жюль Бартоу
источник
4

Для меня решением было изменить модель представления. Предположим, вы ищете счета-фактуры. Эти счета могут быть оплачены или нет. Таким образом, у вашего поиска есть три варианта: Платный, Неоплачиваемый или «Мне все равно».

Я изначально установил это как bool? поле:

public bool? PaidInvoices { get; set; }

Это заставило меня наткнуться на этот вопрос. В итоге я создал тип Enum и обработал это следующим образом:

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

Конечно, я обернул их этикетками и указал текст, я просто имел в виду еще один вариант, который стоит рассмотреть.

Павел
источник
Это полезное представление. Если у вас ограниченное пространство, вы также можете добиться этого с помощью раскрывающегося списка / выбора, за исключением того, что для выбора параметра требуется два щелчка мышки. Как и в случае выбора, радиокнопки в разных браузерах могут выглядеть по-разному, что черт возьми.
Savage
4

Флажок предлагает вам только 2 значения (истина, ложь). Обнуляемое логическое значение имеет 3 значения (true, false, null), поэтому это невозможно сделать с помощью флажка.

Вместо этого можно использовать раскрывающийся список.

Модель

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

Контроллер

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" });

Посмотреть

@Html.DropDownListFor(m => m.myValue, valueList)
Гудрадаин
источник
1
Если вас не волнует, что пользователь может установить все 3 возможных значения, не имеет значения, что вы не можете представить все 3, используя только флажок.
Дэйв
@ Дэйв, я думаю, ты полностью упустил суть. У OP есть bool? Это означает, что ему нужно выбрать одно из 3 возможных значений. Если он хочет иметь только true или false, он должен использовать bool, а не bool?
Gudradain
1
Фактически, комментарий OP указывает на обратное - что если в форме есть флажок, он вообще не хочет, чтобы значение null было вариантом, потому что этот параметр покрывается, когда есть другая форма без флажка.
Дэйв
Я использовал ту же стратегию на странице поиска. Вы можете искать Да, Нет или игнорировать логическое значение при поиске. Там является вариант использования! : D
Джесс
4

Я бы фактически создал для него шаблон и использовал бы этот шаблон с EditorFor ().

Вот как я это сделал:

  1. Создать мой шаблон, который в основном представляет собой частичное представление, которое я создал в каталоге EditorTemplates, в разделе Общие, в разделе Представления назовите его как (например) 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. Используйте это так (в любом виде):

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

Вот и все.

Шаблоны настолько мощны в MVC, используйте их .. Вы можете создать всю страницу как шаблон, который вы будете использовать с @Html.EditorFor(); при условии, что вы передаете его модель представления в лямбда-выражении.

t_plusplus
источник
3

Самый чистый подход, который я мог придумать, - это расширить доступные расширения, HtmlHelperпри этом повторно используя функции, предоставляемые фреймворком.

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);
}

Я экспериментировал с «формированием» выражения, чтобы позволить прямой переход к носителю, CheckBoxFor<Expression<Func<T, bool>>>но я не думаю, что это возможно.

Красный Таз
источник
Мне пришлось изменить сигнатуру метода, чтобы это сработало для меня: я использовал, public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)и он отлично работал.
Пьер-Лу Паньез, 03
1

У меня была аналогичная проблема в прошлом.

Создайте поле флажка в HTML и установите атрибут name = "Foo". Это все равно должно публиковаться правильно.

<input type="checkbox" name="Foo" checked="@model.Foo.Value" /> Foo Checkbox<br />
Крис Лучиан
источник
Это не похоже на публикацию, я все еще получаю ноль.
DMulligan
2
У этого могут быть проблемы с привязкой модели
йоэль халб
0

Просто проверьте нулевое значение и верните ему false:

@{ bool nullableValue = ((Model.nullableValue == null) || (Model.nullableValue == false)) ? false : true; }
@Html.CheckBoxFor(model => nullableValue)
Родриго Боратто
источник
Вы знаете, что метаданные вашего ресурса будут потеряны, верно? Проверка на стороне клиента, имя и идентификатор поля и т. Д.
T-moty
0

Я тоже столкнулся с той же проблемой. Я попробовал следующий подход для решения проблемы, потому что я не хочу изменять БД и снова генерировать EDMX.

@{

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

 }

<label>@Html.CheckBoxFor(m => testVar)testVar</label><br />
Амит Кумар
источник
0

При создании EditorTemplate для модели, содержащей логическое значение, допускающее значение NULL ...

  • Разделите логическое значение, допускающее значение NULL, на 2 логических значения:

    // 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; }
  • В шаблоне редактора:

    @Html.HiddenFor(m => m.FooIsNull)
    
    @if (Model.FooIsNull)
    {
        // Null means "checkbox is hidden"
        @Html.HiddenFor(m => m.FooCheckbox)
    }
    else
    {
        @Html.CheckBoxFor(m => m.FooCheckbox)
    }
  • Не возвращайте исходное свойство Foo, потому что теперь оно вычисляется из FooIsNull и FooCheckbox.


источник
0

Все ответы выше связаны с собственными проблемами. Самый простой / чистый способ ИМО - создать помощника

Бритва MVC5

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)>
    }
}

использование

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

В моем сценарии флажок, допускающий значение NULL, означает, что сотрудник еще не задавал вопрос клиенту, поэтому он обернут в, .checkbox-nullableчтобы вы могли соответствующим образом стилизовать и помочь конечному пользователю определить, что это ни то, trueниfalse

CSS

.checkbox-nullable {
    border: 1px solid red;
    padding: 3px;
    display: inline-block;
}
Зандервар
источник
0

Методы расширения:

        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);
        }
Джим
источник
0

Радиокнопки полезны, но они не позволяют отменить выбор . Если вам нужно такое поведение, рассмотрите возможность использования раскрывающегося списка / выбора. Следующий код будет генерировать, SelectListи это успешно связывается с логическим значением, допускающим значение NULL:

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");
}
Дикарь
источник
-1
@{  bool testVar = ((bool)item.testVar ? true : false); }
  @Html.DisplayFor(modelItem => testVar)
edrdesigner
источник
1
это вызовет исключение, если testVar имеет значение null.
danwyand
-1

ты можешь попробовать вот так

@Html.TextBoxFor(m => m.foo, new {   type = "checkbox" })
Ягмур Билгин
источник
-4

Это старый вопрос, и существующие ответы описывают большинство альтернатив. Но есть один простой вариант, если у вас есть bool? в вашей модели просмотра, и вас не волнует null в вашем пользовательском интерфейсе:

@Html.CheckBoxFor(m => m.boolValue ?? false);
Джефф Деге
источник
2
Вы пробовали это? Поскольку методы xxxFor ожидают выражения члена, я готов поспорить, что это вызовет сбой во время выполнения. Он пытается определить целевое свойство или поле, но на данный момент ничего не оценивает.
JRoughan
2
ошибка времени выполнения «Шаблоны можно использовать только с доступом к полю, доступом к свойствам, индексом одномерного массива или выражениями настраиваемого индексатора с одним параметром».
ePezhman