首页 > 其他分享 >实现Avalonia平台下低配版的Dock控件:实现TabControl的可关闭

实现Avalonia平台下低配版的Dock控件:实现TabControl的可关闭

时间:2024-05-29 16:46:31浏览次数:13  
标签:控件 Dock items 配版 下低 item public Avalonia

在弄一个项目,在WPF下用Dock控件,在Avalonia平台下实现也有一个Dock控件,但用起来有点复杂。

Install-Package Dock.Avalonia
Install-Package Dock.Model.Mvvm

感兴趣的可以访问网站了解:https://github.com/wieslawsoltes/Dock

其实本身用的比较简单,所以就想着,用TabControl来改一下。实现最基本的功能。
需要对TabControl和TabItem进行改造

TabControlEx

using Avalonia;
using Avalonia.Controls;

namespace CommandTerminal.Controls;

public class TabControlEx : TabControl
{
    private ICommand _closeItemCommand;

    public ICommand CloseItemCommand
    {
        get => _closeItemCommand;
        private set => SetAndRaise(CloseItemCommandProperty, ref _closeItemCommand, value);
    }

    public static readonly DirectProperty<TabControlEx, ICommand> CloseItemCommandProperty =
    AvaloniaProperty.RegisterDirect<TabControlEx, ICommand>(
        nameof(CloseItemCommand),
        o => o.CloseItemCommand,
        (o, v) => o.CloseItemCommand = v);

    public TabControlEx()
    {
        _closeItemCommand = new SimpleParamActionCommand(CloseItem);
    }

    private void CloseItem(object? tabItemSource)
    {
        ArgumentNullException.ThrowIfNull(tabItemSource);

        if (tabItemSource is not TabItemEx tabItem)
            return;

        RemoveItem(tabItem);
    }

    private void RemoveItem(TabItemEx container)
    {
        if (container == null || Items == null)
        {
            Debug.Assert(false);
            return;
        }

        Items.Remove(container);
    }

    private void SetSelectedNewTab(IList items, int removedItemIndex) =>
    SelectedItem = removedItemIndex == items.Count ? items[^1] : items[removedItemIndex];
}

public class SimpleParamActionCommand(Action<object?> action) : ICommand
{
    public bool CanExecute(object? parameter) => true;

    public void Execute(object? parameter) => action.Invoke(parameter);

    public event EventHandler? CanExecuteChanged;
}

TabItemEx

using Avalonia;
using Avalonia.Controls;

namespace CommandTerminal.Controls;

public class TabItemEx : TabItem
{
    public string ContentId { get; set; }

    public bool CanClose
    {
        get => GetValue(CanCloseProperty);
        set => SetValue(CanCloseProperty, value);
    }

    public static readonly StyledProperty<bool> CanCloseProperty =
        AvaloniaProperty.Register<TabItemEx, bool>(nameof(CanClose), defaultValue: true);
}

加载控件样式

在App.axaml文件里添加:

<Application.Resources>
    <Thickness x:Key="TabControlTopPlacementItemMargin">0 0 0 2</Thickness>

    <x:Double x:Key="TabItemMinHeight">48</x:Double>
    <x:Double x:Key="TabItemVerticalPipeHeight">24</x:Double>
    <x:Double x:Key="TabItemPipeThickness">2</x:Double>

    <ControlTheme x:Key="CloseItemCommandButton" TargetType="Button">
        <Setter Property="Content">
            <Template>
                <Viewbox
                    Width="16"
                    Height="16"
                    Stretch="Uniform">
                    <Canvas Width="24" Height="24">
                        <Path Data="M13.46,12L19,17.54V19H17.54L12,13.46L6.46,19H5V17.54L10.54,12L5,6.46V5H6.46L12,10.54L17.54,5H19V6.46L13.46,12Z" Fill="{Binding $parent[Button].Foreground}" />
                    </Canvas>
                </Viewbox>
            </Template>
        </Setter>
        <!--<Setter Property="Background" Value="{DynamicResource ButtonBackground}" />-->
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
        <Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrush}" />
        <Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" />
        <Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
        <Setter Property="Padding" Value="2" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="RenderTransform" Value="none" />
        <Setter Property="Transitions">
            <Transitions>
                <TransformOperationsTransition Property="RenderTransform" Duration="0:0:.075" />
            </Transitions>
        </Setter>

        <Setter Property="Template">
            <ControlTemplate>
                <ContentPresenter
                    x:Name="PART_ContentPresenter"
                    Padding="{TemplateBinding Padding}"
                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}"
                    CornerRadius="{TemplateBinding CornerRadius}"
                    Foreground="{TemplateBinding Foreground}"
                    RecognizesAccessKey="True" />
            </ControlTemplate>
        </Setter>

        <Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
            <Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
            <Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
        </Style>

        <Style Selector="^:pressed">
            <Setter Property="RenderTransform" Value="scale(0.98)" />
        </Style>

        <Style Selector="^:pressed  /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
            <Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
            <Setter Property="Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
        </Style>

        <Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
            <Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
            <Setter Property="Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
        </Style>

        <Style Selector="^.accent">
            <Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="{DynamicResource AccentButtonBackground}" />
                <Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrush}" />
                <Setter Property="Foreground" Value="{DynamicResource AccentButtonForeground}" />
            </Style>

            <Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" />
                <Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}" />
                <Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" />
            </Style>

            <Style Selector="^:pressed  /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" />
                <Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}" />
                <Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
            </Style>

            <Style Selector="^:disabled /template/ ContentPresenter#PART_ContentPresenter">
                <Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundDisabled}" />
                <Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}" />
                <Setter Property="Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" />
            </Style>
        </Style>
    </ControlTheme>

    <ControlTheme x:Key="{x:Type ctl:TabControlEx}" TargetType="ctl:TabControlEx">
        <Setter Property="Margin" Value="0" />
        <Setter Property="Padding" Value="{DynamicResource TabItemMargin}" />
        <Setter Property="Background" Value="{DynamicResource TabControlBackground}" />
        <Setter Property="Template">
            <ControlTemplate>
                <Border
                    HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalAlignment}"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    CornerRadius="{TemplateBinding CornerRadius}">
                    <DockPanel>
                        <ItemsPresenter
                            Name="PART_ItemsPresenter"
                            DockPanel.Dock="{TemplateBinding TabStripPlacement}"
                            ItemsPanel="{TemplateBinding ItemsPanel}" />
                        <ContentPresenter
                            Name="PART_SelectedContentHost"
                            Margin="{TemplateBinding Padding}"
                            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                            Content="{TemplateBinding SelectedContent}"
                            ContentTemplate="{TemplateBinding SelectedContentTemplate}" />
                    </DockPanel>
                </Border>
            </ControlTemplate>
        </Setter>

        <Style Selector="^[TabStripPlacement=Left] /template/ ItemsPresenter#PART_ItemsPresenter > WrapPanel">
            <Setter Property="Orientation" Value="Vertical" />
        </Style>
        <Style Selector="^[TabStripPlacement=Right] /template/ ItemsPresenter#PART_ItemsPresenter > WrapPanel">
            <Setter Property="Orientation" Value="Vertical" />
        </Style>
        <Style Selector="^[TabStripPlacement=Top] /template/ ItemsPresenter#PART_ItemsPresenter">
            <Setter Property="Margin" Value="{DynamicResource TabControlTopPlacementItemMargin}" />
        </Style>
    </ControlTheme>

    <ControlTheme x:Key="{x:Type ctl:TabItemEx}" TargetType="ctl:TabItemEx">
        <Setter Property="FontSize" Value="{DynamicResource TabItemHeaderFontSize}" />
        <Setter Property="FontWeight" Value="{DynamicResource TabItemHeaderThemeFontWeight}" />
        <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselected}" />
        <Setter Property="Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselected}" />
        <Setter Property="Padding" Value="{DynamicResource TabItemHeaderMargin}" />
        <Setter Property="Margin" Value="0" />
        <Setter Property="MinHeight" Value="{DynamicResource TabItemMinHeight}" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Template">
            <ControlTemplate>
                <Border
                    Name="PART_LayoutRoot"
                    Padding="{TemplateBinding Padding}"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    CornerRadius="{TemplateBinding CornerRadius}">
                    <Panel>
                        <Grid ColumnDefinitions="* Auto">
                            <ContentPresenter
                                Name="PART_ContentPresenter"
                                Grid.Column="0"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                Content="{TemplateBinding Header}"
                                ContentTemplate="{TemplateBinding HeaderTemplate}"
                                RecognizesAccessKey="True" />
                            <Button
                                Grid.Column="1"
                                Margin="2,0,0,0"
                                Command="{Binding $parent[ctl:TabControlEx].CloseItemCommand}"
                                CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
                                IsVisible="{TemplateBinding CanClose}"
                                Theme="{StaticResource CloseItemCommandButton}" />
                        </Grid>
                        <Border
                            Name="PART_SelectedPipe"
                            Background="{DynamicResource TabItemHeaderSelectedPipeFill}"
                            CornerRadius="{DynamicResource ControlCornerRadius}"
                            IsVisible="False" />
                    </Panel>
                </Border>
            </ControlTemplate>
        </Setter>

        <!--  Selected state  -->
        <!--  We don't use selector to PART_LayoutRoot, so developer can override selected item background with TabStripItem.Background  -->
        <Style Selector="^:selected">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelected}" />
            <Setter Property="Foreground" Value="{DynamicResource TabItemHeaderForegroundSelected}" />
        </Style>
        <Style Selector="^:selected /template/ Border#PART_SelectedPipe">
            <Setter Property="IsVisible" Value="True" />
        </Style>

        <!--  PointerOver state  -->
        <Style Selector="^:pointerover /template/ Border#PART_LayoutRoot">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPointerOver}" />
            <Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPointerOver}" />
        </Style>

        <!--  Selected PointerOver state  -->
        <Style Selector="^:selected:pointerover /template/ Border#PART_LayoutRoot">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPointerOver}" />
            <Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPointerOver}" />
        </Style>

        <!--  Pressed state  -->
        <Style Selector="^:pressed /template/ Border#PART_LayoutRoot">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundUnselectedPressed}" />
            <Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundUnselectedPressed}" />
        </Style>

        <!--  Selected Pressed state  -->
        <Style Selector="^:selected:pressed /template/ Border#PART_LayoutRoot">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundSelectedPressed}" />
            <Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundSelectedPressed}" />
        </Style>

        <!--  Disabled state  -->
        <Style Selector="^:disabled /template/ Border#PART_LayoutRoot">
            <Setter Property="Background" Value="{DynamicResource TabItemHeaderBackgroundDisabled}" />
            <Setter Property="TextElement.Foreground" Value="{DynamicResource TabItemHeaderForegroundDisabled}" />
        </Style>

        <!--  TabStripPlacement States Group  -->
        <Style Selector="^[TabStripPlacement=Left] /template/ Border#PART_SelectedPipe">
            <Setter Property="Width" Value="{DynamicResource TabItemPipeThickness}" />
            <Setter Property="Height" Value="{DynamicResource TabItemVerticalPipeHeight}" />
            <Setter Property="Margin" Value="0,0,2,0" />
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style Selector="^[TabStripPlacement=Left] /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Margin" Value="8,0,0,0" />
        </Style>

        <Style Selector="^[TabStripPlacement=Top] /template/ Border#PART_SelectedPipe, ^[TabStripPlacement=Bottom] /template/ Border#PART_SelectedPipe">
            <Setter Property="Height" Value="{DynamicResource TabItemPipeThickness}" />
            <Setter Property="Margin" Value="0,0,0,2" />
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="VerticalAlignment" Value="Bottom" />
        </Style>

        <Style Selector="^[TabStripPlacement=Right] /template/ Border#PART_SelectedPipe">
            <Setter Property="Width" Value="{DynamicResource TabItemPipeThickness}" />
            <Setter Property="Height" Value="{DynamicResource TabItemVerticalPipeHeight}" />
            <Setter Property="Margin" Value="2,0,0,0" />
            <Setter Property="HorizontalAlignment" Value="Right" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style Selector="^[TabStripPlacement=Right] /template/ ContentPresenter#PART_ContentPresenter">
            <Setter Property="Margin" Value="0,0,8,0" />
        </Style>
        <Style Selector="^[TabStripPlacement=Right]">
            <Setter Property="HorizontalContentAlignment" Value="Right" />
        </Style>
    </ControlTheme>
