DateTime数组元素包含的值不正确

时间:2018-12-27 22:33:22

标签: c# visual-studio

我创建了一个简单的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中的当前时间。因此,如果用户在DateTimePicker中键入10:00(它们的格式为HH:mm),那么我需要MessageBox来说时间是10:00 AM。如果将值更改为22:00,则需要在消息框中说时间是10:00 PM。等等。(日期与我的情况无关,我根本不关心日期是什么。只有时间。)

我怀疑这可能是由于写入顺序所致。运行存储setDefaults();之前的DateTimePicker值的数组吗?如果是这样,由于DateTimePickers的值将发生很大变化,并且我需要使用最新值更新数组元素,如何使数组项的值动态变化?

额外信息:
-使用Visual Studio
-在设计视图中添加了DateTimePickers,在那里将格式更改为HH:mm,在设计视图中未更改默认值
-完全忽略日期,现在只关心时间

PS:我也很努力地在哪里声明数组,以便可以在其他多种方法中访问它,发现我必须在public partial class Form1中声明数组初始化程序,然后在{{ 1}},因为它不允许我将它们添加到public Form1()下。我不知道这是否正确,但是当我用字符串数组进行测试时它似乎可以工作。

3 个答案:

答案 0 :(得分:0)

尚不清楚您希望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);

今天是午夜

答案 1 :(得分:0)

注意
阅读更新的问题中的描述后,似乎可以在按钮DateTimePicker上访问Click控件值。如果这是实际情况,则可能根本不需要DateTime数组字段:您可以直接从DTP控件中读取值,并就地使用 值。
该示例假定(为了符合问题)无论如何您都需要该数组。


一种可能的进行方式:

  • Form.Load 事件中设置默认值。此后立即初始化 monSchedule 数组值,以便对这些值进行同步。请注意,Form.Load事件处理程序代码(当然)是在类构造函数(public Form1() { })之后执行的:Form对象必须已经初始化。
  • 将事件处理程序分配给所有DateTimePicker控件(所有事件均相同)。事件处理程序用于将新值分配给 monSchedule 数组。该事件可以是ValueChanged事件,也可以是更通用的Validating事件。每当您更改“时间”值的任何部分(小时值或分钟值)时,前者都会提高。仅当控件失去焦点时才使用后者。你的选择。
  • 使用事件处理程序中的sender对象来确定哪个控件引发了该事件并更新了相应的数组值。

使用switch statement 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;
        }
    }

答案 2 :(得分:0)

我不得不说这有点回归。在您的previous question中,JoshPart以用户控件的形式给了您很好的建议,尽管他可能留下了一些太大的缝隙,您无法自己填补。

以这种方式使用阵列可能只需要一天的时间,但不能很好地扩展到整周。

如果有任何人阅读此书,则想知道我为什么要谈论整整一周,请您参考previous question。另外,我认识到我对这个特定问题不感兴趣,但我认为这是一个XY问题,而上一个问题实际上是基于实际问题和更具实际意义的工作。


让我们从我们所知道的开始。我从两个问题以及两个问题中的各种评论中总结了这一点。

  1. 您有DateTimePicker控件用于开始,结束和午餐。您只对时间部分感兴趣,因此将Format设置为“自定义”,将CustomFormat设置为“ HH:mm”。 假设:午餐时间是固定的,因此不需要结束时间。

  2. 您具有上述控件七次,一周中的每一天一组。

  3. 您已经编写了验证代码(范围测试)来确定是否正确输入了值,并且当测试失败时,您可以显示带有红色感叹号的标签。

  4. 您已经发现,仅在窗体上具有一堆控件就变得太复杂了。

到目前为止,太好了。现在是您的目标。

  1. 您正在寻找一种组织控件及其收集的数据的方法,以使其更易于使用。

用户控制仍然是解决问题的方法。将所有重复使用的功能封装到一个地方并可以重复使用,您将受益匪浅。

首先创建一个用户控件-我们将其称为DayPanel-然后将所有控件一天放在该画布上。在不考虑星期几的情况下命名控件(例如startlunchend)。您的用户控件既不会知道也不会在意它代表哪一天。

ValueChanged事件的事件处理程序添加到DateTimePicker控件。而不是双击控件,而是转到“属性”工具窗口中的事件列表,然后为ValueChanged事件键入一个名称,例如下面的名称。对其他两个控件执行相同的操作,它将重用它第一次创建的事件处理程序。每当用户更改时间时,都会调用此事件处理程序,并将对UI进行更改。

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

如Jimi所述,sender对象将是对发送事件的DateTimePicker控件的引用。您可能不需要它,但如果需要它就在那里。

UpdateWarningState仅根据输入的有效性隐藏/显示警告标签。

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

我在对上一个问题的评论中建议,如果输入有效,则获取true似乎是有意义的,然后将逻辑否定用于警告标签的可见性。

正如Paul Hebert所指出的那样,您实际上只需要比较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做的事情与众不同:UI逻辑;它会根据输入的有效性更新警告标签的可见性。

  2. UpdateWarningState是可重用的。您将来可以从其他事件处理程序中调用它。事件处理程序真的不应该做太多事情。他们更像电话接线员:“我该如何转接您的电话?”

  3. IsInputValid是可重用的。将来某个时候可以从您的UI代码中提取业务逻辑,然后将其重新使用。我承认这个名字有些不足之处。它适合此处,但在此上下文之外可能应该有所不同。

但是,如果您无法使用其数据,此用户控件有什么好处?消费者需要能够与之交互。用户控件只是另一个类,因此您可以根据需要定义公共属性,方法和事件。我们将为三个感兴趣的值添加属性:

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或(gasp!)将该值限制在可接受的范围内。

现在,您已有一天可以正常使用的控件,您可以在主窗体上拍一堆。返回Form1,添加七个DayPanel控件实例,并将它们命名为mondaysunday。在进行初始化之前,让我们为这些用户控件创建一个查找。

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];

    // ...
}

这应该解决组织控制并使其易于使用它们及其价值的主要问题。用户按下表单上一些尚未确定的按钮后,您将如何处理这一切,都是全新的冒险。


我敢肯定,还有更多更好的方法来完成所有这些工作。我不是UI开发人员,我只是在电视上玩一个。为了您的目的,我希望这不仅可以为您提供本项目目前所需的指导,还可以阐明有关将来如何构建程序的新思路。