Intersoft ClientUI Documentation
Editing Data in UXScheduleView

This topic provides a comprehensive overview of data editing in UXScheduleView, discusses about data validation in depth, and explains how to handle data creation, update and delete (CUD) using MVVM pattern. For information about UXScheduleView and its features in general, see UXScheduleView Overview.

This topic contains the following sections.

Enable Data Editing

Data editing in UXScheduleView involves three fundamental processes, Create, Update and Delete (CUD). You can enable each editing feature by setting the property CanUserAddItems, CanUserDeleteItems, CanUserEditItems, CanUserMoveItems, CanUserResizeItems.

Enable Editing
Copy Code
<Intersoft:UXScheduleView DisplayDate="1/2/2012"
            EventsSource="{Binding Events}" CategoriesSource="{Binding Categories}" ResourcesSource="{Binding Resources}"
            CanUserAddItems="True" CanUserDeleteItems="True" CanUserEditItems="True" CanUserMoveItems="True" CanUserResizeItems="True">
    <Intersoft:UXScheduleDayView/>
    <Intersoft:UXScheduleWorkWeekView/>
    <Intersoft:UXScheduleWeekView IsActive="True"/>
    <Intersoft:UXScheduleMonthView/>
</Intersoft:UXScheduleView>

Inserting an Event

When the CanUserAddItems property is set to true, you will see the Add button in the header area of UXScheduleView. When clicked, a fluid call out that has built-in editing form will appear such as shown in the figure below.

If you prefer to use a custom Add Button, you need to set the AddButtonElement to the desired Add Button element. This allows the editing form's call out to point correctly to your custom Add Button element.

Keyboard Gestures to Add a New Event

Depending on the value of InsertKeyMode property, you can select a single cell or multiple cells and directly type in any characters to insert a new event for that particular date range.

The InsertKeyMode has the following value:

When inserting an event using inline editing feature, the event will not be saved until you press the Enter key or focus to another element. To cancel the insertion, simple press Escape key using keyboard. When you press Escape key to cancel the insertion, the control will automatically reselect previously selected cells.

When inserting a new event using inline editing, it requires a predetermined ResourceID that you can customize in PrepareNewItemCommand. Please see Handling the Create, Update and Delete operation for detailed information.

Context Menu

All schedule view controls provide a built-in context menu allowing users to easily add a new event by right clicking on the selected cells.

 

Notice that you have several insertion options that you can choose, whether you want to create an event or recurring event from the selected date range, or a new all-day event.

Inserting Event Behavior in UXScheduleMonthView

When you inserted an item that overlapped with other events in UXScheduleMonthView, it will try to insert the item in the available space which follow the rendering logic of the events. In the case that it cannot be placed in the available space, it will be placed at the bottom of the event cell until the insertion is committed such as shown in the following figure.

 

Editing an Event

When the CanUserEditItems property is set to true, you can edit the event in UXScheduleView. To edit an event, first you need to select the event to edit, then you can choose one of the provided options to edit the event as follows.

Editing a recurring pattern using the editing form will cause all the exception events to be deleted.

Mouse Actions

Keyboard Actions

Context Menu

You can also use context menu to directly edit an existing event. In this case, the editing form will be shown instead of activating inline editing.

The context menu smartly adapts its commands depending on the type of selected event. The figure above illustrates the context menu when the selected event type is a recurring event.

Deleting Events

When the CanUserDeleteItems property is set to true, you can delete one or more events that currently selected in UXScheduleView. To delete the item you can simply use the Delete key or using the context menu. Similar to editing context menu, the menu item for delete action is also adaptable depending on the type of the event such as shown in the figure below.

When you delete multiple events, it will check whether there are recurring event in the collection. If there is a recurring events, it will prompt a dialog asking whether you want to delete the series or only the particular occurrence. This action will be repeated until all recurring events have been confirmed.

Deleting a standard event will directly delete the event without showing the prompt dialog box.

Resizing an Event

When the CanUserResizeItems property is set to true, you can resize the currently selected event. You can notice the resizing indicator in the selected event. Note that some conditions might disable the resizing, for examples, when the start date or end date of the event is not visible in the current view.

When you resize a recurring event, it automatically becomes an exception event.

 

Moving an Event

When the CanUserMoveItems property is set to true, you can move the current selected event to another time allocation by simply dragging the desired event and dropping it in the desired destination. Note that you can not drag an event from one group to another group when the grouping feature is enabled.

