2009-12-10 21 views
9

समस्याWPF ListBox वर्चुअलाइजेशन शिकंजा अप आइटम

हम कुशलतापूर्वक एक WPF ListBox नियंत्रण में वस्तुओं की एक बड़ी (> 1000) संख्या को प्रदर्शित करने की आवश्यकता है का प्रदर्शन किया। हम इन वस्तुओं को कुशलता से प्रदर्शित करने के लिए WPF ListBox के वर्चुअलाइजेशन (वर्चुअलाइजिंगस्टैक पैनेल के माध्यम से) पर भरोसा कर रहे हैं।

बग: वर्चुअलाइजेशन का उपयोग करते समय WPF ListBox नियंत्रण आइटम को सही तरीके से प्रदर्शित नहीं करता है।

पुन कैसे

हम नीचे दिखाया गया स्टैंडअलोन XAML के लिए समस्या आसुत है।

xaml को XAMLPad में कॉपी और पेस्ट करें।

प्रारंभ में, सूची बॉक्स में कोई चयनित आइटम नहीं है, इसलिए उम्मीद है कि सभी आइटम एक ही आकार के हैं और वे पूरी तरह से उपलब्ध स्थान को भरते हैं।

अब, पहले आइटम पर क्लिक करें। जैसा कि अपेक्षित है, हमारे डेटा टेम्पलेट की वजह से, चयनित आइटम अतिरिक्त जानकारी दिखाने के लिए विस्तारित होगा।

जैसा कि अपेक्षित है, यह क्षैतिज स्क्रॉलबार प्रकट होने का कारण बनता है, क्योंकि चयनित आइटम अब उपलब्ध स्थान से अधिक व्यापक है।

अब क्षैतिज स्क्रॉलबार को दाईं ओर क्लिक करने और खींचने के लिए माउस का उपयोग करें।

बग: गैर-चयनित दृश्य आइटम अब उपलब्ध स्थान को भरने के लिए नहीं फैले हैं। सभी दृश्यमान वस्तुओं को एक ही चौड़ाई होना चाहिए।

क्या यह ज्ञात बग है? क्या यह ठीक करने के लिए कोई तरीका है, या तो XAML या प्रोग्रामेटिक रूप से?


<Page 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:sys="clr-namespace:System;assembly=mscorlib" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > 
    <Page.Resources> 

     <DataTemplate x:Key="MyGroupItemTemplate"> 
      <Border Background="White" 
        TextElement.Foreground="Black" 
        BorderThickness="1" 
        BorderBrush="Black" 
        CornerRadius="10,10,10,10" 
        Cursor="Hand" 
        Padding="5,5,5,5" 
        Margin="2" 
        > 
       <StackPanel> 
        <TextBlock Text="{Binding Path=Text, FallbackValue=[Content]}" /> 
        <TextBlock x:Name="_details" Visibility="Collapsed" Margin="0,10,0,10" Text="[xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]" /> 
       </StackPanel> 
      </Border> 
      <DataTemplate.Triggers> 
       <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" 
          Value="True"> 
        <Setter Property="TextElement.FontWeight" 
          TargetName="_details" 
          Value="Bold"/> 
        <Setter Property="Visibility" 
          TargetName="_details" 
          Value="Visible"/> 
       </DataTrigger> 
      </DataTemplate.Triggers> 
     </DataTemplate> 

    </Page.Resources> 

    <DockPanel x:Name="LayoutRoot"> 

     <Slider x:Name="_slider" 
       DockPanel.Dock="Bottom" 
       Value="{Binding FontSize, ElementName=_list, Mode=TwoWay}" 
       Maximum="100" 
       ToolTip="Font Size" 
       AutoToolTipPlacement="BottomRight"/> 

     <!-- 
      I want the items in this ListBox to completly fill the available space. 
      Therefore, I set HorizontalContentAlignment="Stretch". 

      By default, the WPF ListBox control uses a VirtualizingStackPanel. 
      This makes it possible to view large numbers of items efficiently. 
      You can turn on/off this feature by setting the ScrollViewer.CanContentScroll to "True"/"False". 

      Bug: when virtualization is enabled (ScrollViewer.CanContentScroll="True"), the unselected 
       ListBox items will no longer stretch to fill the available horizontal space. 
       The only workaround is to disable virtualization (ScrollViewer.CanContentScroll="False"). 
     --> 

     <ListBox x:Name="_list" 
       ScrollViewer.CanContentScroll="True" 
       Background="Gray" 
       Foreground="White" 
       IsSynchronizedWithCurrentItem="True" 
       TextElement.FontSize="28" 
       HorizontalContentAlignment="Stretch" 
       ItemTemplate="{DynamicResource MyGroupItemTemplate}"> 
      <TextBlock Text="[1] This is item 1." /> 
      <TextBlock Text="[2] This is item 2." /> 
      <TextBlock Text="[3] This is item 3." /> 
      <TextBlock Text="[4] This is item 4." /> 
      <TextBlock Text="[5] This is item 5." /> 
      <TextBlock Text="[6] This is item 6." /> 
      <TextBlock Text="[7] This is item 7." /> 
      <TextBlock Text="[8] This is item 8." /> 
      <TextBlock Text="[9] This is item 9." /> 
      <TextBlock Text="[10] This is item 10." /> 
     </ListBox> 

    </DockPanel> 
