Thursday, March 12, 2015

WPF: Simple TimePicker User Control for WPF

Hey Guys!
WPF gives us DatePicker but it doesn’t give us TimePicker. I tried to create a very simple TimePicker user control.

And the bumper offer is that it is free. Use it wherever you want. J
Hope this will help you a lot.

XAML Code:
==========

I used TextBlocks to show Hours and Minutes. Up and Down buttons are attached to increment decrement hours and minutes. Drop down with AM/PM values actually add/subtracts 12 hours from actual time based on your selection. I have created SelectedTime dependency property to do bindings.

<UserControl x:Class="UserControls.ucDateTimeUpDown"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d">
    <Grid>
        <Border BorderBrush="Black" BorderThickness=".25" />
        <StackPanel Orientation="Horizontal">


            <TextBlock x:Name="AddHoursTextBox"
                       MinWidth="20"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="{Binding DisplayTimeHours,
                                      Mode=OneWay}"
                       TextAlignment="Center" />
            <StackPanel HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Orientation="Vertical">
                <Button x:Name="HourUpButton" Click="HourUpButton_OnClick">
                    <Button.Template>
                        <ControlTemplate TargetType="Button">
                            <Border BorderBrush="Black" BorderThickness=".25">
                                <Viewbox Width="10" Height="10">
                                    <Image Source="{StaticResource UpImage}" />
                                </Viewbox>
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>

                <Button x:Name="HourDownButton"
                        Margin="0,-1,0,0"
                        Click="HourDownButton_OnClick">
                    <Button.Template>
                        <ControlTemplate TargetType="Button">
                            <Border BorderBrush="Black" BorderThickness=".25">
                                <Viewbox Width="10" Height="10">
                                    <Image Source="{StaticResource DownImage}" />
                                </Viewbox>
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </StackPanel>
            <TextBlock Margin="3,0,0,0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="h." />
            <TextBlock Margin="3,0,0,0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text=":"
                       TextAlignment="Center" />
            <TextBlock x:Name="AddMinutesTextBox"
                       MinWidth="20"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="{Binding DisplayTimeMinutes,
                                      Mode=OneWay}"
                       TextAlignment="Center" />


            <StackPanel HorizontalAlignment="Center"
                        VerticalAlignment="Center"
                        Orientation="Vertical">
                <Button x:Name="MinutesUpButton" Click="MinutesUpButton_OnClick">
                    <Button.Template>
                        <ControlTemplate TargetType="Button">
                            <Border BorderBrush="Black" BorderThickness=".25">
                                <Viewbox Width="10" Height="10">
                                    <Image Source="{StaticResource UpImage}" />
                                </Viewbox>
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>

                <Button x:Name="MinutesDownButton"
                        Margin="0,-1,0,0"
                        Click="MinutesDownButton_OnClick">
                    <Button.Template>
                        <ControlTemplate TargetType="Button">
                            <Border BorderBrush="Black" BorderThickness="0.25">
                                <Viewbox Width="10" Height="10">
                                    <Image Source="{StaticResource DownImage}" />
                                </Viewbox>
                            </Border>
                        </ControlTemplate>
                    </Button.Template>
                </Button>
            </StackPanel>
            <TextBlock Margin="3,0,3,0"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Text="m." />

            <ComboBox x:Name="AmPmComboBox"
                      MinWidth="45"
                      HorizontalAlignment="Center"
                      VerticalAlignment="Center"
                      BorderThickness=".25"
                      ItemsSource="{Binding AmPmTypes}"
                      SelectedItem="{Binding DisplayAmPm}" />
        </StackPanel>


    </Grid>
</UserControl>

Code behind:
=========

