【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; }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章