WPF Layout algorithm woes - control will resize, but not below some arbitrary value
I'm working on an application for a client, and one of the requirements is the ability to make appointments, and display the current week's appointments in a visual format, much like in Google Calendar's or Microsoft Office. I found a great (3 part) article on codeproject, in which he builds a "RangePanel", and composes one for each "period" (for example, the work day.) You can find part 1 here:
http://www.codeproject.com/KB/WPF/OutlookWpfCalendarPart1.aspx
The code presents, but seems to choose an arbitrary height value overall (440.04), and won't resize below that without clipping. What I mean to say, is that the window/container will resize, but it just cuts off the bottom of the control instead of recalculating the height of the range panels, and the controls in the range panels representing the appointment. It will resize and recalculate for greater values, but not less.
Code-wise, what's happening is that when you resize below that value, first the MeasureOverride
is called with the correct "new height". However, by the time the ArrangeOverride
method is called, it's passing the same 440.04 value as the height to arrange to.
I need to find a solution/workaround, but any information that you can provide that might direct me for things to look into would also be greatly appreciated (I understand how frustrating it is to debug code when you don't have the codebase in front of you. :) )
The code for the various Arrange and Measure functions are provided below. The CalendarView
control has a CalendarViewContentPresenter
, which handles several periods. Then, the periods have a CalendarPeriodContentPresenter
, which handles each "block" of appointments. Finally, the RangePanel
has its own implementation. (To be honest, I'm still a bit hazy on how the control works, so if my explanations are a bit hazy, the article I linked probably has a more cogent explanation. :) )
CalendarViewContentPresenter:
protected override Size ArrangeOverride(Size finalSize)
{
int columnCount = this.CalendarView.Periods.Count;
Size columnSize = new Size(finalSize.Width / columnCount, finalSize.Height);
double elementX = 0;
foreach (UIElement element in this.visualChildren)
{
element.Arrange(new Rect(new Point(elementX, 0), columnSize));
elementX = elementX + columnSize.Width;
}
return finalSize;
}
protected override Size MeasureOverride(Size constraint)
{
this.GenerateVisualChildren();
this.GenerateListViewItemVisuals();
// If it's coming back infinity, just return some value.
if (constraint.Width == Double.PositiveInfinity)
constraint.Width = 10;
if (constraint.Height == Double.PositiveInfinity)
constraint.Height = 10;
return constraint;
}
CalendarViewPeriodPersenter:
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement element in this.visualChildren)
{
element.Arrange(new Rect(new Point(0, 0), finalSize));
}
return finalSize;
}
protected override Size MeasureOverride(Size constraint)
{
this.GenerateVisualChildren();
return constraint;
}
RangePanel:
protected override Size ArrangeOverride(Size finalSize)
{
double containerRange = (this.Maximum - this.Minimum);
foreach (UIElement element in this.Children)
{
double begin = (double)element.GetValue(RangePanel.BeginProperty);
double end = (double)element.GetValue(RangePanel.EndProperty);
double elementRange = end - begin;
Size size = new Size();
size.Width = (Orientation == Orientation.Vertical) ? finalSize.Width : elementRange / containerRange * finalSize.Width;
size.Height = (Orientation == Orientation.Vertical) ? elementRange / containerRange * finalSize.Height : finalSize.Height;
Point location = new Point();
location.X = (Orientation == Orientation.Vertical) ? 0 : (begin - this.Minimum) / containerRange * finalSize.Width;
location.Y = (Orientation == Orientation.Vertical) ? (begin - this.Minimum) / containerRange * finalSize.Height : 0;
element.Arrange(new Rect(location, size));
}
return finalSize;
}
protected override Size 开发者_如何学编程MeasureOverride(Size availableSize)
{
foreach (UIElement element in this.Children)
{
element.Measure(availableSize);
}
// Constrain infinities
if (availableSize.Width == double.PositiveInfinity)
availableSize.Width = 10;
if (availableSize.Height == double.PositiveInfinity)
availableSize.Height = 10;
return availableSize;
}
So, after much hunting this morning, I found a workaround. If this gives insight and someone else finds a solution, I'll still mark yours as the accepted solution, because this just seems so hacky to me.
Basically, this morning, I pulled out the proverbial bludgeon, and decided to see what would happen if I set the MaxHeight to some value (100), and see how it would render then. It turns out, it scaled the control properly, and there was no clipping in sight! So, I figured I would try databinding the MaxHeight property to the window's height in the XAML. This didn't work, so I tried doing it from the code behind, and still had no luck. So, I went back to verify the phenomenon, and set the MaxHeight to 100 again, and ran the program. It resized to the size of the window, which was odd since hadn't I just set it to 100? Then I realized it was setting it to 100, and then the codebehind was overriding that value allowing it to scale upwards. Resizing the window even had the desired no-clipping effect... right up until you got to 100 height. So, I set the MaxHeight to zero, and that apparently goes inside the control and breaks some psuedo-minimum-height property, and then the window allows it to expand up to it's full size.
It's extremely hacky, but it works for now. =/ It seems so odd to me that I enabled the shrinking of a control by setting the MaxHeight property. ><
精彩评论