Avalonia 开发自定义控件 IO调试界面

实现的目标:

1.通过Excel文件,加载IO的信息

2.通过颜色反馈IO的当前的状态

3.通过点击,能触发IO的改变

先来看看 Excel文件

再来看看界面的效果

接下来就介绍实现方式了:

创建UserControl  ,把加载和点击控制的功能进行封装,数据来源就用绑定的方式

XAML界面代码:

<UserControl
    x:Class="LS.AvaloniaClient.UserControls.IOControl"
    xmlns="https://github.com/avaloniaui"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <UserControl.Styles>
        <Style Selector="TextBlock.TitleDefaultStyle">
            <Setter Property="Margin" Value="0,0,0,0" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="FontFamily" Value="Microsoft YaHei UI" />
            <Setter Property="FontSize" Value="22" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Foreground" Value="#FF35414F" />
        </Style>
        <Style Selector="Label.ContentDefaultStyle">
            <Setter Property="HorizontalAlignment" Value="Stretch" />
            <Setter Property="VerticalAlignment" Value="Stretch" />
            <Setter Property="HorizontalContentAlignment" Value="Left" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="FontFamily" Value="Microsoft YaHei UI" />
            <Setter Property="Foreground" Value="Black" />
        </Style>
    </UserControl.Styles>
    <ScrollViewer
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch"
        HorizontalScrollBarVisibility="Auto"
        VerticalScrollBarVisibility="Auto">
        <Grid x:Name="showGrid" />
    </ScrollViewer>
</UserControl>

CS代码:

