format datetimepicker in datagridview column - winforms

I am trying to use the DateTime picker custom gridview column type from this MSDN example on How to host controls in DataGridViewCells. I want to display hour and minute in 24 hour format, without seconds or AM PM indicators.
I have set EditingControlFormattedValue to "HH:mm", and when not actually editing the value is displayed correctly.
When editing, if in the constructor of the CalendarEditingControl I set the editing control to CustomFormat = "HH:mm", the control displays the day of week and month. (!?)
When I instead use Format = DateTimePickerFormat.Time, the control shows AM or PM when editing.
How can I persuade this control to display only the parts of the DateTime value that I care about? (C#, VS 2008)

There are a few tweaks you need to to do the linked code to make it work the way you want:
Comment out the hard-coded line in CalendarCell() constructor (this.Style.Format = "d";)
Tell the CalendarEditingControl to use your custom-specified format:
In the designer, set the format you want (EditColumns->DefaultCellStyle->Format)
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
this.Format = DateTimePickerFormat.Custom;
this.CustomFormat = dataGridViewCellStyle.Format;
// ... other stuff
}

I found I needed to make the following changes:
In the constructor of the CalendarCell change the format to 24 hour.
public CalendarCell()
: base()
{
// Use the 24hr format.
//this.Style.Format = "d";
this.Style.Format = "HH:mm";
}
In the constructor for the editing control specify to use a custom format. I've also taken the liberty of setting ShowUpDown true so you don't show the calendar icon when editing the cell:
public CalendarEditingControl()
{
//this.Format = DateTimePickerFormat.Short;
this.Format = DateTimePickerFormat.Custom;
this.CustomFormat = "HH:mm";
this.ShowUpDown = true;
}
Change EditingControlFormattedValue. This doesn't appear to actually be necessary but feels icky to leave as is.
// Implements the IDataGridViewEditingControl.EditingControlFormattedValue
// property.
public object EditingControlFormattedValue
{
get
{
//return this.Value.ToShortDateString();
return this.Value.ToString("HH:mm");
}
set
{
if (value is String)
{
try
{
// This will throw an exception of the string is
// null, empty, or not in the format of a date.
this.Value = DateTime.Parse((String)value);
}
catch
{
// In the case of an exception, just use the
// default value so we're not left with a null
// value.
this.Value = DateTime.Now;
}
}
}
}

Related

Setting the Text property of a DropDownList ComboBox to an invalid value doesn't raise exception?

I'm looking at ways to resolve an issue with a Winforms application, which uses a ComboBox control. Specifically, the ComboBox (Style=DropDownList) is bound to a datasource and, as the user navigates through some other data, the "Text" property of the ComboBox property is set - and the user can select some other value.
The trouble starts when the value I set the "Text" property to is not in the list of available items. It seems that nothing happens. Take the following simple example:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
myComboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
//myComboBox1.Items.AddRange(new[] { "One", "Two", "Three" });
List<KeyValuePair<Int32, String>> values = new List<KeyValuePair<Int32, String>>();
values.Add(new KeyValuePair<Int32, String>(1, "One"));
values.Add(new KeyValuePair<Int32, String>(2, "Two"));
values.Add(new KeyValuePair<Int32, String>(3, "Three"));
myComboBox1.DataSource = values;
myComboBox1.ValueMember = "Key";
myComboBox1.DisplayMember = "Value";
button1.Click += (s, e) => { myComboBox1.Text = "Four"; };
button2.Click += (s, e) => { myComboBox1.SelectedIndex -= 1; };
}
}
public class MyComboBox : System.Windows.Forms.ComboBox
{
public override string Text
{
get { return base.Text; }
set { MessageBox.Show(value); base.Text = value; }
}
}
This technique is used throughout a large application, so when it was noticed that (in the example above) setting the "Text" to "Four" does nothing, I thought that maybe I could trap this and throw an exception. In reality, the application is peppered with code like this:
if (myDataRow.IsBlahNull())
myComboBox1.Text = "";
else
myComboBox1.Text = myDataRow.Blah;
Now, while I appreciate that setting "SelectedIndex = -1" would be better for the "IsNull" case, the fact remains that myDataRow.Blah may not be a valid value. Also, the application is written (and live) so the fewer changes the better.
So, my immediate thought was "let's override the Text property setter and check that the value is in the list". That, it turns out, is nothing like as simple as it would seem. The problem being that the "Text" property is set to all kinds of things, in all kinds of scenarios. For example, it's set when the DataSource property is assigned, or when the SelectedIndex is set to -1. Also, it's set to the string representation of the selected item - so if you happen to have a ComboBox control that's bound to a List of KeyValue pairs, you get the "Text" property set to something like "[Key,Value]". If it's bound to a DataTable/DataView, you get the string representation of the DataRow, and that gets even harder to detect.
It's at this point I thought that there might be another way to achieve the desired result (which is to detect the setting of the Text property to some invalid value - which does nothing).
Any ideas ?
Upon reflection, is this a reasonable work-around ?
/// <summary>
/// Gets or sets the text associated with this control.
/// </summary>
public override string Text
{
get { return base.Text; }
set
{
base.Text = value;
if ((value != null) && (base.Text != value))
if (value == "")
this.SelectedIndex = -1;
else
throw new ArgumentException(String.Format("Cannot set Text property of {0} to \"{1}\".", this.Name, value));
}
}