</Application.Resources>

前台使用

添加引用:

xmlns:ctl="clr-namespace:CommandTerminal.Controls"

代码:

<ctl:TabControlEx
    x:Name="TabList"
    Grid.Row="1"
    Grid.Column="1">
    <ctl:TabItemEx CanClose="False" Header="遥测监视">
        <v:UcTelemetryMonitoring />
    </ctl:TabItemEx>
    <ctl:TabItemEx Header="发令记录">
        <v:UcCommandSendList />
    </ctl:TabItemEx>
    <ctl:TabItemEx Header="发令查询">
        <v:UcCommandHistory />
    </ctl:TabItemEx>
</ctl:TabControlEx>

后台使用:

public void ShowLayoutDocument(string contendId, string title, Func<UserControl> func, bool canClose = true)
{
    var items = _tabList.Items.Cast<TabItemEx>().ToArray();
    var info = items.FirstOrDefault(s => s.ContentId == contendId);
    if (info != null)
    {
        _tabList.SelectedItem = info;
        return;
    }

    var item = new TabItemEx();
    item.ContentId = contendId;
    item.Header = title;
    item.CanClose = canClose;
    item.Content = func.Invoke();

    _tabList.Items.Add(item);
    _tabList.SelectedItem = item;
}

标签:控件,Dock,items,配版,下低,item,public,Avalonia
From: https://www.cnblogs.com/wzwyc/p/18220600

