I am creating a rudimentary Gantt chart as a visual representation of a schedule of events. To do this, I have an ItemsControl to render schedule line items in a StackPanel. Within that "parent" ItemsControl, I have another ItemsControl to render the Gantt chart view - basically just shapes. This looks like the following:
<ItemsControl ItemsSource="{Binding ScheduleLines}"
Grid.Row="1"
Height="100">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"
Grid.Column="0"/>
<ItemsControl ItemsSource="{Binding Events}"
Grid.Column="1">
<ItemsPanelTemplate>
<components:ScheduleRowPanel/>
</ItemsPanelTemplate>
<ItemContainerTemplate>
<scheduleitems:ScheduleEventElement EventDate="{Binding EventDate}"
Status="{Binding Status}"/>
</ItemContainerTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The datacontext for this view has an ObservableCollection
called ScheduleLines
. Each item of ScheduleLines
has another observable collection of Events
.
Inside the child ItemsControl, I have a custom panel that arranges the events:
public class ScheduleRowPanel : Panel
{
//Scale and MinDate are dependency properties on this custom panel
protected override Size ArrangeOverride(Size finalSize)
{
foreach(UIElement child in Children)
{
ArrangeChild(child, MinDate, Scale, finalSize.Height);
}
return finalSize;
}
private void ArrangeChild(UIElement child, DateOnly minDate, double scale, double panelHeight)
{
if (child.GetType() == typeof(ScheduleEventElement))
{
ScheduleEventElement eventElement = (ScheduleEventElement)child;
DateOnly eventDate = eventElement.EventDate;
double xoffset = scale * (eventDate.DayNumber - minDate.DayNumber);
double yoffset = panelHeight / 2;
child.Arrange(new Rect(xoffset, yoffset, 50, 50));
}
else
{
throw new InvalidOperationException("Have not implemented any other type of schedule entity");
}
}
}
The panel arranges events from the following, that are rotated rectangles with a fill:
public class ScheduleEventElement : FrameworkElement
{
public DateOnly EventDate
{
get { return (DateOnly)GetValue(EventDateProperty); }
set { SetValue(EventDateProperty, value); }
}
// Using a DependencyProperty as the backing store for EventDate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EventDateProperty =
DependencyProperty.Register("EventDate", typeof(DateOnly), typeof(ScheduleEventElement), new PropertyMetadata(DateOnly.FromDayNumber(1)));
public EventStatus Status
{
get { return (EventStatus)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
// Using a DependencyProperty as the backing store for Status. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(EventStatus), typeof(ScheduleEventElement), new PropertyMetadata(EventStatus.Scheduled));
protected override void OnRender(DrawingContext drawingContext)
{
RectangleGeometry rectangle = new RectangleGeometry();
SolidColorBrush fill = new SolidColorBrush();
rectangle.Rect = new Rect(0, 0, 10, 0);
RotateTransform rotate = new RotateTransform();
rotate.CenterX = 0.5;
rotate.CenterY = 0.5;
rotate.Angle = 45;
rectangle.Transform = rotate;
switch (Status)
{
case EventStatus.Scheduled:
fill.Color = (Color)ColorConverter.ConvertFromString("#262626");
break;
case EventStatus.Early:
fill.Color = (Color)ColorConverter.ConvertFromString("#009d9a");
break;
case EventStatus.Late:
fill.Color = (Color)ColorConverter.ConvertFromString("#6929c4");
break;
}
drawingContext.DrawGeometry(fill, null, rectangle);
}
}
I am getting the following error when running the nested ItemControl:
I am understanding this as I need to modify the inner ItemControl to instead of having ItemsSource = {Binding Events}
to have <ItemsControl> <ItemsControl.ItemsSource/>
, but I am not sure how to specify the items source using that syntax. Is my understanding correct? If so, what would be the correct syntax for this?
Further, do I have a correct implementation of the ItemsContainerTemplate
? Or should this be in the ItemsTemplate
?
Thanks
CodePudding user response:
you can't just write <ItemsPanelTemplate>
and <ItemContainerTemplate>
inside <ItemsControl>
tag, you need to write them inside relveant property tags: <ItemsControl.ItemsPanel>
and <ItemsControl.ItemTemplate>
.
<ItemsControl ItemsSource="{Binding Events}"
Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<components:ScheduleRowPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<scheduleitems:ScheduleEventElement EventDate="{Binding EventDate}"
Status="{Binding Status}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>