Silverlight data binding breaks when there is no debugger attached

Ok, a bit of a strange one - and it's probably something simple as I'm pretty new to Silverlight!
I have an object with the following property:-
private int targetID = NULL_TARGET_VALUE;
[Display(Name="Target", Order=1)]
[Required]
public int TargetID
{
get
{
return targetID;
}
set
{
if (this.targetID != value)
{
this.ValidateProperty("TargetID", value);
this.targetID = value;
this.RaisePropertyChanged("TargetID");
}
}
}
This object is created using the DataForm from the toolkit. I use the AutoGeneratingField event to change the item to a combo box drop down with the code below:
if (e.PropertyName == "TargetID")
{
ComboBox target = new ComboBox() { DisplayMemberPath = "Title", SelectedValuePath = "ItemID" };
target.ItemsSource = TaskManager.Manager.GanttItemSource;
var selectedItem = TaskManager.Manager.GanttItemSource.FirstOrDefault(p => p.ItemID == ParentTargetID);
target.SelectedItem = selectedItem;
e.Field.ReplaceTextBox(target, ComboBox.SelectedValueProperty, binding => binding.Converter = new TargetNullValueConverter());
break;
}
This does result in a drop down as I would expect. On my save button event I have this code:
if (registerForm.ValidateItem())
{
this.task.Save();
}
If the debugger is attached to the silverlight project this works great. If it's not then ValidateItem returns false as it thinks I have added an invalid target ("Input is not in a correct format" is the exact validation error I get).
Any ideas really appreciated! (BTW Just to confirm this happens in both release and debug build modes, simply attaching or removing a debugger causes this to occur)
Dammit, the issues was down to this line in the autogeneratingfield event:-
e.Field.ReplaceTextBox(target, ComboBox.SelectedValueProperty, binding => binding.Converter = new TargetNullValueConverter());
This allows it to work
e.Field.ReplaceTextBox(target, ComboBox.SelectedValueProperty);
Which makes sense because I'm dealing with ints not objects. Still don't know why it works with the debugger attached however!

WPF charting DateTimeAxis issue?

I created a graph with column series by using data visualization and in that i've taken DateTimeAxis as x-axis and i've one combo box with time intervals and based on selection of combo box i need to bind intervals into my graph at runtime. The problem is i'm getting error if i select intervals randomly like
var xAxis = countChart.ActualAxes.OfType<DateTimeAxis>().FirstOrDefault(ax => ax.Orientation == AxisOrientation.X);
xAxis.Minimum = DateTime.Parse(fromDt.ToString("HH:mm:ss"));
xAxis.Maximum = DateTime.Parse(toDt.ToString("HH:mm:ss"));
"The minimum value must be smaller than or equal to the maximum value."
so how can i overcome my problem plz help me.
Thanks,
#Nagaraju.
Check and modify the Minimum and Maximum properties as needed.
Don't use fallback values in binding, but control the dates set by yourself. If you're not using view model, you could use the setter logics in code-behind.
<charting:DateTimeAxis
Orientation="X"
IntervalType="{Binding IntervalType, FallbackValue=Years}"
Interval="{Binding Interval}"
Maximum="{Binding EndDate}"
Minimum="{Binding StartDate}" />
public DateTime StartDate
{
get { return _startDate; }
set
{
if (EndDate < value)
{
EndDate = value;
}
// Set property and notify that property has changed
//...
}
}
private DateTime _endDate;
public DateTime EndDate
{
get { return _endDate; }
set
{
if (StartDate > value)
{
StartDate = value;
}
// Set property and notify that property has changed
//...
}
}
What may be happening here is the new Minimum value you are setting is larger than the old Maximum, so at the line xAxis.Minimum = .. you get an exception. Can you try this as a workaround (also to prove this theory)? Just change the order that you are setting the min/max on the XAxis.
So, use this:
xAxis.Maximum = DateTime.Parse(toDt.ToString("HH:mm:ss")); // Set max first
xAxis.Minimum = DateTime.Parse(fromDt.ToString("HH:mm:ss"));
instead of this:
xAxis.Minimum = DateTime.Parse(fromDt.ToString("HH:mm:ss"));
xAxis.Maximum = DateTime.Parse(toDt.ToString("HH:mm:ss"));
This ensures that the maximum is always larger, so long as your parsed toDT value is larger than the parsed fromDT
Best regards,
i found solution my self..
reset max with tomorrow and min with yesterday.. 'll fix the issue.. like..
xAxis.Minimum = DateTime.Now.AddDays(-1);
xAxis.Maximum = DateTime.Now.AddDays(1);