因Avalonia没有InputBinding的事件,也没有触发器的概念,所以点击只能用鼠标事件来触发,触发器就用绑定的方式+转换器 来切换颜色;

    public partial class IOControl : UserControl
    {
        public IOControl()
        {
            InitializeComponent();
            this.Loaded += IOControl_Loaded;
        }

        private void IOControl_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
        {
            IOItemsProperty.Changed.AddClassHandler<Interactive>(IOItemsValueChanged);
            BoolArrayProperty.Changed.AddClassHandler<Interactive>(BoolArrayValueChanged);

            if (!string.IsNullOrEmpty(ExcelPath))
            {
                //从Excel中读取数据
                dataInfo = readExcelDataByNPOI();
                LoadControl();
            }
        }

        private void LoadControl()
        {
            if (dataInfo != null && dataInfo.Count > 0)
            {

                Grid grid =  this.FindControl<Grid>("showGrid");
                if (grid.Children.Count > 0)
                    grid.Children.Clear();
                if (grid.ColumnDefinitions.Count > 0)
                    grid.ColumnDefinitions.Clear();
                if (grid.RowDefinitions.Count > 0)
                    grid.RowDefinitions.Clear();

                int colNum = 0;
                int index = 0;//用于数据累计的索引
                foreach (var col in dataInfo)
                {
                    //添加列
                    grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(ColWidth) });
                    //添加行,如果未添加过的情况
                    if (grid.RowDefinitions.Count < 1)
                        grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(TitleHeight) });

                    //列头的显示控件
                    TextBlock title = new TextBlock();
                    title.Classes.Add("TitleDefaultStyle");
                    title.Text = col.Key;
                    grid.Children.Add(title);
                    Grid.SetColumn(title, colNum);
                    Grid.SetRow(title, 0);

                    //循环数据,添加行
                    string[] ds = col.Value.ToArray();

                    for (int i = 0; i < ds.Length; i++)
                    {
                        //先判断是否需要新增行
                        if (grid.RowDefinitions.Count < i + 2)
                            grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(ContentHeight) });

                        //添加边框
                        Border border = new Border();
                        border.HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch;
                        border.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch;
                        //参数可传,有默认值
                        border.BorderThickness = BorderNewThickness;
                        border.BorderBrush = BorderNewBrush;
                        border.Margin = BorderMargin;
                        border.Background = BorderBackground;

                        //添加Label,用来显示文本
                        string contentName = "lb_" + index;
                        Label content = new Label();
                        content.Name = contentName;
                        content.Content = ds[i];
                        content.Margin = ContentMargin;
                        content.Tag = IOItems[index];

                        //点击事件绑定
                        if (IOItems[index].ClickCommand != null)
                        {
                            content.PointerPressed += Content_PointerPressed;
                        }

                        //此处是绑定数组,用来显示颜色
                        Binding binding1 = new Binding($"{BoolArrayName}[{index}]");
                        binding1.Source = this.DataContext;
                        binding1.Mode = BindingMode.TwoWay;

                        //转换器,bool值转换为Brush
                        BoolToColorConverter btc = new BoolToColorConverter();
                        //显示的颜色可传参,有默认值
                        if(IOItems[index].InBrush!=null)
                        {
                            btc.InDefaultBrush = IOItems[index].InBrush;
                        }
                        if (IOItems[index].OutBrush != null)
                        {
                            btc.OutDefaultBrush = IOItems[index].OutBrush;
                        }
                        binding1.Converter = btc;
                        //添加绑定关系
                        content.Bind(ForegroundProperty, binding1);
                        //添加样式
                        content.Classes.Add("ContentDefaultStyle");
                        index++;

                        //添加到界面Grid中
                        grid.Children.Add(border);
                        Grid.SetColumn(border, colNum);
                        Grid.SetRow(border, i + 1);

                        grid.Children.Add(content);
                        Grid.SetColumn(content, colNum);
                        Grid.SetRow(content, i + 1);
                    }

                    colNum++;
                }
                this.showGrid = grid;
            }
        }

        public class BoolToColorConverter : IValueConverter
        {

            /// <summary>
            /// In的时候默认背景色
            /// </summary>
            public IBrush InDefaultBrush { get; set; } = Brushes.Red;
            /// <summary>
            /// Out的时候默认背景色
            /// </summary>
            public IBrush OutDefaultBrush { get; set; } = Brushes.Gray;

            public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
            {
                if (value is bool boolValue)
                {
                    return boolValue ? InDefaultBrush : OutDefaultBrush;
                }
                return OutDefaultBrush;
            }

            public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
            {
                if (value is IBrush colorValue)
                {
                    return colorValue == InDefaultBrush;
                }
                return false;
            }
        }

        private void Content_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
        {
            if(sender!=null)
            {
                Label label = (Label)sender;
                var model = label.Tag;
                if(model!=null)
                {
                    var item = model as IOItemModel;
                    if(item != null && item.ClickCommand!=null)
                    {
                        item.ClickCommand.Execute(item.CommandParameter);
                    }
                }
            }
        }

        Dictionary<string, List<string>> dataInfo;

        /// <summary>
        /// 使用NPOI读取Excel文件 无需安装EXcel
        /// </summary>
        /// <returns></returns>
        private Dictionary<string, List<string>> readExcelDataByNPOI()
        {
            Dictionary<string, List<string>> result = new Dictionary<string, List<string>>();
            if (!File.Exists(ExcelPath))
            {
                //取一个默认的Excel文件
                ExcelPath = AppDomain.CurrentDomain.BaseDirectory + "Assets\\Excels\\IOExcel.xlsx";
            }
            FileStream file = new FileStream(ExcelPath, FileMode.Open);
            ISheet sheet;
            IWorkbook workbook = null;

            // 2007版本及以上版本
            if (ExcelPath.IndexOf(".xlsx") > 0)
            {
                workbook = new XSSFWorkbook(file);
            }
            // 2003版本
            else if (ExcelPath.IndexOf(".xls") > 0)
            {
                workbook = new HSSFWorkbook(file);
            }

            if (workbook != null)
            {
                //读取excel中第一个sheet
                sheet = workbook.GetSheetAt(0);

                int columnsint = sheet.GetRow(0).LastCellNum;//得到列数

                for (int col = 0; col < columnsint; col++)
                {
                    //取列头
                    string title = sheet.GetRow(0)?.GetCell(col)?.StringCellValue;
                    if (string.IsNullOrEmpty(title))
                        continue;
                    if (!result.ContainsKey(title))
                        result.Add(title, new List<string>());
                    int row = 1;
                    while (true)
                    {
                        string content = sheet.GetRow(row)?.GetCell(col)?.StringCellValue;
                        if (string.IsNullOrEmpty(content))
                            break;
                        result[title].Add(content);
                        row++;
                    }
                }
            }

            file.Close();
            file.Dispose();
            return result;
        }

        #region 属性
        private bool InitFinish = false;

        /// <summary>
        /// 显示模板Excel的文件路径
        /// </summary>
        public string ExcelPath { get; set; }

        /// <summary>
        /// 标题的高度
        /// </summary>
        public double TitleHeight { get; set; } = 35;

        /// <summary>
        /// 显示的一列的宽度
        /// </summary>
        public double ColWidth { get; set; } = 298.3;

        /// <summary>
        /// 单个按钮或者Label1的高度
        /// </summary>
        public double ContentHeight { get; set; } = 32;

        /// <summary>
        /// 边框的Margin
        /// </summary>
        public Thickness BorderMargin = new Thickness(5, 1, 5, 1);

        /// <summary>
        /// 边框的粗细
        /// </summary>
        public Thickness BorderNewThickness = new Thickness(1);

        /// <summary>
        /// 边框的颜色
        /// </summary>
        public IBrush BorderNewBrush = Brushes.Black;

        /// <summary>
        /// 边框的背景色
        /// </summary>
        public IBrush BorderBackground = Brushes.WhiteSmoke;

        /// <summary>
        /// 单个按钮或者Label1的间隔
        /// </summary>
        public Thickness ContentMargin { get; set; } = new Thickness(5, 0, 5, 0);

        /// <summary>
        /// 绑定的BoolArray的实例名称
        /// </summary>
        public string BoolArrayName { get; set; }
        /// <summary>
        /// 绑定的Bool数组
        /// </summary>
        public bool[] BoolArray
        {
            get { return (bool[])GetValue(BoolArrayProperty); }
            set { SetValue(BoolArrayProperty, value); }
        }

        public static readonly AttachedProperty<bool[]> BoolArrayProperty =
  AvaloniaProperty.RegisterAttached<IOControl, Interactive, bool[]>("BoolArray", default(bool[]), false);

        private static void BoolArrayValueChanged(Interactive interactElem, AvaloniaPropertyChangedEventArgs args)
        {
            interactElem.SetValue(BoolArrayProperty, (bool[])args.NewValue);
            var element = interactElem as IOControl;
            if (!element.InitFinish)
            {
                element.LoadControl();
                element.InitFinish = true;
            }
        }

        /// <summary>
        /// 按钮的细节配置,绑定的Command,显示的背景色等等
        /// </summary>
        public List<IOItemModel> IOItems
        {
            get { return (List<IOItemModel>)GetValue(IOItemsProperty); }
            set { SetValue(IOItemsProperty, value); }
        }

        public static readonly AttachedProperty<List<IOItemModel>> IOItemsProperty =
