Boris
Boris

Reputation: 5176

how to apply restrictions to which items are selectable in a combobox

I am developing a c# winforms application. The underlying model has, among other things, a StartTime and an EndTime DateTime property, with the restriction, that the StartTime must come before the EndTime. To give the user the ability to change these properties, I fill two comboboxes with sample values to choose from, and hook each combobox up, so that selecting another value will change the corresponding property in the model.

The problem with this approach is that it allows the user to try to select an illegal value. Ideally, I would like it that when a user selects a certain StartTime value, the possible EndTime values are capped, so that it is not possible to select EndTime values earlier than that. One way to achieve this would be to fill the end combobox with the new legal values, and then select what was there previously. This would require that my SelectedIndexChanged event handler was temporarily shut off, since otherwise reselecting the EndTime would cause an infinite loop, alternating between filling/reselecting the StartTime and the EndTime comboboxes, respectively.

Another solution would to keep all the sample values, and then revert any illegal changes the user tries to make, but the fact that the user can try to select illegal values doesn't appeal to me. I am not fond of having to disable an event handler temporarily, since it doesn't seem clean, but I cannot see any better solutions. Do you have any suggestions or improvements?

Upvotes: 0

Views: 327

Answers (3)

xpda
xpda

Reputation: 15813

When a combobox selection is changed, do this:

  1. Set a flag variable which you can use to prevent re-entry into the event handler for this combobox (A), and
  2. Reload the other combobox (B) with all legitimate dates, considering the new date just selected in this combobox (A).
  3. Set the date in the other combobox (B) to the date that was already selected (before the dates were replaced), if there was a date selected in (B) and if it is legal with the new date in combobox (A)
  4. Reset the flag.

Upvotes: 1

Tigran
Tigran

Reputation: 62246

One time you open your form you:

  1. Set StartDate to some initial value, let's say today
  2. Set EndDate to some initial value, let's say tomorrow (disabled)
  3. Add global boolean variable bool SilentInvoke = false;
  4. Subscribe to SelectedIndexChanged for Start and End UI controls.
  5. In beginning of every selectedindex changed event have code more or less like this:

    if( SilentInvoke)
    {
          SilentInvoke=false;
          return;
    }
    
  6. User changes StartDate, enable EndDate.

  7. User changes EndDate, if it's before StartDate, notify to the user and reset it to initial value, but before make SilentInvoke=true;

There is always a possibility that user goes back and changes StartDate again, but you have to have somewhere validation method that validates the data before final request.

First you give a user kind of guide ( start enabled, end disabled), after leave user free to decide and make validation before actual request.

Upvotes: 1

alexm
alexm

Reputation: 6882

I would add a class (some would call it "ModelView") designed specifically for your UI scenario. It is sort of a proxy between data model and UI. The purpose of the proxy is to maintain invariant (startDate < endDate) during editing deferring changes to the model unti editing is done. At that moment changes are applied to the underlying data object.

      class MyDataObjectProxy: IPropertyChangedNotification
      {
          MyDataObjectProxy(MyDataObject model)
         {
             _model = model;
             _editedStartDate = model.StartDate;
             _editedEndDate = model.EndDate;
         }


          MyDataObject _model;

          public DateTime StartDate
          {
             get
             {
                 return Math.Min(editedStartDate, editedEndDate); 
             }

             set 
             {
                 _editedStartDate = value; 
                 RaisePropertyChanged("StartDate");
                 RaisePropertyChanged("EndDate");
             }
          }  

          public DateTime EndDate
          {
             get
             {
                 return Math.Max(editedStartDate, editedEndDate); 
             }

             set 
             {
                 _editedEndDate = value; 
                 RaisePropertyChanged("StartDate");
                 RaisePropertyChanged("EndDate");
             }
          }  


          DateTime _editedStartDate;
          DateTime _editedEndDate;


          public void ApplyChanges()
          {
               DateTime start = StartDate;
               DateTime end = EndDate;

              _data.StartDate = start;
              _data.EndDate = end;
          }

Upvotes: 1

Related Questions