Элемент массива DateTime не содержит правильное значение - PullRequest
0 голосов
/ 28 декабря 2018

Я создал простой массив DateTime, который содержит 3 элемента.Эти элементы настроены на использование значений трех различных DateTimePickers в моей форме.Прежде чем я углублюсь в использование массива, я должен убедиться, что он действительно использует правильные значения, и, похоже, он этого не делает.Вот мой код:

namespace Test
{
    public partial class Form1 : Form
    {
        DateTime[] monSchedule = new DateTime[3];

        public Form1()
        {
            InitializeComponent();

            monSchedule[0] = monStart.Value;
            monSchedule[1] = monEnd.Value;
            monSchedule[2] = monLunch.Value;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            setDefaults();
        }

        private void setDefaults()
        {
            monStart.Value = DateTime.Parse("00:00");
            monEnd.Value = DateTime.Parse("00:00");
            monLunch.Value = DateTime.Parse("00:00");
        }

        private void validate()
        {
            MessageBox.Show("You entered time " + monSchedule[0]);
        }

Когда я загружаю свою форму, setDefaults(); должен изменить значения на текущую дату со временем 00:00.Когда я нажимаю кнопку, чтобы показать значение в массиве, он вытягивает текущую дату и текущее время. Мне нужно вытащить любое текущее время в DateTimePicker.Поэтому, если пользователь вводит 10:00 в DateTimePicker (они отформатированы в формате ЧЧ: мм), то мне нужен MessageBox, чтобы сказать, что время 10:00.Если я изменю значение на 22:00, то мне нужно, чтобы в окне сообщения было указано время 22:00.и т. д. (Дата не имеет значения в моем сценарии, меня вообще не интересует, какая дата. Только время.)

Я подозреваю, что это может быть из-за порядка, в котором она написана.Массив, хранящий значение DateTimePicker, ДО setDefaults(); запускается?Если да, то как мне сделать значения элементов массива динамическими, поскольку значения DateTimePickers будут сильно меняться, и мне нужно, чтобы элементы массива обновлялись до последних значений?

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ:
-Использование Visual Studio
-Добавлен DateTimePickers в режиме конструктора, изменил формат на ЧЧ: мм там, не изменил значения по умолчанию в режиме конструктора
-Поддержка даты полностью, только сейчас касается времени

PS: я также боролся с тем, где объявить массив, чтобы он был доступен во многих других методах, и обнаружил, что мне нужно объявить инициализатор массива в public partial class Form1, но затем добавить элементы в массив в public Form1(), потому что это не позволило бы мне добавить их под public partial class Form1.Хотя я не знаю, правильно ли это, но, похоже, это сработало, когда я тестировал массив строк, поэтому я пошел с ним.

Ответы [ 3 ]

0 голосов
/ 28 декабря 2018

Примечание :
При чтении описания в вашем обновленном вопросе выясняется, что доступ к значениям DateTimePicker элементов управления осуществляется с помощью кнопки Click.Если это реальный сценарий, вам, вероятно, вообще не нужно поле массива DateTime: вы можете просто прочитать значения непосредственно из элементов управления DTP и использовать значения на месте .
В примере предполагается (в соответствии с вопросом), что вам все равно нужен этот массив.


Возможный способ продолжить:

  • Установить значения по умолчанию в событии Form.Load.Сразу после этого инициализируйте значения массива monSchedule, поэтому значения синхронизированы .Обратите внимание, что код обработчика события Form.Load (конечно) выполняется после конструктора класса (public Form1() { }): объект Form должен быть уже инициализирован.
  • Назначить обработчик событий всем элементам управления DateTimePicker (одно и то же событие для всех).Обработчик события используется для присвоения новых значений массиву monSchedule.Это может быть событие ValueChanged или, возможно, более общее событие Validating .Первый появляется каждый раз, когда вы изменяете любую часть значения времени (значение часа или минуты).Последний только тогда, когда контроль теряет фокус.Твой выбор.
  • Используйте объект sender в обработчике событий, чтобы определить, какой элемент управления вызвал событие, и обновить соответствующее значение массива.

Пример с использованием оператора switch и оператор case с предложением when:

Примечания :
1. Вам нужно C# 7.0+ чтобы использовать этот синтаксис switch.В противном случае вы можете переключиться, используя шаблон типа (см. Документы) или имя DateTimePicker (см. Пример).

2. Событие DTP_ValueChanged(object sender, EventArgs e) (ValueChangedобработчик) назначается всем элементам управления DateTimePicker.


public partial class Form1 : Form
{
    DateTime[] monSchedule = new DateTime[3];

    private void Form1_Load(object sender, EventArgs e)
    {
        SetDefaultDTPValues();
    }

