好的,所以在我即將放棄並以某種方式學習如何忍受這個bug後,我碰到了一個帖子(我現在似乎找不到),這表明TreeView確實支持基於像素的滾動(AKA物理滾動)而不關閉可視化。
所以我試了這個,確實 - 它的工作原理!確保驗證虛擬化是否有效,並測試了大約1000個項目,並在我的控件構造函數中設置了一個斷點,並確保在我的視圖滾動時調用它。
使用TreeView而不是ListBox的唯一缺點是TreeView似乎不支持多項選擇(我需要) - 但實現此方法比爲ListBox實現智能滾動要容易得多。
我創建了樹型視圖樣式,使樹型視圖的外觀和行爲就像一個ListBoxItem,這真的不是強制性的 - 但我更喜歡像這樣(的基本風格具有拉伸的問題,我曾與修復的事實旁造型)。基本上,我刪除了ItemsPresenter,只與ContentPresenter住,因爲我的數據是不分層:
<Style x:Key="MyTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Border Name="myBorder"
SnapsToDevicePixels="true"
CornerRadius="0,0,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
BorderBrush="Transparent"
Height="Auto"
Margin="1,1,1,3"
Background="Transparent">
<ContentPresenter Grid.Column="1" x:Name="PART_Header" HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
現在 - 我得剩下要做的唯一事情就是實現多選樹視圖。 可能有不同的方法來實現這種行爲,我採取了ViewModel方法。
從TreeView的派生我創建了一個新的MultiSelectionTreeView:
public class MultiSelectionTreeView : TreeView
{
private static bool CtrlPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftCtrl);
}
}
protected override void OnSelectedItemChanged(RoutedPropertyChangedEventArgs<object> e)
{
base.OnSelectedItemChanged(e);
var previouseItemViewModel = e.OldValue as IMultiSelectionTreeViewItemViewModel;
if (previouseItemViewModel != null)
{
if (!CtrlPressed)
previouseItemViewModel.IsSelected = false;
}
var newItemViewModel = e.NewValue as IMultiSelectionTreeViewItemViewModel;
if (newItemViewModel != null)
{
if (!CtrlPressed)
newItemViewModel.ClearSelectedSiblings();
newItemViewModel.IsSelected = true;
}
}
}
凡IMultiSelectionTreeViewItemViewModel如下:
public interface IMultiSelectionTreeViewItemViewModel
{
bool IsSelected { get; set; }
void ClearSelectedSiblings();
}
當然 - 現在是我的責任,處理的方式被呈現選定項目 - 在我的情況下,它是由於我的樹視圖項目有自己的DataTemplate,它有選擇的跡象。 如果這不是你的情況,並且你需要它,只需根據其視圖模型IsSelected屬性擴展樹視圖項目數據模板以指示其選擇狀態。
希望這將有助於某人某天:-) 玩得開心!
吉利
這將是荒謬的因爲我花了這麼多時間,讓我的系統面臨這樣的風險。我們正在開發系統而不是控制。簡單地將樹視圖調整爲這一點更容易。我永遠不會說永遠不會,如果沒有一個簡單的解決方案 - 我們作爲開發人員需要做必要的事情,但因爲這裏有更好的選擇 - 我不明白爲什麼要冒險。 – Gilad 2012-04-16 11:28:02
BTW:這是一個錯誤。微軟應該照顧這一點。在基於像素的滾動和虛擬化之間不存在任何衝突。證明這一點的是,它在TreeView中工作得很好,由於異端結構他們沒有選擇。有人在那裏懶惰...... :-) – Gilad 2012-04-16 11:31:03
實際上,TreeView根本沒有虛擬化,這是另一個令人頭痛的問題。恰當地實施它是一個適當的痛苦。當然,這很困難,因爲每個父項的項目都是由另一個ItemsControl表示的,並且使每個嵌入的ItemsControl知道最外層容器的大小和屬性,以便虛擬化能夠正常工作。這就是TreeView的性能真的吸引超過一小部分項目的原因。 – 2012-04-18 08:22:42