The code behind may not be so good. We can restructure and re-factor this code. I made this user control in hurry so pardon me for code quality.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
  /// <summary>
  /// Interaction logic for ucDateTimeUpDown.xaml
  /// </summary>
  public partial class ucDateTimeUpDown : UserControl, INotifyPropertyChanged
  {
    #region Private Member variable

    private DateTime _currentTime = DateTime.UtcNow;
    private bool _adHours;
    private bool _addMinutes;
    private ObservableCollection<string> _amPmTypes 
= new ObservableCollection<string>();
    private string _displayAmPm;

    #endregion

    #region Constructors

    public ucDateTimeUpDown()
    {
      InitializeComponent();
      this.DataContext = this;
      AmPmTypes.Add("AM");
      AmPmTypes.Add("PM");
      CurrentTime = DateTime.UtcNow.ToLocalTime();
      SelectedTime = CurrentTime.ToLocalTime().ToString("t");
    }

    #endregion

    #region Public Properties

    public ObservableCollection<string> AmPmTypes
    {
      get { return _amPmTypes; }
      set { _amPmTypes = value; }
    }

    public string DisplayTime
    {
      get { return _currentTime.ToLocalTime().ToString("t"); }
    }


    public string DisplayAmPm
    {
      get
      {
        if (_currentTime.ToLocalTime().Hour >= 0 
&& _currentTime.ToLocalTime().Hour < 12)
          _displayAmPm = AmPmTypes.FirstOrDefault(s => s.Equals("AM"));
        else
        {
          if (_currentTime.ToLocalTime().Hour >= 12)
          {
            _displayAmPm = AmPmTypes.FirstOrDefault(s => s.Equals("PM"));
          }
        }

        return _displayAmPm;
      }
      set
      {
        if (!value.Equals(_displayAmPm))
        {
          if (value.Equals("PM"))
            CurrentTime = CurrentTime.ToLocalTime().AddHours(12);
          else
          {
            CurrentTime = CurrentTime.ToLocalTime().AddHours(-12);
          }
        }
        _displayAmPm = value;
      }
    }

    public string DisplayTimeHours
    {
      get
      {
        var hours = _currentTime.ToLocalTime().Hour;
        return hours > 12 ? (hours - 12).ToString("00") : hours.ToString("00");
        //return hours.ToString();
      }
      set
      {
        var hour = 0;
        Int32.TryParse(value, out hour);
        CurrentTime = CurrentTime.ToLocalTime().AddHours(hour);
        OnPropertyChanged("DisplayTime");
        OnPropertyChanged("DisplayTimeHours");
        OnPropertyChanged("DisplayTimeMinutes");
      }
    }

    public string DisplayTimeMinutes
    {
      get { return _currentTime.ToLocalTime().Minute.ToString("00"); }
      set
      {
        var minutes = 0;
        Int32.TryParse(value, out minutes);
        CurrentTime = CurrentTime.ToLocalTime().AddMinutes(minutes);
        OnPropertyChanged("DisplayTime");
        OnPropertyChanged("DisplayTimeHours");
        OnPropertyChanged("DisplayTimeMinutes");
      }
    }

    public DateTime CurrentTime
    {
      get { return _currentTime; }
      set
      {
        _currentTime = value;

        OnPropertyChanged("CurrentTime");
        OnPropertyChanged("DisplayTime");
        OnPropertyChanged("DisplayTimeHours");
        OnPropertyChanged("DisplayTimeMinutes");
        OnPropertyChanged("DisplayAmPm");
        SelectedTime = value.ToLocalTime().ToString("t");
      }
    }

    #endregion

    #region Dependency Properties

    public static readonly DependencyProperty SelectedTimeProperty 
= DependencyProperty.Register(
      "SelectedTime", typeof (string), typeof (ucDateTimeUpDown), 
new PropertyMetadata(default(string)));

    public string SelectedTime
    {
      get { return ((DateTime) GetValue(SelectedTimeProperty))
.ToLocalTime().ToString("t"); }
      set { SetValue(SelectedTimeProperty, value); }
    }

    #endregion

    #region Methods

    private void MinutesUpButton_OnClick(object sender, RoutedEventArgs e)
    {
      CurrentTime = CurrentTime.AddMinutes(1);
      SelectedTime = CurrentTime.ToLocalTime().ToString("t");
    }

    private void MinutesDownButton_OnClick(object sender, RoutedEventArgs e)
    {
      CurrentTime = CurrentTime.AddMinutes(-1);
      SelectedTime = CurrentTime.ToLocalTime().ToString("t");
    }

    private void HourUpButton_OnClick(object sender, RoutedEventArgs e)
    {
      CurrentTime = CurrentTime.AddHours(1);
      SelectedTime = CurrentTime.ToLocalTime().ToString("t");
    }

    private void HourDownButton_OnClick(object sender, RoutedEventArgs e)
    {
      CurrentTime = CurrentTime.AddHours(-1);
      SelectedTime = CurrentTime.ToLocalTime().ToString("t");
    }

    #endregion

    #region INotifyPropertyChanged Implementation

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
      if (null != PropertyChanged)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

    #endregion
  }
}