AvaloniaProperty.RegisterAttached<IOControl, Interactive,List<IOItemModel>>("IOItems", default(List<IOItemModel>),false, BindingMode.OneTime);

        private static void IOItemsValueChanged(Interactive interactElem, AvaloniaPropertyChangedEventArgs args)
        {
            interactElem.SetValue(IOItemsProperty, (List<IOItemModel>)args.NewValue);
        }


        #endregion
    }

    public class IOItemModel
    {

        public ICommand ClickCommand { get; set; } = null;

        public object CommandParameter { get; set; } = null;

        /// <summary>
        /// In的时候默认背景色
        /// </summary>
        public Brush InBrush { get; set; } = null;
        /// <summary>
        /// Out的时候默认背景色
        /// </summary>
        public Brush OutBrush { get; set; } = null;
    }

使用的方式,就在界面添加 控件,然后赋值相关的属性即可,数据的变化就自行处理了

<ScrollViewer HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible">
    <ucLib:IOControl
        x:Name="_ioCrl"
        x:DataType="vm:VM_PageIO"
        BoolArray="{Binding BoolArrary, Mode=TwoWay}"
        BoolArrayName="BoolArrary"
        ExcelPath="E:\Work\GitCode\LS.Avalonia\Code\LS.AvaloniaClient\Assets\Excels\IOExcel.xlsx"
        IOItems="{Binding IOItemModels, Mode=TwoWay}" />
</ScrollViewer>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rotion_深

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值