我创建了一个简单的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()
下。我不知道这是否正确,但是当我用字符串数组进行测试时它似乎可以工作。
答案 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
对象必须已经初始化。 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问题,而上一个问题实际上是基于实际问题和更具实际意义的工作。
让我们从我们所知道的开始。我从两个问题以及两个问题中的各种评论中总结了这一点。
您有DateTimePicker
控件用于开始,结束和午餐。您只对时间部分感兴趣,因此将Format
设置为“自定义”,将CustomFormat
设置为“ HH:mm”。 假设:午餐时间是固定的,因此不需要结束时间。
您具有上述控件七次,一周中的每一天一组。
您已经编写了验证代码(范围测试)来确定是否正确输入了值,并且当测试失败时,您可以显示带有红色感叹号的标签。
您已经发现,仅在窗体上具有一堆控件就变得太复杂了。
到目前为止,太好了。现在是您的目标。
用户控制仍然是解决问题的方法。将所有重复使用的功能封装到一个地方并可以重复使用,您将受益匪浅。
首先创建一个用户控件-我们将其称为DayPanel
-然后将所有控件一天放在该画布上。在不考虑星期几的情况下命名控件(例如start
,lunch
和end
)。您的用户控件既不会知道也不会在意它代表哪一天。
将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
事件处理程序中执行所有操作?
单一责任原则。 IsInputValid
做一件事:业务逻辑;它会根据范围测试告诉您输入是否有效。 UpdateWarningState
做的事情与众不同:UI逻辑;它会根据输入的有效性更新警告标签的可见性。
UpdateWarningState
是可重用的。您将来可以从其他事件处理程序中调用它。事件处理程序真的不应该做太多事情。他们更像电话接线员:“我该如何转接您的电话?”
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
控件实例,并将它们命名为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];
// ...
}
这应该解决组织控制并使其易于使用它们及其价值的主要问题。用户按下表单上一些尚未确定的按钮后,您将如何处理这一切,都是全新的冒险。
我敢肯定,还有更多更好的方法来完成所有这些工作。我不是UI开发人员,我只是在电视上玩一个。为了您的目的,我希望这不仅可以为您提供本项目目前所需的指导,还可以阐明有关将来如何构建程序的新思路。