</Page> 
+0

धन्यवाद विल! अधिक जानकारी के लिए नीचे "उत्तर" देखें। –

उत्तर

3

मैं अधिक समय बिताया से मैं शायद होना चाहिए इस प्रयास कर, और यह काम करने के लिए नहीं मिल सका। मैं समझता हूं कि यहां क्या हो रहा है, लेकिन शुद्ध एक्सएएमएल में, मुझे इस समस्या को हल करने के तरीके को समझने में परेशानी हो रही है। मुझे लगता है कि मैं समस्या को हल करने का तरीका देखता हूं, लेकिन इसमें एक कनवर्टर शामिल है।

चेतावनी: चीजें जटिल होने जा रही हैं क्योंकि मैं अपने निष्कर्षों को समझाता हूं।

अंतर्निहित समस्या इस तथ्य से आती है कि नियंत्रण की चौड़ाई उनके कंटेनर की चौड़ाई तक फैली हुई है। वर्चुअलाइजेशन सक्षम होने पर, चौड़ाई नहीं बदलेगी। ListBox के अंदर अंतर्निहित ViewportWidth संपत्ति आपके द्वारा देखी जाने वाली चौड़ाई से मेल खाती है। जब एक और नियंत्रण आगे फैलता है (आप इसे चुनते हैं), ViewportWidth अभी भी वही है, लेकिन ExtentWidth पूर्ण चौड़ाई दिखाता है। ExtentWidth के सभी नियंत्रणों की चौड़ाई को बाध्य करना चाहिए ...

लेकिन ऐसा नहीं है। मैंने अपने मामले में त्वरित परीक्षण के लिए फ़ॉन्ट आकार 100 पर सेट किया है। जब कोई आइटम चुना जाता है, ExtentWidth="4109.13। पेड़ को अपने कंट्रोल टेम्पलेट के Border पर जाकर, मुझे ActualWidth="4107.13" दिखाई देता है। 2 पिक्सेल अंतर क्यों?ListBoxItem में 2 पिक्सेल पैडिंग वाला एक सीमा है, जिससे सामग्री प्रस्तुतकर्ता थोड़ा छोटा प्रस्तुत करता है।

मैं help from here साथ निम्नलिखित Style जोड़ा मुझसे सीधे ExtentWidth का उपयोग करने की अनुमति देने के लिए:

<Style x:Key="{x:Type ListBox}" TargetType="ListBox"> 
    <Setter Property="Template"> 
    <Setter.Value> 
     <ControlTemplate TargetType="ListBox"> 
     <Border 
      Name="Border" 
      Background="White" 
      BorderBrush="Black" 
      BorderThickness="1" 
      CornerRadius="2"> 
      <ScrollViewer 
      Name="scrollViewer" 
      Margin="0" 
      Focusable="false"> 
      <StackPanel IsItemsHost="True" /> 
      </ScrollViewer> 
     </Border> 
     <ControlTemplate.Triggers> 
      <Trigger Property="IsEnabled" Value="false"> 
      <Setter TargetName="Border" Property="Background" 
        Value="White" /> 
      <Setter TargetName="Border" Property="BorderBrush" 
        Value="Black" /> 
      </Trigger> 
      <Trigger Property="IsGrouping" Value="true"> 
      <Setter Property="ScrollViewer.CanContentScroll" Value="false"/> 
      </Trigger> 
     </ControlTemplate.Triggers> 
     </ControlTemplate> 
    </Setter.Value> 
    </Setter> 
</Style> 

नोट मैं इस उद्देश्य के लिए ScrollViewer के लिए एक नाम जोड़ दिया।

फिर, मैं ExtentWidth करने के लिए अपने सीमा की चौड़ाई के लिए बाध्य करने का प्रयास किया:

Width="{Binding ElementName=scrollViewer, Path=ExtentWidth}" 

हालांकि, क्योंकि है कि 2 पिक्सेल की पैडिंग की, नियंत्रण अनंत लूप में आकार बदलेंगे, गद्दी 2 पिक्सेल को जोड़ने के साथ ExtentWidth पर, जो सीमा चौड़ाई का आकार बदलता है, जो ExtentWidth, आदि तक 2 और पिक्सल जोड़ता है जब तक कि आप कोड हटाएं और रीफ्रेश न करें।

यदि आपने एक्स्टेंटविड्थ से 2 घटाए गए कनवर्टर को जोड़ा है, तो मुझे लगता है कि यह काम कर सकता है। हालांकि, जब स्क्रॉल बार मौजूद नहीं है (आपने कुछ भी नहीं चुना है), ExtentWidth="0"। इस प्रकार, Width के बजाय MinWidth के लिए बाध्य काम कर सकते हैं बेहतर तो आइटम दिखाई देते हैं सही ढंग से जब कोई स्क्रॉल पट्टी दिखाई दे रहा है: यदि आप सीधे ListBoxItem ही की MinWidth DataBind सकता