    private void SetDefaultDTPValues()
    {
        monStart.Value = DateTime.Parse("00:00");
        monEnd.Value = DateTime.Parse("00:00");
        monLunch.Value = DateTime.Parse("00:00");

        monSchedule[0] = monStart.Value;
        monSchedule[1] = monEnd.Value;
        monSchedule[2] = monLunch.Value;
    }

    private void DTP_ValueChanged(object sender, EventArgs e)
    {
        switch (sender)
        {
            case DateTimePicker dtp when dtp.Equals(monStart):
                monSchedule[0] = dtp.Value;
                break;
            case DateTimePicker dtp when dtp.Equals(monEnd):
                monSchedule[1] = dtp.Value;
                break;
            case DateTimePicker dtp when dtp.Equals(monLunch):
                monSchedule[2] = dtp.Value;
                break;
        }
    }
}

Для события Button.Click:

private void button1_Click(object sender, EventArgs e)
{
    MessageBox.Show($"Start: {monSchedule[0].ToString("hh:mm tt")}  " +
                    $"End: {monSchedule[1].ToString("hh:mm tt")}  " +
                    $"Lunch: {monSchedule[2].ToString("hh:mm tt")}");
}

Если используемая версия C # не допускает этот синтаксис оператора switch, вы можете использовать DateTimePicker имя вместо (есть другие варианты, см. Примеры в Документах):

    private void DTP_ValueChanged(object sender, EventArgs e)
    {
        DateTimePicker dtp = sender as DateTimePicker;
        switch (dtp.Name)
        {
            case "monStart":
                monSchedule[0] = dtp.Value;
                break;
            case "monEnd":
                monSchedule[1] = dtp.Value;
                break;
            case "monLunch":
                monSchedule[2] = dtp.Value;
                break;
        }
    }
0 голосов
/ 29 декабря 2018

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

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

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


Давайте начнем с того, что мы знаем.Я получил это из двух вопросов и различных комментариев в обоих.

  1. У вас есть DateTimePicker элементы управления для начала, конца и обеда.Вас интересует только часть времени, поэтому для Format установлено значение «Пользовательский», а для CustomFormat установлено значение «ЧЧ: мм». Предположение: обед - фиксированная длина, поэтому время окончания не требуется.

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

  3. Вы написали код проверки (диапазонные тесты), чтобы определить, правильно ли введены значения, и вы можете показать метку с красными восклицательными знакамикогда этот тест не пройден.

  4. Вы определили, что становится слишком сложно, просто имея несколько элементов управления в форме.

Пока что, настолько хорошо.Теперь для вашей цели.

  1. Вы ищете способ упорядочить элементы управления и собираемые ими данные, чтобы упростить работу с ними.

A пользовательский контроль - все еще путь сюда.Вы получите выгоду от инкапсуляции всех этих повторяющихся функций в одном месте и возможности их повторного использования.

Начните с создания пользовательского элемента управления - мы назовем его DayPanel -- и поместите все элементы управления на один день на этом холсте.Назовите элементы управления без учета дня недели (например, start, lunch и end).Ваш пользовательский элемент управления не будет ни знать, ни заботиться о том, какой день он представляет.

Добавить обработчик события для ValueChanged в элементы управления DateTimePicker.Вместо двойного щелчка по элементу управления перейдите к списку событий в окне инструмента «Свойства» и введите имя, например, приведенное ниже, для события ValueChanged.Сделайте то же самое для двух других элементов управления, и он будет использовать обработчик событий, который он создал в первый раз.Всякий раз, когда пользователь изменяет время, будет вызываться этот обработчик событий, и он будет влиять на изменения пользовательского интерфейса.

private void picker_ValueChanged(object sender, EventArgs e)
{
    // In case you need to know which DateTimePicker was changed, take a look at 'sender':
    //DateTimePicker picker = (DateTimePicker)sender;

    UpdateWarningState();
}

Как упоминал Джими, объект sender будет ссылкой на элемент управления DateTimePicker, который отправилсобытие.Вам, вероятно, это не понадобится, но оно есть, если вы это сделаете.

UpdateWarningState просто скрывает / показывает метку предупреждения в зависимости от достоверности введенных данных.

private void UpdateWarningState()
{
    warningLabel.Visible = !IsInputValid(start.Value.TimeOfDay, lunch.Value.TimeOfDay, end.Value.TimeOfDay);
}

Я предложилв комментариях к предыдущему вопросу о том, что, по-видимому, имеет смысл получить true, если входные данные верны, а затем использовать логический минус для видимости метки предупреждения.

Как отметил Пол Хеберт, вы действительнонужно только сравнить TimeSpan, поэтому IsInputValid получает свойство TimeOfDay, чтобы иметь дело только с этим.

private bool IsInputValid(TimeSpan startTime, TimeSpan lunchTime, TimeSpan endTime)
{
    return startTime < lunchTime && lunchTime.Add(TimeSpan.FromMinutes(30)) < endTime;
}

На самом деле, даже если вы вводите только время, элемент управленияпо-прежнему возвращает часть даты в свойстве Value.Если вы хотите быть уверены, что вы не сравниваете время в разные даты, вам обязательно нужно использовать свойство TimeOfDay.Тем не менее, не представляя часть даты, у вас есть мера контроля над этим, так что это не насущная проблема.Если вам придется беспокоиться о переходе через полночь, это может усложнить ситуацию.

Обратите внимание, что я имел дело с тем более ранним предположением, что обед является фиксированной продолжительностью, добавив 30 минут в сравнении с временем окончания.

Почему бы просто не сделать все это в обработчике событий ValueChanged?

