Customize Scheduler / Create a Multi-User Schedule
Create a Multi-User Schedule

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