【Avalonia學習筆記】2:用ListBox代替ItemsControl作爲圖形VM容器

使用ItemsControl作爲容器

想要在面板上顯示圖形,先把圖形的VM都創建好,希望能傳所有VM到一個列表(ObservableCollection泛型容器)裏,然後界面上就能顯示出對應的圖形。

這些圖形VM都繼承自ViewModelBase,同時有一些是結點, 有一些是連線。結點是直接繼承了NetworkItem_VM,然後再間接繼承ViewModelBase的。

如果直接用ItemsControl作爲VM容器,即:

<ItemsControl Items="{Binding UserControlVMs}" Height="1000" Width="2000"/>

設置Style和數據模板:

<UserControl.Styles>
    <!--ItemsControl的樣式-->
    <Style Selector="ItemsControl">
      <!--使用Canvas作面板-->
      <Setter Property="ItemsPanel">
        <Setter.Value>
          <ItemsPanelTemplate>
            <!--這裏需要爲Canvas設置顏色才能在按下時相應鼠標事件-->
            <Canvas Background="#EEEEEE" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
              <!--右鍵菜單行爲-->
              <Canvas.ContextMenu>
                <ContextMenu>
                  <MenuItem Header="xxx" Command="{Binding xxx}"/>
                </ContextMenu>
              </Canvas.ContextMenu>
            </Canvas>
          </ItemsPanelTemplate>
        </Setter.Value>
      </Setter>
      <!--裏面放圖形的VM-->
      <Setter Property="ItemTemplate">
        <Setter.Value>
          <DataTemplate DataType="vm:ViewModelBase">
            <ContentControl Content="{Binding .}"/>
          </DataTemplate>
        </Setter.Value>
      </Setter>
    </Style>
    <!--Avalonia的ItemsControl中沒有ItemContainerStyle,用選擇器+樣式來綁定Canvas.Left等附加屬性-->
    <Style Selector="ItemsControl > ContentPresenter">
      <Setter Property="Canvas.Left" Value="{Binding X}"/>
      <Setter Property="Canvas.Top" Value="{Binding Y}"/>
    </Style>
  </UserControl.Styles>

這種方法能實現需求,但是出來的圖形會有些卡頓,僅僅在當前圖形面板上操作時不太能感覺出來,但是在不同圖形面板之間切換時就會有很明顯的卡頓,創建12個結點,40多個控制點,然後所有這些50多個點連線在一起,切換回這個圖形面板時大約需要3秒的時間(8G內存64位win10集成顯卡)。

改用ListBox作爲容器

這篇問答中討論的是WPF上的切換問題,Avalonia中也有這樣的問題,然後答主意思是ListBox支持UI虛擬化,代替原生的ItemsControl效果會好一些。

改用ListBox作爲VM容器:

<ListBox Name="panel" Items="{Binding UserControlVMs}" Height="1000" Width="2000"/>

設置Style和數據模板:

<UserControl.Styles>
    <!--ListBox的樣式-->
    <Style Selector="ListBox#panel">
      <!--使用Canvas作面板-->
      <Setter Property="ItemsPanel">
        <Setter.Value>
          <ItemsPanelTemplate>
            <!--這裏需要爲Canvas設置顏色才能在按下時相應鼠標事件-->
            <Canvas Background="#EEEEEE" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
              <!--右鍵菜單行爲-->
              <Canvas.ContextMenu>
                <ContextMenu>
                  <MenuItem Header="xxx" Command="{Binding xxx"/>
                </ContextMenu>
              </Canvas.ContextMenu>
            </Canvas>
          </ItemsPanelTemplate>
        </Setter.Value>
      </Setter>
      <!--裏面放圖形的VM-->
      <Setter Property="ItemTemplate">
        <Setter.Value>
          <DataTemplate DataType="vm:ViewModelBase">
            <ContentControl Content="{Binding .}"/>
          </DataTemplate>
        </Setter.Value>
      </Setter>
    </Style>
    <!--ListBox下的ListBoxItem項-->
    <Style Selector="ListBox#panel ListBoxItem">
      <Setter Property="Focusable" Value="False"/>
      <Setter Property="Canvas.Left" Value="{Binding X}"/>
      <Setter Property="Canvas.Top" Value="{Binding Y}"/>
    </Style>
  </UserControl.Styles>

這裏用ListBox#panel指明name的原因是圖形裏面有些也帶ListBox(狀態機的轉移邊),這裏讓選擇器只選擇這個容器ListBox。現在渲染速度比用ItemsControl快了大概三倍,和上面一樣多的圖形大概要用1秒的時間。

但是這種方式又帶來一個問題,就是因爲ListBox有指針停留和選中的問題,其中的項會產生灰色不透明和藍色透明的背景,而且會影響下層內容的點擊,問題效果和可視樹如下:
在這裏插入圖片描述
在這裏插入圖片描述
如果用Enabled="False"或者IsHitTestVisible="False"確實可以解決這個問題,但是這樣整個ListBox就不響應鼠標操作了,對圖形的拖拽和打開右鍵菜單都會失效。

這裏加了一個Style來解決這個問題,判斷是NetWorkItem_VM的子類就設置IsHitTestVisible="True",否則就是圖形連線,設置爲False

<Style Selector="ListBox#panel ListBoxItem > ContentPresenter">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="IsHitTestVisible" Value="{Binding IsNetWorkItemVM}"/>
</Style>

相應地,在ViewModelBase類裏要加個判斷的屬性:

namespace sbid._VM
{
    public class ViewModelBase : ReactiveObject
    {
        // 判斷這個ViewModelBase對象是不是一個NetWorkItem_VM
        // 用於在狀態機中區分"線"(直線/箭頭)和"元素"(狀態/轉移塊/控制點)
        // 以將"線"設置不可點擊也不會顯示選中效果,防止影響到"元素"的顯示和移動
        public bool IsNetWorkItemVM { get => this is NetworkItem_VM; }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章