相关文章

  • 界面控件DevExpress WinForms的流程图组件 - 可完美复制Visio功能(一)
    DevExpressWinForms的Diagram(流程图)组件允许您复制MicrosoftVisio中的许多功能,并能在下一个WindowsForms项目中引入信息丰富的图表、流程图和组织图。P.S:DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能完美......
  • GDI+图形显示控件(坐标轴、缩放、轨迹、图像、实时编辑等功能)
    1.显示控件介绍自动化设备(点胶、激光、贴装等等)中经常需要实时显示加工轨迹以及在线编辑加工轨迹等功能,需要用到相应的绘图显示控件。功能强的显示控件不但需要支持大量数据点显示还需要有强大的轨迹编辑功能。如下CadDisplay控件使用GDI+绘图技术,不断的在多个项目中进......
  • 界面控件DevExtreme v23.2亮点 - 标签、表单、编辑器功能升级
    DevExtreme拥有高性能的HTML5/JavaScript小部件集合,使您可以利用现代Web开发堆栈(包括React,Angular,ASP.NETCore,jQuery,Knockout等)构建交互式的Web应用程序。从Angular和Reac,到ASP.NETCore或Vue,DevExtreme包含全面的高性能和响应式UI小部件集合,可在传统Web和下一代移动应用程序中......
  • 使用.Net Core开发WPF App系列教程( 八、WPF中的常用控件(下))
    使用.NetCore开发WPFApp系列教程一、.NetCore和WPF介绍二、在VisualStudio2019中创建.NetCoreWPF工程三、与.NetFramework的区别四、WPF中的XAML五、WPF中的布局六、WPF中的常用控件(上)七、WPF中的常用控件(中)八、WPF中的常用控件(下)其它、实现多语言切换的几种方......
  • Qt QListWidget 存放自定义控件不显示问题
    问题软件功能:每点击一次新建按钮,在QListWidget新增一行自定义控件,主窗口和自定义窗口如下。主窗口:自定义窗口问题代码:Form*myform=newForm();QListWidgetItem*item=newQListWidgetItem(ui->listWidget);ui->listWidget->addItem(item);......
  • WPF一个简单的属性编辑控件
    代码:publicclassPropertiesControl:Grid{[TypeConverter(typeof(LengthConverter))]publicdoubleRowHeight{get{return(double)GetValue(RowHeightProperty);}set{SetValue(RowHeightProperty,......
  • ThinkEditor跨平台BS电子病历编辑器控件具备哪些能力
            笔者从事医疗信息化行业工作10多年,对当前热门的BS架构的电子病历编辑器有一些自己的思考发出来供大家讨论,笔者信译,演示网址:www.thinkeditor.com。1.病历结构是否需要结构化        首先是陈旧的自定义字符串格式,虽格式灵活,但需自己编写病历格式解析......
  • 推荐2款开源、美观的WinForm UI控件库
    前言今天大姚给大家分享2款开源、美观的WinFormUI控件库,希望可以帮助到有需要的同学。WinForm介绍WinForm是一个传统的桌面应用程序框架,它基于Windows操作系统的原生控件和窗体。通过简单易用的API,开发者可以快速构建基于窗体的应用程序,并且可以利用多种控件和事件来实现应......
  • 使用python uiautomation模块,结合多线程快速寻找控件
    文章目录1.形式一2.形式二1.形式一该方法使用多线程进行搜索,主线程不会等待所有子线程返回结果后再继续执行,而是在获取队列中第一个结果后立即继续执行。优势在于一旦有子线程找到结果,主线程就能立即继续执行;劣势在于未找到结果的子线程会持续搜索,直到达到设定的最大......
  • 【WPF】WPF中调用winform的控件,winform始终置顶处理
    在WPF中调用windowFormsHost的控件时,由于渲染机制的问题总会出现各种问题,比如Winform的控件始终会出现在最顶层。在WPF项目中添加Microsoft.DwayneNeed.dll可以避免置顶问题<xmlns:interop=clr-namespace:Microsoft.DwayneNeed.Interop;assembly=Microsoft.DwayneNeed></xmln......