  1. Принцип единой ответственности.IsInputValid делает одно: бизнес-логика;он говорит вам, если входные данные действительны на основе тестирования диапазона.UpdateWarningState делает другое: логика пользовательского интерфейса;он обновляет видимость метки предупреждения в зависимости от достоверности введенных данных.

  2. UpdateWarningState можно использовать повторно.Вы можете вызвать его из других обработчиков событий в будущем.Обработчики событий действительно не должны ничего делать.Они больше похожи на телефонных операторов: «Как я могу направить ваш звонок?»

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

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

public TimeSpan Start
{
    get => start.Value.TimeOfDay;
    set => start.Value = start.Value.Date + value;
}

public TimeSpan Lunch
{
    get => lunch.Value.TimeOfDay;
    set => lunch.Value = lunch.Value.Date + value;
}

public TimeSpan End
{
    get => end.Value.TimeOfDay;
    set => end.Value = end.Value.Date + value;
}

Что интересно отметить, эти свойства не имеют собственного резервного хранилища.Вместо этого они переходят к элементам управления и переводят между своим собственным типом данных TimeSpan и типом данных DateTime элементов управления.На get они возвращают только свойство TimeOfDay.На set они удаляют часть времени (с .Date) и добавляют время суток.

Если вы строите это для кого-то другого, вы захотите убедиться, чтосвойство Days равно 0 и что все значение неотрицательно, и либо бросьте ArgumentOutOfRangeException, либо (задохнитесь!) ограничить значение до приемлемого диапазона.

Теперь, когда выиметь функциональный контроль в течение одного дня, вы можете шлепнуть их несколько в основной форме.Вернувшись в Form1, добавьте семь экземпляров элемента управления DayPanel и назовите их от monday до sunday.Прежде чем мы приступим к инициализации, давайте создадим поиск для этих пользовательских элементов управления.

private readonly Dictionary<DayOfWeek, DayPanel> _dayPanelLookup;

public Form1()
{
    InitializeComponent();

    _dayPanelLookup = new Dictionary<DayOfWeek, DayPanel>()
    {
        [DayOfWeek.Monday] = monday,
        [DayOfWeek.Tuesday] = tuesday,
        [DayOfWeek.Wednesday] = wednesday,
        [DayOfWeek.Thursday] = thursday,
        [DayOfWeek.Friday] = friday,
        [DayOfWeek.Saturday] = saturday,
        [DayOfWeek.Sunday] = sunday
    };
}

Теперь обработчик Load может инициализировать все свойства.Это DefaultTime дублирует константу TimeSpan.Zero с целью придания ей особого значения и может помочь в дальнейшем рефакторинге.

private static readonly TimeSpan DefaultTime = TimeSpan.Zero;

private void Form1_Load(object sender, EventArgs e)
{
    SetDefaults();
}

private void SetDefaults()
{
    foreach (DayPanel dayPanel in _dayPanelLookup.Values)
    {
        dayPanel.Start = DefaultTime;
        dayPanel.Lunch = DefaultTime;
        dayPanel.End = DefaultTime;
    }
}

И просто для забавы, мы можем использовать _dayPanelLookup, чтобы взять одиниз них на основе переменной, содержащей день недели.

public void someButton_Click(object sender, 
{
    DayOfWeek whichDay = SelectADay();

    DayPanel dayPanel = _dayPanelLookup[whichDay];

    // ...
}

Это должно решить основную проблему организации элементов управления и облегчения работы с ними и их значениями.То, что вы делаете с ним, когда пользователь нажимает какую-то еще неопознанную кнопку в форме, - это совершенно новое приключение.


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

0 голосов
/ 28 декабря 2018

Неясно, что вы хотите, чтобы компонентом даты части DateTime был DateTime.Parse ("00:00") должен возвращать полночь сегодня или 12/27/18 12:00:00 AM;Это также то же значение, что и DateTime.Today. Кроме того, вы можете создать новый DateTime с конструктором

monStart.Value = new DateTime(2018, 12, 27, 0, 0, 0);

Это полночь сегодняшнего дня

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...