Cross fire with Messenger system between ViewModels causes Problems

I have a DateNavigatorViewModel + DateNavigatorView on my ButtonBar.
Below are 2 Views which get exchanged:
DailyView and WeeklyView. Each View has a DailyViewModel and WeeklyViewModel.
In my DateNavigatorViewModel I have messenger.Send(SelectedDate);
In my DailyViewModel and WeeklyViewModel each register in the constructor:
messenger.Register<DateTime>(this, LoadDailyData);
messenger.Register<DateTime>(this, LoadWeeklyData);
guess what happens when I select a date...
I am using MVVM Light toolkit.
How can I solve that problem of getting 2 times data from database?
In your data access layer you could use caching stored in some static dictionary, load all the data you need from the database for both view and filter at the data layer for the individual viewsmodels.
Or an alternative, have the DateChanged messege be received by the Data objects, load the data and then have a second message raised and received by your two views.
MainViewModel is instantiated then:
clicking on the Daily-Button instantiates the:
public DailyViewModel(IMessenger messenger)
{
_messenger = messenger;
_messenger.Register<DateNavigatorInfoObject>(this, LoadDailyData);
}
private void LoadDailyData(DateNavigatorInfoObject infoObject)
{
if (infoObject.DateNavigatorMode != DateTest.DateMode.Day)
return;
// get daily database stuff
}
After the DateNavigatorViewModel got instantiated see BELOW
clicking on the Weekly-Button instantiates the:
public WeeklyViewModel(IMessenger messenger)
{
_messenger = messenger;
_messenger.Register<DateNavigatorInfoObject>(this, LoadWeeklyData);
}
private void LoadWeeklyData(DateNavigatorInfoObject infoObject)
{
if (infoObject.DateNavigatorMode != DateTest.DateMode.Week)
return;
// get weekly database stuff
}
After the DateNavigatorViewModel got instantiated see BELOW
public DateNavigatorViewModel(IMainRepository mainRepo, IMessenger messenger)
{
_mainRepo = mainRepo;
_messenger = messenger;
SelectedDate = DateTime.Now;
// Wether daily/weekly data is fetched the start date for the data is NOW // when the ViewModels are instantiated the first time using a ViewModelLocator...
}
Now the property that got fired setting the DateTime.Now in the Ctor
private DateTime _selectedDate;
public DateTime SelectedDate
{
get { return _selectedDate; }
set
{
if (_selectedDate.Date == value.Date)
return;
_selectedDate = value;
this.RaisePropertyChanged("SelectedDate");
var infoObject = new DateNavigatorInfoObject();
switch (DateNavigatorMode)
{
case DateTest.DateMode.Day:
infoObject.DateNavigatorMode = DateNavigatorMode;
infoObject.SelectedStartDate = value;
break;
case DateTest.DateMode.Week:
infoObject.DateNavigatorMode = DateNavigatorMode;
infoObject.SelectedStartDate = value;
infoObject.SelectedEndDate = value.AddDays(6);
break;
}
_messenger.Send(infoObject);
}
public class DateTest
{
public enum DateMode
{
Day,
Week,
Month,
}
}
The infoObject is send both to the Daily and WeeklyViewModel but depending on the DateNavigatorMode the database fetching is rejected from the ViewModel.
For me thats a solution because it firstly works and second no DAL is involved just the ViewModels.
Someone might mark it as solution if you like it. Critics are welcome too maybe I can still improve something?

Is there a way to apply a mask to the textbox of a DatePicker?

I'm working on my first WPF app. In this case, using VS 2010. My users are used to typing the date like this: "09082010" (without the double quotes; this would represent today). After they enter that, then it gets converted to 9/8/2010. I've put the DatePicker control onto the WPF page, but if the user enters 09082010, then it doesn't recognize it as a date and ignores it. I've applied a IValueConverter, to no effect, again because it doesn't recognize "09082010" as a date. So, I'm wondering, is it possible to apply a mask to the textbox of the DatePicker in VS 2010, so that when a user enters 09082010 it will change that to 09/08/2010 (at least)?
Here's something you could probably do: handle the TextBox.TextChanged event in the DatePicker, then in the event handler, put your custom logic to parse the current text. Something like this:
<DatePicker x:Name="dp" TextBoxBase.TextChanged="DatePicker_TextChanged"/>
private void DatePicker_TextChanged(object sender, TextChangedEventArgs e)
{
DateTime dt;
DatePicker dp = (sender as DatePicker);
string currentText = (e.OriginalSource as TextBox).Text;
if (!DateTime.TryParse(currentText, out dt))
{
try
{
string month = currentText.Substring(0,2);
string day = currentText.Substring(2,2);
string year = currentText.Substring(4,4);
dt = new DateTime(int.Parse(year), int.Parse(month), int.Parse(day));
dp.SelectedDate = dt;
}
catch (Exception ex)
{
dp.SelectedDate = null;
}
}
}
I know it ain't pretty. But this could be a start.

Resources