MinWidth="{Binding ElementName=scrollViewer, Path=ExtentWidth, Converter={StaticResource PaddingSubtractor}}" 

एक बेहतर समाधान होगा। आप सीधे एक्स्टेंटविड्थ से बांध सकते हैं, और कोई कनवर्टर आवश्यक नहीं होगा। हालांकि मुझे नहीं पता कि उस आइटम तक पहुंच कैसे प्राप्त करें।

संपादित करें: संगठन के लिए, यहां क्लिप करने के लिए आवश्यक क्लिप है। सब कुछ अनावश्यक बनाता है:

<Style TargetType="{x:Type ListBoxItem}"> 
    <Setter Property="MinWidth" Value="{Binding Path=ExtentWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" /> 
</Style> 
+0

"क्या यह एक बग है?" के जवाब में, मुझे कोई जानकारी नहीं है, लेकिन वर्चुअलाइजेशन के लिए अंतर्निहित समर्थन पर विचार करते हुए, मुझे उम्मीद है कि यह है, खासकर अगर इस से बेहतर समाधान नहीं है I साथ आ रहा था –

2

विल के महान विश्लेषण के लिए धन्यवाद!

विल के सुझाव के आधार पर: "एक बेहतर समाधान हो सकता है अगर आप सीधे ListBoxItem ही ... लेकिन मैं पता नहीं कैसे है कि आइटम तक पहुँच प्राप्त करने के लिए है की MinWidth DataBind सकता है", मैं कर रहा था शुद्ध XAML का उपयोग कर कि लागू करने के लिए, इस प्रकार है:

<ListBox x:Name="_list" 
     Background="Gray" 
     Foreground="White" 
     IsSynchronizedWithCurrentItem="True" 
     TextElement.FontSize="28" 
     HorizontalContentAlignment="Stretch" 
     ItemTemplate="{DynamicResource MyGroupItemTemplate}"> 

    <!-- Here is Will's suggestion, implemented in pure xaml. Seems to work. 
     Next problem is if you drag the Slider to the right to increase the FontSize. 
     This will make the horizontal scroll bar appear, as expected. 
     Problem: the horizontal scroll bar never goes away if you drag the Slider to the left to reduce the FontSize. 
    --> 
    <ListBox.Resources> 
     <Style TargetType="{x:Type ListBoxItem}"> 
      <Setter Property="MinWidth" Value="{Binding Path=ExtentWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}" /> 
     </Style> 
    </ListBox.Resources> 

    <TextBlock Text="[1] This is item 1." /> 
    <TextBlock Text="[2] This is item 2." /> 
    <TextBlock Text="[3] This is item 3." /> 
    <TextBlock Text="[4] This is item 4." /> 
    <TextBlock Text="[5] This is item 5." /> 
    <TextBlock Text="[6] This is item 6." /> 
    <TextBlock Text="[7] This is item 7." /> 
    <TextBlock Text="[8] This is item 8." /> 
    <TextBlock Text="[9] This is item 9." /> 
    <TextBlock Text="[10] This is item 10." /> 
</ListBox> 

मैं एडम नाथन के महान किताब से विचार आया, "विंडोज प्रस्तुति फाउंडेशन Unleashed"।

तो, यह मूल समस्या को ठीक करने लगता है।

नई समस्या

आप XAML में एक स्लाइडर नियंत्रण है कि आपको वृद्धि/ListBox फॉन्ट में कमी की है कि वहाँ ध्यान दें। यहां विचार यह था कि उपयोगकर्ता को आसानी से दृश्यता के लिए सूची बॉक्स सामग्री को ऊपर या नीचे स्केल करने की क्षमता प्रदान करने की अनुमति दी गई थी।

यदि आप पहले स्लाइडर को फ़ॉन्टसाइज़ बढ़ाने के लिए दाएं पर खींचें, तो यह अपेक्षित के रूप में क्षैतिज स्क्रॉल बार दिखाई देगा।नई समस्या यह है कि यदि आप FontSize को कम करने के लिए स्लाइडर को बाईं ओर खींचते हैं तो क्षैतिज स्क्रॉल बार कभी नहीं चला जाता है।

कोई विचार?

+0

आप एक नए प्रश्न पूछने से बेहतर होंगे, क्योंकि आपको इस बिंदु पर कई विचार नहीं मिलेगा। मैं बाद में इस पर एक नज़र डाल सकता हूं क्योंकि मुझे मूल मुद्दा दिलचस्प लगता है। इसके अलावा, अगर मेरी प्रतिक्रिया सहायक थी तो एक उत्थान या स्वीकार्य उत्तर की सराहना की जाएगी :) –

+0

इसके बारे में क्षमा करें ... मैं इस मंच अवधारणा के लिए नया हूं, इसलिए बस यह पता लगाया कि वास्तव में पंजीकरण कैसे करें, आदि –

+0

जाहिर है, यह है एक wpf बग, और .NET 4.0 में तय किया गया है। –

संबंधित मुद्दे