允许将时间跨度数据输入到 Access 数据库字段

Allowing bound Timespan data entry to Access database field

我正在尝试允许用户将小时和分钟输入到类似文本框的控件 (winforms) 中,该控件绑定到与 Access 数据库相关联的数据集。我目前使用的是存储总分钟数的字段,而不是使用 date/time 字段,因为我只需要小时和分钟,而不是包含的天数。

换句话说,我希望用户能够以 HH:mm 格式输入,小时数大于 23(例如 100),这样它看起来像 100:30( 100小时30分钟)输入的时候,存入底层table,就是总分钟数(6030)。另外,也应该允许一个Null值。

我对它在数据库中的存储方式很灵活,但是Access没有TimeSpan字段类型,而DateType字段类型将小时数限制为23,所以这就是我选择Long Integer值的原因分钟。

必须有办法做到这一点,但经过大量搜索后,我找不到任何可行的解决方案,允许超过 23 个小时。

我最初的想法是允许用户在普通文本框中输入任何内容,然后使用代码在更改事件中对其进行解析并将其转换为分钟(如果有效),将该值设置为隐藏的文本框是实际绑定到数据的内容table,但是解析输入的内容存在很多问题,似乎应该有更简单的方法。我尝试使用仅使用时间格式的 DateTimePicker,但它不允许大于 23 小时。

有人可以给我一些关于如何实现这一点的建议吗?

作为最后的手段,我可​​能最终会将字段更改为 Single 并让他们输入 100.5 100 小时 30 分钟,但它仍然更可取,因为它被输入为 100:30


编辑:

很抱歉没有完全遵循答案,但我收到了一些关于

的错误

Value is not a member of EventArgs

'AddMinutes' is not a member of 'TimeSpan'.


编辑:

我现在离我更近了...谢谢你教我一些非常有用的东西。

我不得不更改处理程序中的代码以便我能够理解它。它不像你的那么花哨,但它有效:

Private Sub Binding_Format(sender As Object, e As ConvertEventArgs)
    If e.Value IsNot DBNull.Value Then
        Dim hours As Integer = e.Value \ 60
        Dim minutes As Integer = e.Value - (hours * 60)
        e.Value = CType(hours, String) & ":" & CType(minutes, String)
    End If
End Sub

Private Sub Binding_Parse(sender As Object, e As ConvertEventArgs)
    If e.Value IsNot DBNull.Value Then
        Dim parts As String() = e.Value.Split(":"c)
        If parts.GetUpperBound(0) = 1 Then
            If IsNumeric(parts(0)) And IsNumeric(parts(1)) And
               parts(0).ToString.Length > 0 And parts(1).ToString.Length > 0 Then
                e.Value = CInt(parts(0) * 60) + CInt(parts(1))
            End If
        End If
    End If
End Sub

我遇到的最后一个问题是,当表单最初 loads/binds 时,它不会 运行 Binding_Format 而是显示整数。如果我单击网格中的不同行,文本框将从那时起正确更新。

编辑:

通过在我填充 Table.

之前移动处理程序代码来解决该问题

只需像往常一样绑定一个 TextBox,然后处理 BindingFormatParse 事件。这将允许您在 TimeSpanString 之间来回转换。

我没有测试过这段代码,但假设数据有效,我相信这些事件处理程序应该可以工作:

Private Sub Binding_Format(sender As Object, e As ConvertEventArgs)
    Dim time = DirectCast(e.Value, TimeSpan)

    'Convert from TimeSpan to String.
    e.Value = $"{CInt(Math.Floor(time.TotalHours)):#00}:{time.Minutes:00}"
End Sub

Private Sub Binding_Parse(sender As Object, e As ConvertEventArgs)
    Dim text = CStr(e.Value)
    Dim parts = text.Split(":"c)

    'Convert from String to TimeSpan.
    e.Value = TimeSpan.FromHours(CInt(parts(0))) + TimeSpan.FromMinutes(CInt(parts(1)))
End Sub

您可以按照以下方式将它们联系起来:

Dim binding = timeTextBox.DataBindings.Add("Text", myDataSource, "TimeColumnOrProperty")

AddHandler binding.Format, AddressOf Binding_Format
AddHandler binding.Parse, AddressOf Binding_Parse

如果数据已经在没有格式化的情况下写入控件,您可能需要在附加事件处理程序后调用 binding.ReadValue(),但我也没有测试过。

