protected override Size MeasureOverride(Size availableSize) { if (_hasViewChanged) { // In determining the labels to display, it must be kept in mind that the spacings between // the labels might be variable depending on the current time unit. For instance, if the // timeline is measured in months, then the labels will not be equally spaced apart because // a month is anywhere from 28 to 31 days. The solution is to produce labels one at a time // while the total accumulated width is less than the viewport width. // // 1. First we first convert the current horizontal offset into a TimeSpan, and add // that to the timeline's start time to get the time of the viewport's left edge. This // time may be more precise than the timeline's current unit of measurement. For // example, the viewport left time may be defined down to the second, but the timeline's // unit is an hour. Therefore, we must round up to the nearest unit. // // 2. The difference between the rounded date and the precise date is the time between // the start of the viewport and the initial label. Converting this TimeSpan into pixels // will yield the x position of the initial label relative to the viewport's left edge. // We can store this value along with the label number for use during the arranging process. // // 3. Now we can create a label, add it to the panel, and measure it. // // 4. Next we increment the rounded date by whatever is set as the timeline's unit. // // 5. Repeat steps 2 to 4 until the rounded date is equal to or exceeds the date of the // viewport's right edge. Children.Clear(); if (_labelOffsets == null) { _labelOffsets = new Dictionary<int, double>(); } else { _labelOffsets.Clear(); } ExtendedDateTime viewportLeftTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset); ExtendedDateTime viewportRightTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset + availableSize.Width); ExtendedDateTime labelTime = null; int labelIndex = 0; switch (Resolution) { case TimeResolution.Century: labelTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 100); break; case TimeResolution.Decade: labelTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 10); break; case TimeResolution.Year: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Year); break; case TimeResolution.Month: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Month, false); break; case TimeResolution.Day: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Day, false); break; case TimeResolution.Hour: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Hour, false); break; case TimeResolution.Minute: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Minute, false); break; case TimeResolution.Second: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Second, false); break; default: break; } while (labelTime < viewportRightTime) { var label = new TextBlock(); label.Text = labelTime.ToString(); label.FontFamily = FontFamily; label.FontSize = FontSize; label.Foreground = Foreground; switch (Resolution) { case TimeResolution.Century: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(100); break; case TimeResolution.Decade: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(10); break; case TimeResolution.Year: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(1); break; case TimeResolution.Month: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddMonths(1); break; case TimeResolution.Day: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromDays(1); break; case TimeResolution.Hour: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromHours(1); break; case TimeResolution.Minute: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromMinutes(1); break; case TimeResolution.Second: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromSeconds(1); break; default: break; } Children.Add(label); label.Measure(availableSize); labelIndex++; } _hasViewChanged = false; return availableSize; } foreach (UIElement child in Children) { child.Measure(availableSize); } return availableSize; }
protected override Size MeasureOverride(Size availableSize) { if (_hasViewChanged) { // In determining the number of guidelines to display, it must be kept in mind that // the spacings between the lines might be variable depending on the // current time unit. For instance, if the timeline is measured in months, then the // lines will not be equally spaced apart because a month is anywhere from 28 to 31 days. // The solution is to produce lines one at a time while the total accumulated width is // less than the viewport width. // // 1. First we first convert the current horizontal offset into a TimeSpan, and add // that to the timeline's start time to get the time of the viewport's left edge. This // time may be more precise than the timeline's current unit of measurement. For // example, the viewport left time may be defined down to the second, but the timeline's // unit is an hour. Therefore, we must round up to the nearest unit. // // 2. The difference between the rounded date and the precise date is the time between // the start of the viewport and the initial line. Converting this TimeSpan into pixels // will yield the x position of the initial line relative to the viewport's left edge. // We can store this value along with the line number for use during the arranging process. // // 3. Now we can create a guideline, add it to the panel, and measure it. // // 4. Next we increment the rounded date by whatever is set as the timeline's unit. // // 5. Repeat steps 2 to 4 until the rounded date is equal to or exceeds the date of the // viewport's right edge. Children.Clear(); if (_lineOffsets == null) { _lineOffsets = new Dictionary<int, double>(); } else { _lineOffsets.Clear(); } ExtendedDateTime viewportLeftTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset); ExtendedDateTime viewportRightTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset + availableSize.Width); ExtendedDateTime guidelineTime = null; int guidelineIndex = 0; switch (Resolution) { case TimeResolution.Century: guidelineTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 100 + 100); break; case TimeResolution.Decade: guidelineTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 10 + 10); break; case TimeResolution.Year: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Year, true); break; case TimeResolution.Month: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Month, true); break; case TimeResolution.Day: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Day, true); break; case TimeResolution.Hour: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Hour, true); break; case TimeResolution.Minute: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Minute, true); break; case TimeResolution.Second: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Second, true); break; default: break; } while (guidelineTime < viewportRightTime) { var guideline = new Line(); guideline.Y2 = availableSize.Height; guideline.StrokeThickness = 1; guideline.UseLayoutRounding = true; guideline.SnapsToDevicePixels = true; switch (Resolution) { case TimeResolution.Century: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % (100 * MajorFrequency) == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(100); break; case TimeResolution.Decade: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % (10 * MajorFrequency) == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(10); break; case TimeResolution.Year: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(1); break; case TimeResolution.Month: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Month % MajorFrequency == 0 ? MajorBrush : MinorBrush; try { guidelineTime = guidelineTime.AddMonths(1); } catch (Exception) { var month = guidelineTime.Month + 1; var year = guidelineTime.Year; if (month > 12) { month -= 12; year++; } var day = ExtendedDateTimeCalculator.DaysInMonth(year, month); guidelineTime = new ExtendedDateTime(year, month, day, guidelineTime.Hour, guidelineTime.Minute, guidelineTime.Second, guidelineTime.UtcOffset.Hours, guidelineTime.UtcOffset.Minutes); } break; case TimeResolution.Day: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Day % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromDays(1); break; case TimeResolution.Hour: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Hour % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromHours(1); break; case TimeResolution.Minute: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Minute % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromMinutes(1); break; case TimeResolution.Second: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Second % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromSeconds(1); break; default: break; } Children.Add(guideline); guideline.Measure(availableSize); guidelineIndex++; } _hasViewChanged = false; return availableSize; } foreach (UIElement child in Children) { child.Measure(availableSize); } return availableSize; }