문제

Book이라는 클래스가 있습니다.

class Book
{
    public string Name { get; set; }
    public string Author { get; set; }
    public int PagesCount { get; set; }
    public int Category { get; set; }
}

ListBox에는 도서 목록이 표시되며, ItemTemplate은 도서를 시각적으로 표현할 수 있도록 수정되었습니다.텍스트에는 책 이름, 저자 및 페이지 수가 표시됩니다.그러나 범주는 특정 색상(예: 역사는 파란색, 로맨스는 빨간색 등)으로 표시됩니다. 이제 텍스트에는 OuterGlowBitmap 효과와 범주(int)에서 적절한 색상으로의 값 변환기가 있습니다.모든 것은 ListBoxItem의 DataTemplate에 바인딩됩니다.기술적으로 모든 것이 잘 작동합니다.

다만 문제는 성능이다.OuterGlow 비트맵 효과가 프로세서에 부담을 주는 것 같아서 약 500권의 책 목록이 있을 때 데이터베이스에서 데이터를 검색하는 데 약 500ms가 걸리지만 실제로 항목을 ListBox에 로드하는 데는 약 10초가 걸립니다.그리고 로딩이 완료되더라도 스크롤이 매우 느립니다.VirtualizingStackPanel.IsVirtualizing을 True로 설정하려고 시도했지만 아무 소용이 없습니다.(특정 시간에 데이터베이스에 포함될 수 있는 최대 도서 수는 약 30000권입니다.)

그러나 목록 상자에 100개가 넘는 항목이 있어도 인간의 마음은 그렇게 빨리 처리할 수 없기 때문에 검색된 모든 책을 사용자에게 로드하여 나열하는 것을 목표로 하지 않습니다.이것이 바로 목록 상자를 ObservableCollection 개체에 실제로 바인딩하는 래퍼 탐색 클래스 BookNavigator를 만든 이유입니다.모든 책은 이 BookNavigator에 로드되지만 그 중 X개만 목록 상자에 표시됩니다(observableCollection에 추가하여).

이것의 문제는 목록 상자가 스크롤 막대를 표시하지 않을 만큼 표시되는 책의 수가 작아서 내 자신의 스크롤 방법(첫 번째, 이전, 다음, 마지막 또는 단순히 내 스크롤 막대가 표시되지 않음)을 구현할 수 있다는 것입니다. 상관없어).

스크롤바가 표시되지 않도록 표시할 항목 수를 어떻게 계산합니까?

나타나는 두 가지 문제:- 응용 프로그램 크기를 조정하면 목록 상자의 크기가 변경될 수 있습니다. - 모든 목록 상자 항목의 높이가 같지는 않습니다(작성자 수에 따라 다름).

내가하려는 일을 달성 할 수있는 방법이 있습니까?


편집 (Martin Harris에 대한 답변)

Martin Harris가 제안한 코드의 문제점은 foreach 루프가 FrameworkElement를 사용하지만 목록 상자가 FrameworkElement에서 상속되지 않은 Book 유형의 개체로 채워지거나 높이를 계산하는 다른 방법이 없다는 것입니다.ListBoxItem의 루트 요소는 그리드이므로 이 그리드를 검색하는 것이 가능할 수 있지만 어떻게 해야 할지 모르겠습니다.

목록 상자 항목을 나타내기 위해 생성된 실제 UI 요소를 가져올 수 있는 방법이 있습니까?


편집하다

나에게 꼭 필요한 Q/A를 발견했습니다..ItemContainerGenerator

도움이 되었습니까?

해결책 4

해결책은 여기에서 암시적으로 찾을 수 있습니다.해결책

다른 팁

비슷한 것을 알아내려고 노력한 후에 결과를 여기에 공유하겠다고 생각했습니다(다른 응답보다 쉬워 보이기 때문에).

제가 받은 간단한 가시성 테스트 여기.

private static bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible)
        return false;

    Rect bounds =
        element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    var rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

그런 다음 목록 상자 항목을 반복하고 해당 테스트를 사용하여 표시되는 항목을 확인할 수 있습니다.이 목록의 개수는 목록 상자에 표시되는 항목의 수를 제공합니다.