I hope you will find above user control quite useful. You suggestions are welcome. Please send your comments and improvement suggestion.

Have a good day!

Friday, February 6, 2015

WPF-MVVM: RelayCommand Implementation

In WPF if we are implementing MVVM pattern then we need to play with Command rather than Events. You can use ICommand interface to create each command class. Implementation of ICommand in a class gives you CanExecute(), Execute() methods which take part in the action performed by Command.

Rather than making Command Class for each Command we can implement a generic Relay Command to get Command. Below is a RelayCommand class that we will implement.

/// <summary>
    /// To register commands in MMVM pattern
    /// </summary>
    class RelayCommands : ICommand
    {
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;
        /// <summary>
        /// Constructer takes Execute events to register in CommandManager.
        /// </summary>
        /// <param name="execute">Execute method as action.</param>
        public RelayCommands(Action<object> execute)
            : this(execute, null)
        {
            try
            {
                if (null == execute)
                {
                    throw new NotImplementedException("Not implemented");
                }
                _execute = execute;
            }
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// Constructer takes Execute and CanExcecute events to register in CommandManager.
        /// </summary>
        /// <param name="execute">Execute method as action.</param>
        /// <param name="canExecute">CanExecute method as return bool type.</param>
        public RelayCommands(Action<object> execute, Predicate<object> canExecute)
        {
            try
            {
                if (null == execute)
                {
                    _execute = null;
                    throw new NotImplementedException("Not implemented");
                }
                _execute = execute;
                _canExecute = canExecute;
            }
            catch (Exception)
            {
            }
        }
        /// <summary>
        /// Can Executed Changed Event
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
        /// <summary>
        /// Execute method.
        /// </summary>
        /// <param name="parameter">Method parameter.</param>
        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        /// <summary>
        /// CanExecute method.
        /// </summary>
        /// <param name="parameter">Method parameter.</param>
        /// <returns>Return true if can execute.</returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null || _canExecute(parameter);
        }
        /// <summary>
        /// InvalidateCanExecute method will initiate validation of the Command.
        /// </summary>
        private void InvalidateCanExecute()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }



You need to create object of RelayCommand with delegate as paramenters point to CanExecute and Execute methods.
Like:

       ICommand _createNewCollegeCommand;
 
       /// <summary>
       /// Command to create new College.
       /// </summary>
       public ICommand CreateNewCollegeCommand
       {
           get
           {
               if (_createNewCollegeCommand == null)
               {
                   _createNewCollegeCommand = 
new RelayCommands(param => this.CreateNewCollegeCommand_Execute(param),
                  param => this.CreateNewCollegeCommand_CanExecute(param));
               }
               return _createNewCollegeCommand;
           }
       }
 
       bool CreateNewCollegeCommand_CanExecute(object param)
       {
           return true;
       }
 
       void CreateNewCollegeCommand_Execute(object param)
       {
           IndividualCollegeViewModel indCollegeVM = 
this._individualCollegeVMFactory();
 
           //Set the DataContext
           (indCollegeVM.View as AddCollege).DataContext = indCollegeVM;
 
           //Show dialog box to add College.
           (indCollegeVM.View as AddCollege).Owner = this.CurrentView;
           (indCollegeVM.View as AddCollege).ShowDialog();
       }


Hope this will be helpful to you all.
Have a nice day!