【问题标题】:Can you bind separate date and time fields to a single DateTime in C#?您可以将单独的日期和时间字段绑定到 C# 中的单个 DateTime 吗?
【发布时间】:2009-08-10 18:35:37
【问题描述】:

我们的界面在单独的框中有日期和时间。一个日期框(DatePicker),一个时间框(TextBox)。

是否可以将单个DateTime(或者最好是可为空的DateTime)数据绑定到这些单独的值? (如果没有,有什么替代方案?)

如何做到这一点?

【问题讨论】:

  • 你能设置你的数据源,让它有一个日期列和一个时间列吗?
  • 不,数据源是我们无法更改的日期和时间SQL列。

标签: c# winforms data-binding


【解决方案1】:

在您的界面中创建一个公共属性或方法来检索整个日期。然后,此方法或属性将使用您的界面字段创建日期。您所做的操作听起来像是一个用户友好的界面,因为将两者明确分开是合乎逻辑的。

public DateTime GetDateTime(){
    return new DateTime(DateControl.Year,DateControl.Month,DateControl.Day,TimeControl.Hour,TimeControl.Minute);
}

例如,这可能是您界面上可以使用的一种可能方法!

你可能有一个 set 方法,它需要一个日期时间,然后你拆分值并绑定两个控件。 :-)

安德鲁

【讨论】:

  • 这听起来像是“否”,您可以将两个控件直接绑定到日期时间。问题是,我们想直接绑定到一个哑消息,除了字段之外没有任何其他内容,在这种情况下是日期时间。如果没有,我们将不得不找到解决方法,但首选是直接进行。
  • 只要您的日期字段有时间,那么将该日期传递给时间控件应该只提取时间值和日期控件的日期值。这取决于控件的构建方式。它是 .NET 用户控件吗?它有 DataSource 属性吗?如果不是,您可以在控件周围创建一个包装器并自己添加 DataBinding。
  • 此时它不是任何类型的控件,它是两个独立的控件。我明白你对包装器的意思。非常感谢您的回复。
【解决方案2】:

是的,有可能!

场景

  1. 在 UI 中有两个不同的 DateTimePicker 字段,一个代表日期,另一个代表时间。示例:

DateTimePicker dtpDate;
DateTimePicker dtpTime;
  1. 拥有一个具有单个 DateTime 属性的模型。示例:

public class MyModel
{
    public DateTime? When;
}

我们想要做什么?

当任何提到的 UI 控件发生更改时,自动更改模型的 When 属性,反之亦然;

解决方案

使您的模型类可绑定有两个额外的属性,它们就像When 属性和代表部分DateTime 值的字段之间的桥梁(代理)(例如:一个DateTimePicker 用于日期,另一个用于时间)。这些代理是必需的,因为当任何 UI 控件发生更改时,我们需要知道 DateTime 的哪些组件/部分需要更改。是日期还是时间?

现在,下面的代码做了很多事情。首先,您希望对任何代理(ProxyWhenDateProxyWhenTime)的任何更改做出反应,将此更改反映到生成的DateTimeWhen)中,反之亦然。要完成这项工作,您需要使此类可绑定。为此,您可以实现 INotifyPropertyChanged 并实现自定义属性设置器,该设置器会引发具有相应属性名称的 PropertyChanged 事件。

例子:

public class MyModel : INotifyPropertyChanged
{
    #region Bindable properties

    private DateTime? _When;
    public virtual DateTime? When
    {
        get { return _When; }
        set
        {
            SetField(ref _When, value);
            // When a change to `When` occurs, it means `ProxyWhenDate` and `ProxyWhenTime` have been changed as well,
            // so we need to raise a `PropertyChanged` notification for both of them.
            NotifyPropertyChanged(this.GetPropertyName((MyModel x) => x.ProxyWhenDate));
            NotifyPropertyChanged(this.GetPropertyName((MyModel x) => x.ProxyWhenTime));
        }
    }

    #endregion

    #region Proxies

    public virtual DateTime ProxyWhenDate
    {
        get
        {
            return When.HasValue ? When.Value : DateTime.UtcNow;
        }
        set
        {
            DateTime v = When.HasValue ? When.Value : DateTime.UtcNow;
            // Change only Year + Month + Day, keeping the rest as it is.
            When = new DateTime(value.Year, value.Month, value.Day, v.Hour, v.Minute, v.Second);
        }
    }

    public virtual DateTime ProxyWhenTime
    {
        get
        {
            return When.HasValue ? When.Value : DateTime.UtcNow;
        }
        set
        {
            DateTime v = When.HasValue ? When.Value : DateTime.UtcNow;
            // Change only Hour + Minute + Second, keeping the rest as it is.
            When = new DateTime(v.Year, v.Month, v.Day, value.Hour, value.Minute, value.Second);
        }
    }

    #endregion

    #region Object extensions

    public static string GetPropertyName<TSource, TField>(this Object obj, Expression<Func<TSource, TField>> Field)
    {
        return (Field.Body as MemberExpression ?? ((UnaryExpression)Field.Body).Operand as MemberExpression).Member.Name;
    }

    #endregion

    #region Support for bindings

    public virtual event PropertyChangedEventHandler PropertyChanged;

    // NotifyPropertyChanged will raise the PropertyChanged event passing the
    // source property that is being updated.
    public virtual void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public virtual void NotifyPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
    {
        var lambda = (LambdaExpression)property;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression)lambda.Body;
            memberExpression = (MemberExpression)unaryExpression.Operand;
        }
        else
        {
            memberExpression = (MemberExpression)lambda.Body;
        }
        NotifyPropertyChanged(memberExpression.Member.Name);
    }

    public virtual bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value))
            return false;

        field = value;
        NotifyPropertyChanged(propertyName);
        return true;
    }

    #endregion
}

现在您所要做的就是在 UI 控件的属性和模型属性之间创建绑定。示例:

public partial class MyForm : Form
{
    private MyModel Model;

    public MyForm(MyModel model)
    {
        InitializeComponent();

        Model = model;

        // Create our bindings

        dtpDate.DataBindings.Add(new Binding("Value", Model,
            this.GetPropertyName((MyModel x) => x.ProxyWhenDate)));

        dtpTime.DataBindings.Add(new Binding("Value", Model,
            this.GetPropertyName((MyModel x) => x.ProxyWhenTime)));
    }
}

完成!享受魔法,很抱歉迟到了 6 年 ;-)

注意:属性 CallerMemberName 是在 .NET Framework 4.5 中引入的,但您只需更改几行代码即可不使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    • 2013-08-29
    相关资源
    最近更新 更多