private List<object> GetVisibleItemsFromListbox(ListBox listBox, FrameworkElement parentToTestVisibility)
{
    var items = new List<object>();

    foreach (var item in PhotosListBox.Items)
    {
        if (IsUserVisible((ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item), parentToTestVisibility))
        {
            items.Add(item);
        }
        else if (items.Any())
        {
            break;
        }
    }

    return items;
}

그러면 현재 목록 상자에 표시된 항목 목록이 포함됩니다(스크롤 또는 이와 유사한 항목으로 숨겨진 항목 포함).

아마 그럴까요? ~할 수 있었다 목록 상자에 책을 추가하기 전에 모든 책의 크기를 파악합니다(아마도 뒤에 있는 코드에서 템플릿 XAML을 구문 분석하고 채운 다음 외부 컨트롤에 크기를 요청하여). 이는 별 이득이 없는 많은 작업이 될 것입니다.목록 상자를 채우기에 충분하지만 렌더링 속도를 늦추고 스크롤 막대를 끌 만큼 많은 책을 선택할 수는 없습니까?이는 목록 상자의 크기와 연결되어 목록 상자가 커짐에 따라 더 많은 항목이 추가될 수 있지만 몇 가지 추가 항목을 추가하는 데 드는 성능 비용은 모든 크기를 다시 계산하는 데 드는 비용보다 낮아야 합니다.

빠른 예:저자가 한 명인 책의 크기가 150픽셀이라고 가정해 보겠습니다.목록 상자의 크기를 125로 나누어 야구장에 들어갈 항목 수를 대략적으로 추정할 수 있지만 계산하는 데 비용이 많이 들지는 않습니다.피하고 싶은 것은 항목이 너무 적으면 빈 공간이 남게 되기 때문입니다.


귀하의 의견을 반영하여 수정되었습니다.

이 경우 목록 상자에 항목을 추가한 다음(위의 방법을 사용하여 대략적인 추측을 얻음) 완전히 표시되는 마지막 항목이 무엇인지 계산한 다음 추가 항목을 제거할 수 있습니다.

이 확장 메서드는 목록 상자에 완전히 표시되는 마지막 요소를 가져오거나 항목이 표시되지 않는 경우 null을 가져옵니다.

public static class ListBoxExtensions
{
    public static FrameworkElement GetLastItem(this ListBox listBox)
    {
        double height = listBox.ActualHeight;

        double currentHeight = 0;
        FrameworkElement previous = null;

        foreach (FrameworkElement item in listBox.Items)
        {
            currentHeight += item.ActualHeight;
            if (currentHeight > height)
            {
                return previous;
            }

            previous = item;
        }

        return previous;
    }
}

목록이 더 커지면 컬렉션에 공백을 메울 만큼 충분한 항목을 추가하고 프로세스를 다시 실행할 수 있습니다.

"스크롤바가 표시되지 않도록 표시할 항목 수를 어떻게 계산할 수 있나요?"

짧은 답변:(listBox1.Height/listBox1.ItemHeight)

이는 표시/사용 가능한 줄 수를 제공하므로 이 줄 수만 읽을 수 있고 모든 목록 상자를 채울 수 있습니다.

이제 데모는 다음과 같습니다.

        int listbox_total_visible_lines = 2;
        listBox1.Height = (listbox_total_visible_lines + 1) * listBox1.ItemHeight;
        listBox1.Items.Add("um");
        listBox1.Items.Add("dois");
        listBox1.Items.Add("tres");
        listBox1.Items.Add("quatro");
        listBox1.Items.Add("cinco");
        listBox1.SelectedIndex = listBox1.Items.Count - 1;
        this.Text = (listBox1.Height/ listBox1.ItemHeight).ToString();

이 예에서는 표시할 항목 수를 선택할 수 있으므로 실제로 표시할 수 있는 OS 라인 수가 됩니다.따라서 "listbox_total_visible_items" 항목만 추가하면 목록 상자가 가득 차고 스크롤 막대가 표시되지 않습니다.

코드 설명:

  1. listbox_total_visible_items에는 표시할 줄 수가 포함되어 있습니다.

  2. 적절한 크기의 목록 상자 설정, 2줄만

3-7.몇 줄 추가

  1. 그냥 재미로 마지막 줄로 가세요

  2. 목록 상자 높이를 각 항목의 크기로 나눈 값을 기준으로 목록 상자 줄 수를 양식 텍스트 표시줄에 표시합니다.

그게 다야.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top