Scheduler for WPF | ComponentOne
Customize Scheduler / Create a Multi-User Schedule
In This Topic
    Create a Multi-User Schedule
    In This Topic

    Using Scheduler control, you can display schedules for multiple users. For this, you need to perform the following steps:

    Create the Resources and the Scheduler Control

     In this step you will create your application resources and data bindings. You will also add and customize a Scheduler control. 

    Follow these steps:

    1. Set up the application and configure the data source. For detailed steps, see Quick Start.
    2. In the XAML view, add a <Window.Resources></Window.Resources> tag and add a set of <DataTemplate></DataTemplate> tags to it.
    3. Specify Key property in the <DataTemplate> tag to control the group header for the custom style using the following XAML markup:
      XAML
      Copy Code
      <DataTemplate x:Key="myCustomGroupHeaderTemplate"></DataTemplate>
      
    4. Insert the following markup between the <DataTemplate> tags to add the DataTemplate resources:
      XAML
      Copy Code
      <DataTemplate.Resources>
          <ControlTemplate x:Key="looklessButton" TargetType="{x:Type Button}">
              <Border>
                  <ContentPresenter Margin="4,0" VerticalAlignment="Center" />
              </Border>
          </ControlTemplate>
      </DataTemplate.Resources>
      
    5. Insert a set of <Grid></Grid> tags beneath the <DataTemplate.Resources></DataTemplate.Resources> tag and set SnapsToDevicePixels="True" for the grid.
    6. Use the following markup to add row and column definitions to the Grid component:
      XAML
      Copy Code
      <Grid.ColumnDefinitions>
           <ColumnDefinition Width="Auto"/>
           <ColumnDefinition Width="Auto"/>
           <ColumnDefinition/>
           <ColumnDefinition Width="Auto"/>
           <ColumnDefinition Width="Auto"/>
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
           <RowDefinition />
           <RowDefinition />
      </Grid.RowDefinitions>
      
    7. Create the C1BrushBuilders and the Border control for the Grid component by adding the following code beneath the <Grid.RowDefinitions></Grid.RowDefinitions> tags :
      XAML
      Copy Code
      <c1:C1BrushBuilder x:Name="Background"  Input="{Binding Background}" />
      <c1:C1BrushBuilder x:Name="BorderBrush" Input="{Binding Background}" />
      <Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="2" Grid.RowSpan="2" 
              BorderThickness="1,0,1,0" BorderBrush="{Binding Output, ElementName=BorderBrush}" 
              Background="{Binding Output, ElementName=Background}" />
      
    8. Add the ButtonTemplates and the TextBlocks after the <Border/> tag to control the navigation and display for the Scheduler view:
      XAML
      Copy Code
      <!-- navigate to the first group -->
      <Button Template="{StaticResource looklessButton}" Content="|<<" Grid.Column="0" Grid.RowSpan="2" VerticalAlignment="Center" FontSize="12" Command="c1:C1Scheduler.NavigateToPreviousGroupCommand" CommandParameter="Home" CommandTarget="{Binding Scheduler}" Visibility="{Binding ShowPreviousButton, Converter={x:Static c1:BooleanToVisibilityConverter.Default}}" />
      <!-- navigate to the previous group -->
      <Button Template="{StaticResource looklessButton}" Content="<" Grid.Column="1" Grid.RowSpan="2" VerticalAlignment="Center" FontSize="12" Command="c1:C1Scheduler.NavigateToPreviousGroupCommand" CommandTarget="{Binding Scheduler}" Visibility="{Binding ShowPreviousButton, Converter={x:Static c1:BooleanToVisibilityConverter.Default}}" />
      <!-- navigate to the next group -->
      <Button Template="{StaticResource looklessButton}" Content=">" Grid.Column="3" Grid.RowSpan="2" VerticalAlignment="Center" FontSize="12" Command="c1:C1Scheduler.NavigateToNextGroupCommand" CommandTarget="{Binding Scheduler}" Visibility="{Binding ShowNextButton, Converter={x:Static c1:BooleanToVisibilityConverter.Default}}" />
      <!-- navigate to the last group -->
      <Button Template="{StaticResource looklessButton}" Content=">>|" Grid.Column="4" Grid.RowSpan="2" VerticalAlignment="Center" FontSize="12" Command="c1:C1Scheduler.NavigateToNextGroupCommand" CommandParameter="End" CommandTarget="{Binding Scheduler}" Visibility="{Binding ShowNextButton, Converter={x:Static c1:BooleanToVisibilityConverter.Default}}" />
      <TextBlock Foreground="{Binding Path=Scheduler.Foreground}" Margin="10,3" Grid.Column="2" Visibility="{Binding IsSelected, Converter={x:Static c1:BooleanToVisibilityConverter.Default}, ConverterParameter=Invert}" Text="{Binding DisplayName}" VerticalAlignment="Center" HorizontalAlignment="Center" />
      <TextBlock Foreground="{Binding Path=Scheduler.Foreground}" Margin="10,3" Grid.Column="2" FontWeight="Bold" Visibility="{Binding IsSelected, Converter={x:Static c1:BooleanToVisibilityConverter.Default}}" Text="{Binding DisplayName}" VerticalAlignment="Center" HorizontalAlignment="Center" />
      <!-- show additional info from the EmployeesRow -->
      <TextBlock Foreground="{Binding Path=Scheduler.Foreground}" Margin="10,3" Grid.Column="2" Grid.Row="1" Text="{Binding Path=Tag.Title}" VerticalAlignment="Center" HorizontalAlignment="Center" />
      
    9. Create another DataTemplate to control the group header for the TimeLine style:
      XAML
      Copy Code
      <!-- use different group header for TimeLine style -->
      <DataTemplate x:Key="myCustomTimeLineGroupHeaderTemplate">
          <Grid IsHitTestVisible="False">
              <c1:C1BrushBuilder x:Name="Background"  Input="{Binding Background}" />
              <c1:C1BrushBuilder x:Name="BorderBrush"  Input="{Binding Background}" />
              <Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="4,1,0,1" BorderBrush="{Binding Output, ElementName=BorderBrush}" Background="{Binding Output, ElementName=Background}">
                  <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
                      <TextBlock TextWrapping="Wrap" Foreground="{Binding Path=Scheduler.Foreground}" Margin="2" Text="{Binding DisplayName}" HorizontalAlignment="Center" />
                      <!-- show additional info from the EmployeesRow -->
                      <TextBlock TextWrapping="Wrap" Foreground="{Binding Path=Scheduler.Foreground}" Margin="2" Text="{Binding Path=Tag[Title]}" HorizontalAlignment="Center" />
                  </StackPanel>
              </Border>
          </Grid>
      </DataTemplate>
      
    10. Beneath the closing </Window.Resources> tag, insert a set of <Grid></Grid> tags.
    11. Insert the following markup between the <Grid></Grid> tags:
      XAML
      Copy Code
      <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition />
          <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>
      
    12. Use the following code within the <Grid> tags to add a ToolBar and a ListBox to your application:
      XAML
      Copy Code
      <ToolBar Grid.Row="0" Grid.ColumnSpan="2">
          <RadioButton x:Name="btnDay" Content="Day" CommandTarget="{Binding ElementName=Scheduler}" Command="c1:C1Scheduler.ChangeStyleCommand" CommandParameter="{Binding Path=OneDayStyle, ElementName=Scheduler}" />
          <RadioButton x:Name="btnWorkWeek" Content="Work Week" CommandTarget="{Binding ElementName=Scheduler}" Command="c1:C1Scheduler.ChangeStyleCommand" CommandParameter="{Binding Path=WorkingWeekStyle, ElementName=Scheduler}" />
          <RadioButton x:Name="btnWeek" Content="Week" CommandTarget="{Binding ElementName=Scheduler}" Command="c1:C1Scheduler.ChangeStyleCommand" CommandParameter="{Binding Path=WeekStyle, ElementName=Scheduler}" />
          <RadioButton x:Name="btnMonth" Content="Month" CommandTarget="{Binding ElementName=Scheduler}" Command="c1:C1Scheduler.ChangeStyleCommand" CommandParameter="{Binding Path=MonthStyle, ElementName=Scheduler}" />
          <RadioButton x:Name="btnTimeLine" Content="Time Line" CommandTarget="{Binding ElementName=Scheduler}" Command="c1:C1Scheduler.ChangeStyleCommand" CommandParameter="{Binding Path=TimeLineStyle, ElementName=Scheduler}" />
      </ToolBar>
      <ListBox Grid.Column="0" Grid.Row="2" x:Name="lstUsers" MinHeight="100" Margin="2" ItemsSource="{Binding GroupItems, ElementName=Scheduler}">
          <ListBox.ItemTemplate>
              <DataTemplate>
                  <CheckBox Margin="2" Content="{Binding}" Tag="{Binding}" IsChecked="{Binding IsChecked}" />
              </DataTemplate>
          </ListBox.ItemTemplate>
      </ListBox>
      
    13. Add Scheduler and create the bindings for your application:
      XAML
      Copy Code
      <c1:C1Scheduler x:Name="Scheduler" GroupBy="Owner" GroupHeaderTemplate="{StaticResource myCustomGroupHeaderTemplate}" GroupPageSize="2" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" Style="{DynamicResource {ComponentResourceKey ResourceId=OneDayStyle, TypeInTargetAssembly=c1:C1Scheduler}}">
          <c1:C1Scheduler.Settings>
              <c1:C1SchedulerSettings FirstVisibleTime="07:00:00" AllowContactsEditing="False" AllowCategoriesEditing="False" AllowCategoriesMultiSelection="False" />
          </c1:C1Scheduler.Settings>
      </c1:C1Scheduler>
      

    Back to Top

    Add the Code to your Application

    1. Navigate to the Code view and Import the following namespaces:
      C#
      Copy Code
      using C1.WPF.Schedule;
      using System.Collections.Specialized;
      using C1.C1Schedule;
      using System.Windows;
      using System.Windows.Controls;
      using SamplesName.C1NWindDataSetTableAdapters;
      
    2. Add the following fields in the MainWindow class:
      C#
      Copy Code
      private AppointmentsTableAdapter appointmentsTableAdapter = new AppointmentsTableAdapter();
      private EmployeesTableAdapter employeesTableAdapter = new EmployeesTableAdapter();
      private CustomersTableAdapter customersTableAdapter = new CustomersTableAdapter();
      private C1NWindDataSet dataSet = new C1NWindDataSet();
      
    3. Insert the following handlers below the InitializeComponent() method and call to get data from the database:
      C#
      Copy Code
      Scheduler.ReminderFire += new EventHandler(scheduler_ReminderFire);
      
      Scheduler.GroupItems.CollectionChanged += new NotifyCollectionChangedEventHandler(GroupItems_CollectionChanged);
      
      //get data from the data base
      this.employeesTableAdapter.Fill(dataSet.Employees);
      this.customersTableAdapter.Fill(dataSet.Customers);
      this.appointmentsTableAdapter.Fill(dataSet.Appointments);
      
    4. Set the mappings and DataSource for the AppointmentStorage:
      C#
      Copy Code
      //set mappings and DataSource for the AppointmentStorages
      AppointmentStorage storage = Scheduler.DataStorage.AppointmentStorage;
      storage.Mappings.AppointmentProperties.MappingName = "Properties";
      storage.Mappings.Body.MappingName = "Description";
      storage.Mappings.End.MappingName = "End";
      storage.Mappings.IdMapping.MappingName = "AppointmentId";
      storage.Mappings.Location.MappingName = "Location";
      storage.Mappings.Start.MappingName = "Start";
      storage.Mappings.Subject.MappingName = "Subject";
      storage.Mappings.OwnerIndexMapping.MappingName = "Owner";
      storage.DataSource = dataSet.Appointments;
      
    5. Set the mappings and DataSOurce for the OwnerStorage:
      C#
      Copy Code
      //set mappings and DataSource for the OwnerStorage
      ContactStorage ownerStorage = Scheduler.DataStorage.OwnerStorage;
      INotifyCollectionChanged)ownerStorage.Contacts).CollectionChanged += new NotifyCollectionChangedEventHandler(Owners_CollectionChanged);
      ownerStorage.Mappings.IndexMapping.MappingName = "EmployeeId";
      ownerStorage.Mappings.TextMapping.MappingName = "FirstName";
      ownerStorage.DataSource = dataSet.Employees;
      
    6. Set the mappings and DataSource for the ContactStorage:
      C#
      Copy Code
      //set mappings and DataSource for the ContactStorage
      ContactStorage cntStorage = Scheduler.DataStorage.ContactStorage;
      ((INotifyCollectionChanged)cntStorage.Contacts).CollectionChanged += new NotifyCollectionChangedEventHandler(Contacts_CollectionChanged);
      cntStorage.Mappings.IdMapping.MappingName = "CustomerId";
      cntStorage.Mappings.TextMapping.MappingName = "CompanyName";
      cntStorage.DataSource = dataSet.Customers;
      
    7. Add the following code to set the IsChecked property of the btnDay, create StyleChanged event for the scheduler control and MainWindowLoaded event.
      C#
      Copy Code
      btnDay.IsChecked = true;
      Scheduler.StyleChanged += new System.EventHandler<RoutedEventArgs>(Scheduler_StyleChanged);
      this.Loaded += MultiUser_Loaded;
      
    8. Add the following methods to update the group items, owners and contacts.
      C#
      Copy Code
      void GroupItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
              foreach (SchedulerGroupItem group in Scheduler.GroupItems)
              {
                      if (group.Owner != null)
                      {
                              // set SchedulerGroupItem.Tag property to the data row. That allows to use data row fields in xaml for binding
                              int index = (int)group.Owner.Key[0];
                              C1NWindDataSet.EmployeesRow row = dataSet.Employees.Rows.Find(index) as C1NWindDataSet.EmployeesRow;
                              group.Tag = row;
                      }
              }
      }
      
      void Owners_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
              if (e.Action == NotifyCollectionChangedAction.Add)
              {
                      foreach (Contact cnt in e.NewItems)
                      {
                              C1NWindDataSet.EmployeesRow row = dataSet.Employees.Rows.Find(cnt.Key[0]) as C1NWindDataSet.EmployeesRow;
                              if (row != null)
                              {
                                      // set Contact.MenuCaption to the FirstName + LastName string
                                      cnt.MenuCaption = row["FirstName"].ToString() + " " + row["LastName"].ToString();
                              }
                      }
              }
      }
      
      void Contacts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
              if (e.Action == NotifyCollectionChangedAction.Add)
              {
                      foreach (Contact cnt in e.NewItems)
                      {
                              C1NWindDataSet.CustomersRow row = dataSet.Customers.Rows.Find(cnt.Key[0]) as C1NWindDataSet.CustomersRow;
                              if (row != null)
                              {
                                      // set Contact.MenuCaption to the CompanyName + ContactName string
                                      cnt.MenuCaption = row["CompanyName"].ToString() + " (" + row["ContactName"].ToString() + ")";
                              }
                      }
              }
      }
      
    9. Get the current window, save changes and prevent showing reminders using the following code:
      C#
      Copy Code
      // get current window
                      private void MultiUser_Loaded(object sender, RoutedEventArgs e)
                      {
                              Window window = Window.GetWindow(this);
                  window.Closing += Window_Closing;
                      }
                      // save changes
                      private void Window_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
              {
                              this.appointmentsTableAdapter.Update(dataSet.Appointments);
                      }
      
              // prevent showing reminders
              private void scheduler_ReminderFire(object sender, ReminderActionEventArgs e)
                      {
                              e.Handled = true;
                      }
                      private void Scheduler_StyleChanged(object sender, RoutedEventArgs e)
                      {
                              if (Scheduler.Style == Scheduler.TimeLineStyle)
                              {
                                      // update group header (use different headers for TimeLine and other views)
                                      Scheduler.GroupHeaderTemplate = (DataTemplate)Resources["myCustomTimeLineGroupHeaderTemplate"];
                                      btnTimeLine.IsChecked = true;
                              }
                              else
                              {
                                      // update group header (use different headers for TimeLine and other views)
                                      Scheduler.GroupHeaderTemplate = (DataTemplate)Resources["myCustomGroupHeaderTemplate"];
                                      // update toolbar buttons state according to the current C1Scheduler view
                                      if (Scheduler.Style == Scheduler.WorkingWeekStyle)
                                      {
                                              btnWorkWeek.IsChecked = true;
                                      }
                                      else if (Scheduler.Style == Scheduler.WeekStyle)
                                      {
                                              btnWeek.IsChecked = true;
                                      }
                                      else if (Scheduler.Style == Scheduler.MonthStyle)
                                      {
                                              btnMonth.IsChecked = true;
                                      }
                                      else
                                      {
                                              btnDay.IsChecked = true;
                                      }
                              }
                      }
      

    Back to Top

    Run the Application

    Press F5 to run your application.

    Back to Top