2011-10-27 41 views
0

我最近遇到了一個很奇怪的問題。如何在Windows Phone7中完全手動控制ListBoxItem的風格?

我想控制ListBoxItem自己渲染一個非常複雜的ListBox, 當我完成工作時,我發現第一個外觀正是我想要的,但是當我向下滾動ListBox時,我很困惑,沮喪地看到ListBoxItem的順序根本就沒有順序。

現在,我創建了一個新的空similiar項目和測試,結果是相同的(在Windows Phone 7的是,但在WPF應用程序的一切都很好。)

V1:

<ListBox x:Name="ListBoxTest"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel> 
         <TextBlock Text="{Binding Channel}" Width="1000"></TextBlock> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

V1只是將財產約束爲特殊控制,這樣一切都很好。 和東西V2是不同的:

<ListBox x:Name="ListBoxTest"> 
<ListBox.ItemTemplate> 
    <DataTemplate> 
     <local:TestPanel Data="{Binding}" HoldedHeight="100"></local:TestPanel> 
    </DataTemplate> 
</ListBox.ItemTemplate> 

和パ中的DataTemplate是:

public class TestPanel : StackPanel 
{ 
    private bool _beginLoaded; 
    private bool _isLoaded; 
    private ListBox _parentListBox; 

    public TestPanel() 
     : base() 
    { 
     this.Loaded += TestPanel_Loaded; 
    } 

    void TestPanel_Loaded(object sender, RoutedEventArgs e) 
    { 
     if (Data != null) 
     { 
      System.Diagnostics.Debug.WriteLine("ProgramsPanel_Loaded with {0}", Data.Channel); 
     } 
     else 
     { 
      System.Diagnostics.Debug.WriteLine("ProgramsPanel_Loaded with NULL data"); 
     } 

     if (_beginLoaded || _isLoaded) 
      return; 

     _beginLoaded = true; 
     InitializeWidthParent(); 
     this.Children.Clear(); 
     Show(Data, HoldedWidth, HoldedHeight); 
     _isLoaded = true; 
    } 




    private void InitializeWidthParent() 
    { 
     _parentListBox = FindParent<listbox>(this); 
     if (_parentListBox != null) 
     { 
      HoldedWidth = _parentListBox.ActualWidth;    
     } 
    } 

    static T FindParent<t>(DependencyObject d) where T : DependencyObject 
    { 
     DependencyObject parent = d; 
     while (parent != null) 
     { 
      parent = VisualTreeHelper.GetParent(parent); 
      if (parent != null && (parent.GetType() == typeof(T))) 
      { 
       return parent as T; 
      } 
     } 
     return parent as T; 
    } 

    private void Show(TestDataItem data, double holdedwidth, double holdedheight) 
    { 

     if (data == null) 
      return; 

     if (!holdedwidth.IsValid() || !holdedheight.IsValid()) 
      return; 

     #region Stand at Left Side (Channel Name and Image) 
        System.Diagnostics.Debug.WriteLine("Begin Show this Pannel of {0}", data.Channel); 

     TextBlock tb = new TextBlock() { FontSize = 32, Height=HoldedHeight,Text = data.Channel, HorizontalAlignment = HorizontalAlignment.Center }; 


     this.Children.Add(tb); 
     #endregion 

     #region Right Side 


     #endregion 
    } 

    public double HoldedWidth 
    { 
     get; 
     set; 
    } 

    public double HoldedHeight 
    { 
     get; 
     set; 
    } 



    /// <summary> 
    /// The <see cref="Data" /> dependency property's name. 
    /// </summary> 
    public const string DataPropertyName = "Data"; 

    /// <summary> 
    /// Gets or sets the value of the <see cref="Data" /> 
    /// property. This is a dependency property. 
    /// </summary> 
    public TestDataItem Data 
    { 
     get 
     { 
      return (TestDataItem)GetValue(DataProperty); 
     } 
     set 
     { 
      SetValue(DataProperty, value); 
     } 
    } 

    /// <summary> 
    /// Identifies the <see cref="Data" /> dependency property. 
    /// </summary> 
    public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
     DataPropertyName, 
     typeof(TestDataItem), 
     typeof(TestPanel), 
     new PropertyMetadata(null, OnDataPropertyChanged)); 

    private static void OnDataPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     (d as TestPanel).OnDataPropertyChanged(e.OldValue as TestDataItem, e.NewValue as TestDataItem); 
    } 

    protected virtual void **OnDataPropertyChanged**(TestDataItem oldData, TestDataItem newData) 
    { 
     if (oldData != null) 
     { 
      Show(oldData, HoldedWidth, HoldedHeight); 
      System.Diagnostics.Debug.WriteLine("rebuild old pannel of {0}, ListBox count is {1}", oldData.Channel, _parentListBox.Items.Count); 
     } 

     string oldstr = oldData == null ? "NULL" : oldData.Channel; 
     string newstr = newData == null ? "NULL" : newData.Channel; 

     System.Diagnostics.Debug.WriteLine("old data is {0}, new data is {1}", oldstr, newstr); 
    } 
} 


