Tôi đang cố gắng để viết một lớp tùy chỉnh Panel
cho WPF, bằng cách ghi đè MeasureOverride
và ArrangeOverride
nhưng, trong khi đó hầu hết là làm việc Tôi đang gặp một vấn đề kỳ lạ tôi không thể giải thích.Một tùy chỉnh tự động kích thước WPF Bảng điều chỉnh lớp
Cụ thể, sau khi tôi gọi Arrange
trên các mặt hàng con trong ArrangeOverride
sau khi xác định kích thước của chúng, chúng không định kích thước theo kích thước tôi cung cấp cho chúng và có kích thước Phương thức Measure
bên trong MeasureOverride
.
Tôi có thiếu thứ gì đó trong cách hệ thống này được yêu cầu hoạt động không? Sự hiểu biết của tôi là việc gọi số Measure
chỉ đơn giản là làm cho trẻ đánh giá DesiredSize
của nó dựa trên số liệu có sẵnSize và không nên ảnh hưởng đến kích thước cuối cùng thực tế của nó. Đây là mã đầy đủ của tôi (Panel, btw, được thiết kế để sắp xếp trẻ em theo cách hiệu quả nhất về mặt không gian, cho ít không gian hơn cho các hàng không cần nó và chia tách không gian còn lại lên một cách đồng đều giữa các phần còn lại-- nó hiện chỉ hỗ trợ định hướng dọc nhưng tôi có kế hoạch thêm chiều ngang khi tôi làm cho nó hoạt động bình thường):
Chỉnh sửa: Cảm ơn phản hồi. Tôi sẽ kiểm tra chúng chặt chẽ hơn trong giây lát. Tuy nhiên hãy để tôi làm rõ thuật toán dự định của tôi hoạt động như thế nào vì tôi không giải thích điều đó.
Trước hết, cách tốt nhất để nghĩ về những gì tôi đang làm là tưởng tượng một Grid với mỗi hàng được đặt thành *. Điều này chia không gian lên đồng đều. Tuy nhiên, trong một số trường hợp, phần tử trong một hàng có thể không cần tất cả không gian đó; nếu đây là trường hợp, tôi muốn lấy bất kỳ không gian còn sót lại nào và đưa nó cho những hàng có thể sử dụng không gian đó. Nếu không có hàng nào cần thêm bất kỳ không gian nào, tôi chỉ cố gắng để không gian mọi thứ đồng đều (đó là những gì extraSpace
đang làm, nó chỉ dành cho trường hợp đó).
Tôi thực hiện điều này theo hai lần. Điểm cuối cùng của lần vượt qua đầu tiên là xác định "kích thước bình thường" cuối cùng của một hàng - nghĩa là. kích thước của các hàng sẽ bị thu nhỏ (với kích thước nhỏ hơn kích thước mong muốn của nó). Tôi làm điều này bằng cách bước qua vật nhỏ nhất lớn nhất và điều chỉnh kích thước bình thường được tính toán ở từng bước, bằng cách thêm khoảng trống còn lại từ mỗi mục nhỏ vào từng mục lớn hơn tiếp theo cho đến khi không có thêm mục nào "vừa" và sau đó ngắt. Trong lần tiếp theo, tôi sử dụng giá trị thông thường này để xác định xem một món đồ có thể vừa hoặc không, đơn giản bằng cách lấy số Min
của kích thước bình thường với kích thước mong muốn của mặt hàng đó.
(Tôi cũng đã thay đổi phương pháp vô danh đến một hàm lambda cho đơn giản.)
Chỉnh sửa 2: thuật toán của tôi dường như làm việc tuyệt vời tại việc xác định kích thước thích hợp của trẻ em. Tuy nhiên, bọn trẻ không chấp nhận kích cỡ của chúng. Tôi đã cố gắng đề nghị của Goblin MeasureOverride
bằng cách chuyển PositiveInfinity và trả về Kích thước (0,0), nhưng điều này khiến trẻ tự vẽ như thể không có ràng buộc về không gian nào cả. Phần không rõ ràng về điều này là nó đang xảy ra vì một cuộc gọi đến Measure
. Tài liệu của Microsoft về chủ đề này không rõ ràng, vì tôi đã đọc qua từng lớp và mô tả đặc tính nhiều lần. Tuy nhiên, bây giờ rõ ràng là việc gọi Measure
thực tế ảnh hưởng đến kết xuất của đứa trẻ, vì vậy tôi sẽ cố gắng chia logic thành hai hàm như BladeWise đề xuất.
Đã giải quyết !! Tôi nhận được nó hoạt động.Như tôi nghi ngờ, tôi cần phải gọi Measure() hai lần cho mỗi đứa trẻ (một lần để đánh giá DesiredSize và một giây để cho mỗi đứa trẻ có chiều cao thích hợp). Nó có vẻ kỳ lạ với tôi rằng bố trí trong WPF sẽ được thiết kế theo cách kỳ lạ, nơi nó được chia thành hai lượt, nhưng vượt qua Measure thực sự làm hai điều: các biện pháp và kích cỡ trẻ em và vượt qua Arrange không bên cạnh gì ngoài thực sự thể chất vị trí của trẻ em. Rất kì lạ.
Tôi sẽ đăng mã hoạt động ở dưới cùng.
Đầu tiên, bản gốc (chia) mã:
protected override Size MeasureOverride(Size availableSize) {
foreach (UIElement child in Children)
child.Measure(availableSize);
return availableSize;
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) {
double extraSpace = 0.0;
var sortedChildren = Children.Cast<UIElement>().OrderBy<UIElement, double>(child=>child.DesiredSize.Height;);
double remainingSpace = finalSize.Height;
double normalSpace = 0.0;
int remainingChildren = Children.Count;
foreach (UIElement child in sortedChildren) {
normalSpace = remainingSpace/remainingChildren;
if (child.DesiredSize.Height < normalSpace) // if == there would be no point continuing as there would be no remaining space
remainingSpace -= child.DesiredSize.Height;
else {
remainingSpace = 0;
break;
}
remainingChildren--;
}
// this is only for cases where every child item fits (i.e. the above loop terminates normally):
extraSpace = remainingSpace/Children.Count;
double offset = 0.0;
foreach (UIElement child in Children) {
//child.Measure(new Size(finalSize.Width, normalSpace));
double value = Math.Min(child.DesiredSize.Height, normalSpace) + extraSpace;
child.Arrange(new Rect(0, offset, finalSize.Width, value));
offset += value;
}
return finalSize;
}
Và đây là đoạn code làm việc:
double _normalSpace = 0.0;
double _extraSpace = 0.0;
protected override Size MeasureOverride(Size availableSize) {
// first pass to evaluate DesiredSize given available size:
foreach (UIElement child in Children)
child.Measure(availableSize);
// now determine the "normal" size:
var sortedChildren = Children.Cast<UIElement>().OrderBy<UIElement, double>(child => child.DesiredSize.Height);
double remainingSpace = availableSize.Height;
int remainingChildren = Children.Count;
foreach (UIElement child in sortedChildren) {
_normalSpace = remainingSpace/remainingChildren;
if (child.DesiredSize.Height < _normalSpace) // if == there would be no point continuing as there would be no remaining space
remainingSpace -= child.DesiredSize.Height;
else {
remainingSpace = 0;
break;
}
remainingChildren--;
}
// there will be extra space if every child fits and the above loop terminates normally:
_extraSpace = remainingSpace/Children.Count; // divide the remaining space up evenly among all children
// second pass to give each child its proper available size:
foreach (UIElement child in Children)
child.Measure(new Size(availableSize.Width, _normalSpace));
return availableSize;
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize) {
double offset = 0.0;
foreach (UIElement child in Children) {
double value = Math.Min(child.DesiredSize.Height, _normalSpace) + _extraSpace;
child.Arrange(new Rect(0, offset, finalSize.Width, value));
offset += value;
}
return finalSize;
}
Nó có thể không được siêu hiệu quả gì với việc phải gọi Measure
hai lần (và lặp lại Children
ba lần), nhưng nó hoạt động. Mọi sự tối ưu hóa cho thuật toán sẽ được đánh giá cao.
Một điều nữa: Tôi không lo lắng về một ngoại lệ từ trở về 'availableSize' trong 'MeasureOverride' vì bảng điều khiển làm cho không có ý nghĩa bên trong một vô hạn không gian như một 'ScrollViewer'. Nó chỉ có ý nghĩa khi không gian bị giới hạn. – devios1
Tôi cũng gặp vấn đề này. Thay vào đó, mọi thay đổi về kích thước trong ArrangeOverride chỉ là clip. Không phải những gì tôi mong đợi từ việc đọc tài liệu hoặc bất kỳ cuốn sách WPF nào. Ngoài ra, còn phải gọi đến Measure hai lần để giải quyết vấn đề này. Cảm ơn. –