[]
Using Scheduler control, you can display schedules for multiple users. For this, you need to perform the following steps:
In this step you will create your application resources and data bindings. You will also add and customize a Scheduler control.
Follow these steps:
Set up the application and configure the data source. For detailed steps, see Quick Start.
In the XAML view, add a <Window.Resources></Window.Resources> tag and add a set of <DataTemplate></DataTemplate> tags to it.
Specify Key property in the <DataTemplate> tag to control the group header for the custom style using the following XAML markup:
<DataTemplate x:Key="myCustomGroupHeaderTemplate"></DataTemplate>
Insert the following markup between the <DataTemplate> tags to add the DataTemplate resources:
<DataTemplate.Resources>
<ControlTemplate x:Key="looklessButton" TargetType="{x:Type Button}">
<Border>
<ContentPresenter Margin="4,0" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</DataTemplate.Resources>
Insert a set of <Grid></Grid> tags beneath the <DataTemplate.Resources></DataTemplate.Resources> tag and set SnapsToDevicePixels="True" for the grid.
Use the following markup to add row and column definitions to the Grid component:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
Create the C1BrushBuilders and the Border control for the Grid component by adding the following code beneath the <Grid.RowDefinitions></Grid.RowDefinitions> tags :
<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}" />
Add the ButtonTemplates and the TextBlocks after the <Border/> tag to control the navigation and display for the Scheduler view:
<!-- 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" />
Create another DataTemplate to control the group header for the TimeLine style:
<!-- 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>
Beneath the closing </Window.Resources> tag, insert a set of <Grid></Grid> tags.
Insert the following markup between the <Grid></Grid> tags:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
Use the following code within the <Grid> tags to add a ToolBar and a ListBox to your application:
<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>
Add Scheduler and create the bindings for your application:
<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>
Navigate to the Code view and Import the following namespaces:
using C1.WPF.Schedule;
using System.Collections.Specialized;
using C1.C1Schedule;
using System.Windows;
using System.Windows.Controls;
using SamplesName.C1NWindDataSetTableAdapters;
Add the following fields in the MainWindow class:
private AppointmentsTableAdapter appointmentsTableAdapter = new AppointmentsTableAdapter();
private EmployeesTableAdapter employeesTableAdapter = new EmployeesTableAdapter();
private CustomersTableAdapter customersTableAdapter = new CustomersTableAdapter();
private C1NWindDataSet dataSet = new C1NWindDataSet();
Insert the following handlers below the InitializeComponent() method and call to get data from the database:
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);
Set the mappings and DataSource for the AppointmentStorage:
//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;
Set the mappings and DataSOurce for the OwnerStorage:
//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;
Set the mappings and DataSource for the ContactStorage:
//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;
Add the following code to set the IsChecked property of the btnDay, create StyleChanged event for the scheduler control and MainWindowLoaded event.
btnDay.IsChecked = true;
Scheduler.StyleChanged += new System.EventHandler<RoutedEventArgs>(Scheduler_StyleChanged);
this.Loaded += MultiUser_Loaded;
Add the following methods to update the group items, owners and contacts.
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() + ")";
}
}
}
}
Get the current window, save changes and prevent showing reminders using the following 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;
}
}
}
Press F5 to run your application.