public static partial class Extentsions 
{ 
    public static bool IsValid(this double width) 
    { 
     if (double.IsNaN(width) 
      || Math.Abs(width) < 0.1) 
     { 
      return false; 
     } 

     return true; 
    } 
} 


public class TestDataItem 
{ 
    public string Channel 
    { 
     get; 
     set; 
    } 

} 

主要就是頁面:

public partial class MainPage : PhoneApplicationPage 
{ 
    // Constructor 
    public MainPage() 
    { 
     InitializeComponent(); 

     this.Loaded += new RoutedEventHandler(MainPage_Loaded);   
    } 

    void MainPage_Loaded(object sender, RoutedEventArgs e) 
    { 
     ObservableCollection<testdataitem> source = new ObservableCollection<testdataitem>(); 
     ListBoxTest.ItemsSource = source; 

     for (int i = 0; i < 10; i++) 
     { 
      source.Add(new TestDataItem() { Channel = i.ToString() }); 
     } 
    } 
} 

爲什麼我使用後V2 ListBoxItems會無序出現,我該如何解決這個問題?

PS:輸出信息是:

old data is NULL, new data is 0 

old data is NULL, new data is 1 

old data is NULL, new data is 2 

old data is NULL, new data is 3 

old data is NULL, new data is 4 

old data is NULL, new data is 5 

old data is NULL, new data is 6 

old data is NULL, new data is 7 

old data is NULL, new data is 8 

old data is NULL, new data is 9 

ProgramsPanel_Loaded with 0 

Begin Show this Pannel of 0 

ProgramsPanel_Loaded with 1 

Begin Show this Pannel of 1 

ProgramsPanel_Loaded with 2 

Begin Show this Pannel of 2 

ProgramsPanel_Loaded with 3 

Begin Show this Pannel of 3 

ProgramsPanel_Loaded with 4 

Begin Show this Pannel of 4 

ProgramsPanel_Loaded with 5 

Begin Show this Pannel of 5 

ProgramsPanel_Loaded with 6 

Begin Show this Pannel of 6 

ProgramsPanel_Loaded with 7 

Begin Show this Pannel of 7 

ProgramsPanel_Loaded with 8 

Begin Show this Pannel of 8 

ProgramsPanel_Loaded with 9 

Begin Show this Pannel of 9 

Begin Show this Pannel of 9 

rebuild old pannel of 9, ListBox count is 10 

old data is 9, new data is 0 

ProgramsPanel_Loaded with 8 

感謝, 閃亮

回答

0

我想這個問題是通過列表框中的 「功能」 引起的:UI Virtualization
簡而言之,UI虛擬化將幫助您減少UI內存,並且僅將元素呈現在屏幕上。
因此,當您滾動列表框時,您的自定義測試面板將被回收並重新使用。
可能是我沒有清除,但您可以嘗試通過使用stackpanel作爲ListBox的ItemPanel來禁用列表框的UI虛擬化功能,以查看您的問題是否已解決。

您可以定義列表框的ItemsPanelTemplate這樣的:

<ListBox> 
    <ListBox.ItemsPanel> 
    <ItemsPanelTemplate> 
     <StackPanel/> 
    </ItemsPanelTemplate> 
    </ListBox.ItemsPanel> 
</ListBox> 

您也可以一個propertychangedCallback添加到您的數據屬性,並在此回調重建面板元件。這將在您的面板被回收並且其數據被更改時調用。

/// <summary> 
/// Identifies the <see cref="Data" /> dependency property. 
/// </summary> 
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
    DataPropertyName, 
    typeof(TestDataItem), 
    typeof(TestPanel), 
    new PropertyMetadata(null, OnIsDataChanged)); 

protected static void OnIsDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     //rebuild your panel here...... 

     this.Children.Clear(); 
     Show(Data, HoldedWidth, HoldedHeight); 
    } 
+0

是的,重新定義列表框的ItemsPanel可以解決這個問題,但會造成性能負擔。爲什麼V1的XAML沒有這個問題,V2的XAML有。兩者都具有UI虛擬化功能 – Sparkling

+0

如果您的列表包含大量項目,那麼會導致性能問題。你也可以對自定義測試面板進行一些修改,爲你的DataProperty添加一個propertyChangedCallback,當你的面板被回收時,數據也會改變,你可以在回調中重建你的面板 – yiyang

+0

我在上面的代碼中添加回調,但是它似乎列表框的外觀仍然無序。 – Sparkling