即使您只关心分钟数,您仍然应该使用日期时间。原因是您可以使用所有内置函数来获取日、分、秒。您可以使用内置函数添加分钟等。

更好的是,您可以 display/get 开始时间和结束时间,然后再过午夜。 您可能只需要分钟数和原始分钟数,但根据我的经验,这是很少见的需求。我的意思是,如果这只是几分钟,那么只需要一个简单的整数列就足够了,你会想到的。

因此,您应该有(我们假设)开始和结束时间。

如果用户只输入分钟,那么您仍然可以而且应该使用开始和结束时间。您拥有并可以在 .net 中使用时间跨度这一事实非常支持这个想法。

I cannot find anything that gives me a viable solution that allows for hours being more than 23.

为什么?

如果此人输入 5 小时,则您的开始时间为 0,结束时间为 5 小时。

如果他们想输入 200 小时,那么您的开始时间为 0,结束时间为 200 小时后。这样的美妙之处在于,您可以从代表开始和结束时间的跨度中得到几天、几分钟或几秒钟,甚至几周。通过使用日期时间列,您可以轻松跨越数天。

如何显示 UI 由您决定。当你拉取开始+结束时间时,你将它转换为分钟,然后将分钟显示在一个框中供用户编辑。保存时,您将分钟转换回开始时间 (0)(和日期)和未来的结束时间(同样,它也会有日期部分)。那个结束时间可能足够一整年的时间——一个人不应该关心一点。

以上的优点是所有报告(排序)甚至分组现在都可以工作了。

我真的无法想象一个应用程序只需要输入 40 个小时,但有些日期也不是这个组合的一部分。正如我所说,如果您只需要分钟,则使用整数字段。

所以,在这里包括开始日期似乎更有价值。处理会议记录是一团糟,而且还必须处理单独的日期列。

因此不必显示开始日期和结束日期(带时间)列。但是,您的 UI 可以提取这两个值,然后创建一个时间跨度变量,您的代码等将因此享受许多 .net 例程的好处。因此,您可以使用数据库中的许多功能。

所以,说给我一年中第 3 周的所有时间。如果您只处理两列(开始时间和结束时间),那么该查询将更加容易。因此,您可以查询日期范围和时间作为结果。因此,虽然在 start/end 列之间转换为一个时间跨度,您的代码逻辑会花费更多的钱,但这里的其他一切都更有价值和灵活得多。

那么从逻辑上来说呢?包含日期的开始时间列和结束时间列很有意义。

从物理(UI 显示)的角度来看,您可以提取这两个值,获取分钟数,并向用户显示 edit/enter 分钟数。当您保存回数据库时,您将转换回开始和结束时间列。

所以,如果我输入今天 50 小时,那么开始时间就是今天(0 时间),然后结束时间将是 + 50 小时(未来)。这样做的好处是我现在可以不用代码 get/calculate/display 刚输入 50 小时的未来日期。因此,现在您将小时数转换为未来日期是自动的,甚至可以显示在报告等中,并且无需额外代码即可完成。 这也意味着您可以通过使用 SQL 查询 + 求和命令(和 datediff)等来求和()分钟

自从您将时间跨度纳入讨论以来?嗯,这意味着几天、几小时等。因为时间跨度可以涵盖几分钟到几天等?好吧,虽然日期时间列不仅仅是天、分钟、秒,但当您将开始 date/time 和结束 date/time 转换为 .net 时间跨度时,它可以被认为是相同的数据结构。

所以: 抢占 date/time 列 抓取结束 date/time 列

现在将上面的内容转换为时间跨度(天、小时、分钟)。 现在显示,允许用户编辑或输入 35 小时。

现在, 转换回开始日期(没有时间),然后结束 date/time 是 + 天、小时、分钟、秒。因此,您始终可以从这两列中获取分钟数 - 但它们存储为未来的开始日期(0 时间)和结束日期。因此,这将使您能够获得分钟数、秒数、天数或其他任何时间。

在概念层面上,您确实在这里进行了一对一的转换。如果日期永远不会成为其中的一部分,并且您没有 have/need 一个单独的日期字段,那么您可以将其存储为一个简单的分钟整数。但是,start/end 日期时间列将大大简化报告工作,并且还具有到 .net 时间跨度(没有日期)的一对一概念转换。