Handling the Create, Update and Delete operation

To handle the Create, Update, and Delete (CUD) operation, UXScheduleView provides several command-related properties that you can bind to your ViewModel to execute the method depending on the action. These command-related properties are listed as follows:

Command Sequences

Create operation

The following commands are called in sequence when an event or an exception is created:

Update Operation

The following commands are called in sequence when an event is updated:

Delete Operation

The following commands are called in sequence when one or multiple events are deleted:

When you edited an occurrence of recurring event, it doesn't actually perform an update operation. If the occurrence is not an exception, it will create an exception by triggering the create operation. Similar approach is applied when you deleted an occurrence of recurring event.

Binding the Commands to View Model

You need to bind these command properties to your ViewModel and handle each of the action.

The following code example shows how to define and bind the editing commands from ViewModel to UXScheduleView.

ScheduleViewModelGenericBase
Copy Code
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ServiceModel.DomainServices.Client;
using Intersoft.Client.Data.ComponentModel;
using Intersoft.Client.Framework;
using UXScheduleView.Samples.ModelServices;

namespace UXScheduleView.Samples.ViewModels
{
    public class ScheduleViewModelBase<T> : ValidationViewModelBase where T : Entity
    {
        #region Fields

        private IEnumerable _categories;
        private IEnumerable _events;
        private IEnumerable _resources;

        private bool _isBusy;
        private bool _isPrerequisiteDataLoaded;
        private bool _isCategoriesLoaded;
        private bool _isResourcesLoaded;

        private T _selectedEvent;
        private IEnumerable<T> _selectedEvents;
        private QueryDescriptor _queryDescriptor;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ScheduleViewModelBase"/> class.
        /// </summary>
        public ScheduleViewModelBase()
        {
            this.QueryDescriptor = new QueryDescriptor();
            this.Presenter = new MessagePresenter();

            this.QueryLatency = 0.6;
            
            if (!this.IsInDesignMode)
            {
                this.IsBusy = true;
                this.LoadCategoriesData();
                this.LoadResourcesData();
            }
        }

        #endregion

        #region Data Source

        /// <summary>
        /// Gets the categories data repository instance that implements <see cref="IDataRepository"/>.
        /// </summary>
        protected virtual IDataRepository CategoriesDataSource { get; private set; }

        /// <summary>
        /// Gets the events data repository instance that implements <see cref="IDataRepository"/>.
        /// </summary>
        protected virtual IDataRepository EventsDataSource { get; private set; }

        /// <summary>
        /// Gets the resources data repository instance that implements <see cref="IDataRepository"/>.
        /// </summary>
        protected virtual IDataRepository ResourcesDataSource { get; private set; }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value representing the collection of category data.
        /// </summary>
        public IEnumerable Categories
        {
            get { return _categories; }
            set
            {
                if (_categories != value)
                {
                    _categories = value;
                    OnPropertyChanged("Categories");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value representing the collection of event data.
        /// </summary>
        public IEnumerable Events
        {
            get { return _events; }
            set
            {
                if (_events != value)
                {
                    _events = value;
                    OnPropertyChanged("Events");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value representing the collection of resource data.
        /// </summary>
        public IEnumerable Resources
        {
            get { return _resources; }
            set
            {
                if (_resources != value)
                {
                    _resources = value;
                    OnPropertyChanged("Resources");
                }
            }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the data repository is busy processing a query based on the given <see cref="QueryDescriptor"/>.
        /// </summary>
        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                if (_isBusy != value)
                {
                    _isBusy = value;
                    OnPropertyChanged("IsBusy");
                }
            }
        }

        /// <summary>
        /// Gets a value that indicates whether the application is currently in design-time mode.
        /// </summary>
        protected bool IsInDesignMode
        {
            get { return Intersoft.Client.Framework.ISControl.IsInDesignModeStatic; }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the prerequisite data is loaded.
        /// </summary>        
        protected virtual bool IsPrerequisiteDataLoaded
        {
            get { return _isPrerequisiteDataLoaded; }
            set
            {
                if (_isPrerequisiteDataLoaded != value)
                {
                    _isPrerequisiteDataLoaded = value;
                    OnPropertyChanged("IsPrerequisiteDataLoaded");

                    if (value)
                        this.QueryDescriptor.RaiseQueryChanged();
                }
            }
        }

        public bool IsCategoriesLoaded
        {
            get { return _isCategoriesLoaded; }
            set
            {
                if (_isCategoriesLoaded != value)
                {
                    _isCategoriesLoaded = value;
                    OnPropertyChanged("IsCategoriesLoaded");

                    if (value)
                        this.NotifyPrerequisiteDataLoaded();
                }
            }
        }

        public bool IsResourcesLoaded
        {
            get { return _isResourcesLoaded; }
            set
            {
                if (_isResourcesLoaded != value)
                {
                    _isResourcesLoaded = value;
                    OnPropertyChanged("IsResourcesLoaded");

                    if (value)
                        this.NotifyPrerequisiteDataLoaded();
                }
            }
        }

        /// <summary>
        /// Gets the <see cref="MessagePresenter"/> instance.
        /// </summary>
        protected virtual MessagePresenter Presenter { get; private set; }

        /// <summary>
        /// Gets or sets a <see cref="QueryDescriptor"/> instance which provides the required information for data query purpose.
        /// </summary>
        public QueryDescriptor QueryDescriptor
        {
            get
            {
                return _queryDescriptor;
            }
            set
            {
                if (_queryDescriptor != value)
                {
                    if (_queryDescriptor != null)
                        _queryDescriptor.QueryChanged -= new EventHandler(OnQueryChanged);

                    _queryDescriptor = value;
                    _queryDescriptor.QueryChanged += new EventHandler(OnQueryChanged);

                    OnPropertyChanged("QueryDescriptor");
                }
            }
        }

        private double QueryLatency { get; set; }

        /// <summary>
        /// Gets or sets a value indicating the currently selected item.
        /// </summary>
        public T SelectedEvent
        {
            get { return _selectedEvent; }
            set
            {
                if (_selectedEvent != value)
                {
                    if (_selectedEvent != null)
                        _selectedEvent.PropertyChanged -= new PropertyChangedEventHandler(OnSelectedEventPropertyChanged);

                    T _oldItem = _selectedEvent;
                    _selectedEvent = value;
                    OnSelectedEventChanged(_oldItem, value);

                    if (_selectedEvent != null)
                        _selectedEvent.PropertyChanged += new PropertyChangedEventHandler(OnSelectedEventPropertyChanged);

                    OnPropertyChanged("SelectedEvent");
                }
            }
        }

        /// <summary>
        /// Gets or sets a collection of multiple selected items.
        /// </summary>
        public IEnumerable<T> SelectedEvents
        {
            get { return _selectedEvents; }
            set
            {
                if (_selectedEvents != value)
                {
                    _selectedEvents = value;
                    OnPropertyChanged("SelectedEvents");
                }
            }
        }

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        /// <param name="dataSource">An instance of the data repository.</param>
        protected virtual void LoadCategoriesData(IDataRepository dataSource)
        {
            if (!this.IsInDesignMode && dataSource == null)
                throw new ArgumentNullException("dataSource should not be null.");

            this.CategoriesDataSource = dataSource;
            this.LoadCategoriesData();
        }

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        protected virtual void LoadCategoriesData()
        {
            Utility.ExecuteTimeOut(this.QueryLatency,
            () =>
            {
                if (this.CategoriesDataSource != null)
                {
                    this.IsBusy = true;

                    this.CategoriesDataSource.GetData
                    (
                        (items) =>
                        {
                            this.IsBusy = false;
                            this.IsCategoriesLoaded = true;
                            this.Categories = new PagedCollectionView(items);
                        },
                        (error) =>
                        {
                            this.IsBusy = false;

                            this.Presenter.ShowErrorMessage(
                                "An exception has occurred during data loading.\n" +
                                "Message: " + error.Message + "\n" +
                                "Stack Trace: " + error.StackTrace);
                        }
                    );

                    this.QueryLatency = 0;
                }
            });
        }

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        /// <param name="dataSource">An instance of the data repository.</param>
        protected virtual void LoadEventsData(IDataRepository dataSource)
        {
            if (!this.IsInDesignMode && dataSource == null)
                throw new ArgumentNullException("dataSource should not be null.");

            this.EventsDataSource = dataSource;
            this.LoadEventsData();
        }

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        protected virtual void LoadEventsData()
        {
            if (this.EventsDataSource != null)
            {
                this.IsBusy = true;

                this.EventsDataSource.GetData
                (
                    this.QueryDescriptor,
                    (items) =>
                    {
                        this.IsBusy = false;
                        this.Events = new PagedCollectionView(items);
                    },
                    (totalItemCount) =>
                    {
                        // do nothing
                    },
                    (error) =>
                    {
                        this.IsBusy = false;

                        this.Presenter.ShowErrorMessage(
                            "An exception has occurred during data loading.\n" +
                            "Message: " + error.Message + "\n" +
                            "Stack Trace: " + error.StackTrace);
                    }
                );
            }
        }

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        /// <param name="dataSource">An instance of the data repository.</param>
        protected virtual void LoadResourcesData(IDataRepository dataSource)
        {
            if (!this.IsInDesignMode && dataSource == null)
                throw new ArgumentNullException("dataSource should not be null.");

            this.ResourcesDataSource = dataSource;
            this.LoadResourcesData();
        }

        /// <summary>
        /// Called when the data repository needs to fetch data from the repository, either during initial load, or due to query change.
        /// </summary>
        protected virtual void LoadResourcesData()
        {
            Utility.ExecuteTimeOut(this.QueryLatency,
            () =>
            {
                if (this.ResourcesDataSource != null)
                {
                    this.IsBusy = true;

                    this.ResourcesDataSource.GetData
                    (
                        (items) =>
                        {
                            this.IsBusy = false;
                            this.IsResourcesLoaded = true;
                            this.Resources = new PagedCollectionView(items);
                        },
                        (error) =>
                        {
                            this.IsBusy = false;

                            this.Presenter.ShowErrorMessage(
                                "An exception has occurred during data loading.\n" +
                                "Message: " + error.Message + "\n" +
                                "Stack Trace: " + error.StackTrace);
                        }
                    );

                    this.QueryLatency = 0;
                }
            });
        }


        protected virtual void NotifyPrerequisiteDataLoaded()
        {
            if (this.IsCategoriesLoaded && this.IsResourcesLoaded)
                this.IsPrerequisiteDataLoaded = true;
        }

        /// <summary>
        /// Called when one of the properties in the <see cref="QueryDescriptor"/> object is changed.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The data related to the event.</param>
        protected virtual void OnQueryChanged(object sender, EventArgs e)
        {
            // Perform data retrieval in 0.6ms latency to ensure smooth transition. By default, transition took ~0.3ms.
            // If transition is not applied in the View, it's not necessary to use ExecuteTimeOut method.

            Utility.ExecuteTimeOut(this.QueryLatency,
                () =>
                {
                    if (this.IsPrerequisiteDataLoaded)
                    {
                        this.LoadEventsData();
                        this.QueryLatency = 0;
                    }
                });
        }

        /// <summary>
        /// Called when the <see cref="SelectedEvent"/> property changes.
        /// </summary>
        /// <param name="oldItem">The old selected item.</param>
        /// <param name="newItem">The new selected item.</param>
        protected virtual void OnSelectedEventChanged(T oldItem, T newItem)
        {
        }

        /// <summary>
        /// Called when one of the properties in the <see cref="SelectedEvent"/> object is changed.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The data related to the event.</param>
        protected virtual void OnSelectedEventPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
        }

        #endregion
    }
}
EditableScheduleViewModelBase
Copy Code
using System.Collections;
using System.ServiceModel.DomainServices.Client;
using Intersoft.Client.Framework.Input;
using Intersoft.Client.UI.ScheduleView;
using UXScheduleView.Samples.ModelServices;

namespace UXScheduleView.Samples.ViewModels
{
    public class EditableScheduleViewModelBase<T> : ScheduleViewModelBase<T> where T : Entity
    {
        #region Constructors

        public EditableScheduleViewModelBase()
            : base()
        {
            this.DeleteItemCommand = new DelegateCommand(ExecuteDeleteItem);
            this.InsertItemCommand = new DelegateCommand(ExecuteInsertItem);
            this.PrepareNewItemCommand = new DelegateCommand(ExecutePrepareNewItem);
            this.SaveChangesCommand = new DelegateCommand(ExecuteSaveChanges);
            this.RefreshCommand = new DelegateCommand(ExecuteRefreshCommand);
            this.RejectChangesCommand = new DelegateCommand(ExecuteRejectChanges);
            this.RejectItemCommand = new DelegateCommand(ExecuteRejectItem);
            this.UpdateItemCommand = new DelegateCommand(ExecuteUpdateItem);
            this.ValidateItemCommand = new DelegateCommand(ExecuteValidateItem, CanValidateItem);
        }

        #endregion

        #region Fields

        private T _newItem;

        #endregion

        #region Properties

        public IEditableDataRepository<T> EditableEventsDataSource
        {
            get { return this.EventsDataSource as IEditableDataRepository<T>; }
        }

        public T NewItem
        {
            get { return this._newItem; }
            set
            {
                if (this._newItem != value)
                {
                    this._newItem = value;
                    this.OnPropertyChanged("NewItem");
                }
            }
        }

        #endregion

        #region Commands

        public DelegateCommand DeleteItemCommand { get; set; }
        public DelegateCommand InsertItemCommand { get; set; }
        public DelegateCommand PrepareNewItemCommand { get; set; }
        public DelegateCommand SaveChangesCommand { get; set; }
        public DelegateCommand RefreshCommand { get; set; }
        public DelegateCommand RejectChangesCommand { get; set; }
        public DelegateCommand RejectItemCommand { get; set; }
        public DelegateCommand UpdateItemCommand { get; set; }
        public DelegateCommand ValidateItemCommand { get; set; }

        #endregion

        #region Methods

        protected virtual bool CanValidateItem(object parameter)
        {
            UXScheduleViewEventModel evtModel = parameter as UXScheduleViewEventModel;
            if (evtModel != null)
            {
                if (string.IsNullOrEmpty(evtModel.Subject))
                {
                    return false;
                }
            }

            return true;
        }

        protected virtual void ExecuteDeleteItem(object parameter)
        {
            IList list = parameter as IList;
            if (list != null)
                this.EditableEventsDataSource.Delete(list);
            else
                this.EditableEventsDataSource.Delete(parameter as T);
        }

        protected virtual void ExecuteInsertItem(object parameter)
        {

        }

        protected virtual void ExecutePrepareNewItem(object parameter)
        {

        }

        protected virtual void ExecuteSaveChanges(object parameter)
        {
            this.IsBusy = true;

            this.EditableEventsDataSource.SaveChanges
            (
                () =>
                {
                    this.IsBusy = false;
                },
                exception =>
                {
                    this.IsBusy = false;

                    this.Presenter.ShowErrorMessage(
                        "An exception has occurred during save changes.\n" +
                        "Message: " + exception.Message + "\n" +
                        "Stack Trace: " + exception.StackTrace);
                }
            );
        }

        protected virtual void ExecuteRefreshCommand(object parameter)
        {
            this.LoadEventsData();
        }

        protected virtual void ExecuteRejectChanges(object parameter)
        {
            this.EditableEventsDataSource.RejectChanges();
        }

        protected virtual void ExecuteRejectItem(object parameter)
        {
            T entity = parameter as T;
            this.EditableEventsDataSource.RejectChanges(entity);
        }

        protected virtual void ExecuteUpdateItem(object parameter)
        {

        }

        protected virtual void ExecuteValidateItem(object parameter)
        {

        }

        #endregion
    }
}
Binding ViewModel to View
Copy Code
<Intersoft:UXScheduleView x:Name="SampleControl1" DisplayDate="1/2/2012"
            IsBusy="{Binding IsBusy, Mode=TwoWay}"
            EventsSource="{Binding Events}" CategoriesSource="{Binding Categories}" ResourcesSource="{Binding Resources}"
            CanUserAddItems="True" CanUserDeleteItems="True" CanUserEditItems="True" CanUserMoveItems="True" CanUserResizeItems="True"
            NewItem="{Binding NewItem, Mode=TwoWay}"
            PrepareNewItemCommand="{Binding PrepareNewItemCommand}" 
            InsertItemCommand="{Binding InsertItemCommand}"
            DeleteItemCommand="{Binding DeleteItemCommand}"
            UpdateItemCommand="{Binding UpdateItemCommand}"
            RejectItemCommand="{Binding RejectItemCommand}"
            ValidateItemCommand="{Binding ValidateItemCommand}"
            SaveChangesCommand="{Binding SaveChangesCommand}"
            RefreshCommand="{Binding RefreshCommand}>
    <Intersoft:UXScheduleDayView/>
    <Intersoft:UXScheduleWorkWeekView/>
    <Intersoft:UXScheduleWeekView IsActive="True"/>
    <Intersoft:UXScheduleMonthView/>
</Intersoft:UXScheduleView>

Handling the CUD operation can be a tedious task in the RIA development due to the repetitive processes. You can use the above generic templates to quickly handle the CUD operation in a more efficient way. The following code shows an example how to use the provided generic templates.

HospitalViewModel
Copy Code
using Intersoft.Client.UI.ScheduleView;
using UXScheduleView.Samples.ModelServices;
using UXScheduleView.Samples.Web;

namespace UXScheduleView.Samples.ViewModels
{
    public class HospitalViewModel : EditableScheduleViewModelBase<HospitalEvent>
    {
        #region Constructors

        public HospitalViewModel()
        {
            
        }

        #endregion

        protected override IDataRepository CategoriesDataSource
        {
            get { return HospitalCategoriesRepository.Instance; }
        }

        protected override IDataRepository EventsDataSource
        {
            get { return HospitalEventsRepository.Instance; }
        }

        protected override IDataRepository ResourcesDataSource
        {
            get { return HospitalResourcesRepository.Instance; }
        }
        
        protected override void ExecutePrepareNewItem(object parameter)
        {
            base.ExecutePrepareNewItem(parameter);

            UXScheduleViewEventModel evtModel = parameter as UXScheduleViewEventModel;
            HospitalEvent evt = this.EditableEventsDataSource.Create();

            if (evtModel.ResourceID == -1)
            {
                if (evtModel.GroupValues.Count > 0)
                {
                    foreach (string key in evtModel.GroupValues.Keys)
                    {
                        switch (key)
                        {
                            case "ResourceID":
                                evt.ResourceID = int.Parse(evtModel.GroupValues[key].ToString());
                                break;
                            case "CategoryID":
                                evt.CategoryID = int.Parse(evtModel.GroupValues[key].ToString());
                                break;
                        }
                    }
                }
                else
                {
                    evt.ResourceID = 1;
                }
            }

            this.NewItem = evt;
            this.EditableEventsDataSource.Insert(evt);
        }
    }
}

As you can see in the code above, you only need to override several properties and methods since most of other operations are already handled in the base class. Feel free to override any of the provided methods and add additional logic to suit your application's requirements.

When ValidateItemCommand return false, trying to lost focus in the current editing item will re-focus the item.

Interactive Validation

Besides the standard validation when committing changes during editing, UXScheduleView also provides interactive validation procedures that you can implement to limit certain interactive actions. Similar to handling the CUD operation, UXScheduleView also provides command-related properties that you can bind to your ViewModel. These command-related properties are listed as follows:

The following example shows how to utilize the above commands in a real-world scenario:

For these interactive validation commands, you need to handle the logic in CanExecuteCommand method instead of the ExecuteCommand method.
Interactive Validation
Copy Code
using System;
using Intersoft.Client.Framework.Input;
using Intersoft.Client.UI.ScheduleView;
using UXScheduleView.Samples.Web;

namespace UXScheduleView.Samples.ViewModels
{
    public class InteractiveValidationViewModel : HospitalViewModel
    {
        #region Constructors

        public InteractiveValidationViewModel()
        {
            this.CanUserAddItemCommand = new DelegateCommand(ExecuteCanUserAddItem, CanUserAddItem);
            this.CanUserDeleteItemCommand = new DelegateCommand(ExecuteCanUserDeleteItem, CanUserDeleteItem);
            this.CanUserEditItemCommand = new DelegateCommand(ExecuteCanUserEditItem, CanUserEditItem);
            this.CanUserMoveItemCommand = new DelegateCommand(ExecuteCanUserMoveItem, CanUserMoveItem);
            this.CanUserResizeItemCommand = new DelegateCommand(ExecuteCanUserResizeItem, CanUserResizeItem);
        }

        #endregion

        #region Commands

        public DelegateCommand CanUserAddItemCommand { get; set; }
        public DelegateCommand CanUserDeleteItemCommand { get; set; }
        public DelegateCommand CanUserEditItemCommand { get; set; }
        public DelegateCommand CanUserMoveItemCommand { get; set; }
        public DelegateCommand CanUserResizeItemCommand { get; set; }

        #endregion

        #region Methods

        private bool IsWeekend(DateTime date)
        {
            return date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
        }

        // User can only add item to week days.
        private bool CanUserAddItem(object parameter)
        {
            object[] parameters = (object[])parameter;

            bool allDayEvent = (bool)parameters[0];
            DateTime startDate = (DateTime)parameters[1];
            DateTime endDate = (DateTime)parameters[2];

            return !this.IsWeekend(startDate) && !this.IsWeekend(endDate);
        }

        // User can not delete item with resource id == 1
        private bool CanUserDeleteItem(object parameter)
        {
            HospitalEvent evt = parameter as HospitalEvent;
            if (evt != null)
            {
                if (evt.ResourceID == 1)
                    return false;
            }

            return true;
        }

        // User can not edit item with resource id == 1
        private bool CanUserEditItem(object parameter)
        {
            HospitalEvent evt = parameter as HospitalEvent;
            if (evt != null)
            {
                if (evt.ResourceID == 1)
                    return false;
            }

            return true;
        }

        // User can not move item to weekend
        private bool CanUserMoveItem(object parameter)
        {
            UXScheduleViewInteractiveData data = parameter as UXScheduleViewInteractiveData;
            if (data != null && data.TargetDate.HasValue)
            {
                HospitalEvent evt = data.EventModel.OriginalObject as HospitalEvent;
                if (evt != null)
                {
                    if (evt.ResourceID == 1)
                        return false;

                    DateTime startDate = data.TargetDate.Value;
                    TimeSpan diff = startDate - evt.StartDate;
                    DateTime endDate = evt.EndDate.Add(diff);

                    return !this.IsWeekend(startDate) && !this.IsWeekend(endDate);
                }
            }

            return true;
        }

        // User can not resize item to weekend
        private bool CanUserResizeItem(object parameter)
        {
            UXScheduleViewInteractiveData data = parameter as UXScheduleViewInteractiveData;
            if (data != null && data.TargetDate.HasValue)
            {
                HospitalEvent evt = data.EventModel.OriginalObject as HospitalEvent;
                if (evt != null)
                {
                    if (evt.ResourceID == 1)
                        return false;

                    return !this.IsWeekend(data.TargetDate.Value);
                }
            }

            return true;
        }

        private void ExecuteCanUserAddItem(object parameter)
        {
            throw new NotImplementedException();
        }

        private void ExecuteCanUserDeleteItem(object parameter)
        {
            throw new NotImplementedException();
        }

        private void ExecuteCanUserEditItem(object parameter)
        {
            throw new NotImplementedException();
        }

        private void ExecuteCanUserMoveItem(object parameter)
        {
            throw new NotImplementedException();
        }

        private void ExecuteCanUserResizeItem(object parameter)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}

Using Editing Form

UXScheduleView provides a sophisticated, built-in editing form to handle data editing with common fields. It also includes the support for recurring event which allows you to easily add, remove, and modify the recurring pattern. You can access the editing form from the context menu, or from the event detail such as described in the following sections.

Editing Event

UXScheduleView provides a built-in context menu that adapts to the selection. When an existing event is selected, the context menu will show commands related to event editing such as shown in the figure below.

The following figure shows the recurring event editing support through the event details.

When editing an existing event, the editing form will be shown with a callout pointing to that particular event, see the following figure.

Adding New Event

UXScheduleView provides a built-in context menu that adapts to the selection. When the selection is on empty cells, the context menu will show commands related to event creation such as shown in the figure below.

UXScheduleView also provides an intuitive Add button allowing users to quickly create a new event regardless of the selection. When the editing form is shown, the callout will be automatically pointing to the Add button such as shown in the figure below. You can customize the callout pointing offset by specifying a custom element in the AddButtonElement property.

  

This built-in editing form provides you a sophisticated user experience where you can easily modify the common fields such as Subject, Start Time, End Time, Location and more. You can also enable, disable, and modify the recurring pattern for a recurring event. The editing form smartly adapts to the type of event being edited.

For examples, if the occurrence of a recurring event is being edited, the Recurring tab will be automatically disabled while the checkbox to remove the recurring pattern will not be available such as  shown in the figure below.

Editing Recurring Event

To add or edit the pattern of a recurring event, you can select the Recurring tab provided in the editing form, then select and configure the desired recurring pattern.

Customizing Editing Form

UXScheduleView provides a sophisticated, built-in editing form to handle data editing with common fields. In some cases, you might want to add or remove fields from the editing form which can be achieved by customizing the EventDetailStyle property.

To learn more how to customize the editing form in UXScheduleView, see How-to: Customize Editing Form in UXScheduleView.

See Also

Tasks