WPF 自定义可交互 Tester 控件:拖动、缩放、内容承载与层级管理全方案

WPF 自定义可交互 Tester 控件:拖动、缩放、内容承载与层级管理全方案

在 WPF 项目开发中,测试工具、仪表盘类场景常需要实现可灵活交互的容器控件(Tester) ,需支持工具栏(标题 + 关闭)、动态注入子控件、鼠标拖动、边缘缩放、点击置顶等核心功能。本文提供一套无第三方依赖、代码可直接运行、无省略的完整实现方案,覆盖从布局到交互的全流程。

一、环境准备

  • 开发工具:Visual Studio 2022
  • 框架版本:.NET 6(兼容.NET Framework 4.8/5/7)
  • 无额外 NuGet 依赖(纯 WPF 原生 API 实现)

二、完整代码实现

步骤 1:创建 TesterView 控件(核心容器)

TesterView 是核心控件,包含工具栏、内容承载区,封装关闭、内容注入等基础逻辑。

1.1 TesterView.xaml(布局文件)
XML 复制代码
<Window x:Class="WpfTester.TesterView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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"
        mc:Ignorable="d"
        x:Name="RootTester"
        Width="300" Height="200"
        WindowStyle="None"  <!-- 隐藏系统标题栏,自定义工具栏 -->
        AllowsTransparency="True"
        Background="Transparent">
    <!-- 外层Grid:实现圆角+阴影(可选,提升视觉效果) -->
    <Grid>
        <Grid.Effect>
            <DropShadowEffect BlurRadius="10" Color="#888" Opacity="0.5" Direction="270"/>
        </Grid.Effect>
        <!-- 主容器:白色背景+圆角 -->
        <Grid Background="White" CornerRadius="4">
            <Grid.RowDefinitions>
                <!-- 工具栏:固定30px高度 -->
                <RowDefinition Height="30"/>
                <!-- 内容区:自适应剩余空间 -->
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <!-- 1. 顶部工具栏(拖动区域+关闭按钮) -->
            <Grid Grid.Row="0" x:Name="ToolBarGrid" Background="#2E86AB" CornerRadius="4 4 0 0">
                <!-- 标题文本 -->
                <TextBlock Text="Tester容器" 
                           Foreground="White" 
                           FontSize="12"
                           Margin="10,0,0,0" 
                           VerticalAlignment="Center"/>
                <!-- 关闭按钮 -->
                <Button x:Name="CloseBtn"
                        Content="×" 
                        Width="20" Height="20"
                        HorizontalAlignment="Right" 
                        Margin="0,0,5,0"
                        Background="Transparent" 
                        Foreground="White" 
                        BorderThickness="0" 
                        Cursor="Hand"
                        FontSize="12"
                        Click="CloseBtn_Click">
                    <Button.Style>
                        <Style TargetType="Button">
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="#E74C3C"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
            </Grid>

            <!-- 2. 内容承载控件(动态注入子控件) -->
            <ContentControl x:Name="TesterContentControl" 
                            Grid.Row="1" 
                            Margin="5"
                            HorizontalContentAlignment="Stretch"
                            VerticalContentAlignment="Stretch"/>
        </Grid>
    </Grid>
</Window>
1.2 TesterView.xaml.cs(逻辑文件)
cs 复制代码
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfTester
{
    /// <summary>
    /// TesterView.xaml 的交互逻辑
    /// </summary>
    public partial class TesterView : Window
    {
        #region 事件定义
        /// <summary>
        /// 关闭事件(通知主窗体移除当前Tester)
        /// </summary>
        public event RoutedEventHandler OnClose;

        /// <summary>
        /// 鼠标按下事件(供主窗体绑定拖动逻辑)
        /// </summary>
        public event MouseButtonEventHandler ToolBarMouseDown
        {
            add => ToolBarGrid.MouseLeftButtonDown += value;
            remove => ToolBarGrid.MouseLeftButtonDown -= value;
        }

        /// <summary>
        /// 鼠标移动事件(供主窗体绑定拖动逻辑)
        /// </summary>
        public event MouseEventHandler ToolBarMouseMove
        {
            add => ToolBarGrid.MouseMove += value;
            remove => ToolBarGrid.MouseMove -= value;
        }

        /// <summary>
        /// 鼠标释放事件(供主窗体绑定拖动逻辑)
        /// </summary>
        public event MouseButtonEventHandler ToolBarMouseUp
        {
            add => ToolBarGrid.MouseLeftButtonUp += value;
            remove => ToolBarGrid.MouseLeftButtonUp -= value;
        }
        #endregion

        #region 构造函数
        public TesterView()
        {
            InitializeComponent();
            // 绑定鼠标移动事件(用于缩放检测)
            this.MouseMove += TesterView_MouseMove;
            this.MouseLeftButtonDown += TesterView_MouseLeftButtonDown;
            this.MouseMove += TesterView_MouseMove_Resize;
            this.MouseLeftButtonUp += TesterView_MouseLeftButtonUp;
        }
        #endregion

        #region 核心方法
        /// <summary>
        /// 注入子控件到ContentControl中
        /// </summary>
        /// <param name="content">要注入的任意WPF控件</param>
        public void SetContent(UIElement content)
        {
            if (content == null) return;

            // 确保子控件填充ContentControl
            content.HorizontalAlignment = HorizontalAlignment.Stretch;
            content.VerticalAlignment = VerticalAlignment.Stretch;
            content.Margin = new Thickness(2);

            // 设置内容并强制刷新布局(解决内容不显示问题)
            TesterContentControl.Content = content;
            TesterContentControl.UpdateLayout();
            this.UpdateLayout();
        }

        /// <summary>
        /// 获取工具栏控件(供外部调用)
        /// </summary>
        /// <returns>工具栏Grid</returns>
        public Grid GetToolBar()
        {
            return ToolBarGrid;
        }
        #endregion

        #region 关闭逻辑
        private void CloseBtn_Click(object sender, RoutedEventArgs e)
        {
            // 触发关闭事件,通知主窗体移除当前Tester
            OnClose?.Invoke(this, e);
            // 关闭当前窗口
            this.Close();
        }
        #endregion

        #region 缩放相关逻辑
        // 缩放边缘阈值(鼠标离边缘10px内触发缩放)
        private const int ResizeThreshold = 10;
        // 缩放状态变量
        private bool _isResizing;
        private ResizeEdge _currentResizeEdge;
        private Point _resizeStartMousePos;
        private double _resizeStartWidth;
        private double _resizeStartHeight;
        private double _resizeStartLeft;
        private double _resizeStartTop;

        /// <summary>
        /// 鼠标移动:检测边缘并切换光标
        /// </summary>
        private void TesterView_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isResizing) return; // 缩放中不切换光标

            Point mousePos = e.GetPosition(this);
            // 排除工具栏区域(仅内容区触发缩放)
            if (mousePos.Y < 30)
            {
                this.Cursor = Cursors.Hand;
                _currentResizeEdge = ResizeEdge.None;
                return;
            }

            // 检测当前鼠标所在边缘
            _currentResizeEdge = DetectResizeEdge(mousePos);
            // 根据边缘切换光标
            this.Cursor = GetCursorByEdge(_currentResizeEdge);
        }

        /// <summary>
        /// 鼠标按下:初始化缩放状态
        /// </summary>
        private void TesterView_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // 仅在内容区且检测到边缘时触发缩放
            Point mousePos = e.GetPosition(this);
            if (mousePos.Y < 30 || _currentResizeEdge == ResizeEdge.None)
            {
                _isResizing = false;
                return;
            }

            _isResizing = true;
            // 记录缩放起始状态
            _resizeStartMousePos = e.GetPosition(this);
            _resizeStartWidth = this.Width;
            _resizeStartHeight = this.Height;
            _resizeStartLeft = this.Left;
            _resizeStartTop = this.Top;
            this.CaptureMouse(); // 捕获鼠标,避免移出控件中断缩放
        }

        /// <summary>
        /// 鼠标移动:执行缩放逻辑
        /// </summary>
        private void TesterView_MouseMove_Resize(object sender, MouseEventArgs e)
        {
            if (!_isResizing || _currentResizeEdge == ResizeEdge.None) return;

            Point currentMousePos = e.GetPosition(this);
            double deltaX = currentMousePos.X - _resizeStartMousePos.X;
            double deltaY = currentMousePos.Y - _resizeStartMousePos.Y;

            // 计算新尺寸和位置
            double newWidth = _resizeStartWidth;
            double newHeight = _resizeStartHeight;
            double newLeft = _resizeStartLeft;
            double newTop = _resizeStartTop;

            switch (_currentResizeEdge)
            {
                case ResizeEdge.Left:
                    newWidth = Math.Max(150, _resizeStartWidth - deltaX); // 最小宽度150
                    newLeft = _resizeStartLeft + deltaX;
                    break;
                case ResizeEdge.Top:
                    newHeight = Math.Max(100, _resizeStartHeight - deltaY); // 最小高度100
                    newTop = _resizeStartTop + deltaY;
                    break;
                case ResizeEdge.Right:
                    newWidth = Math.Max(150, _resizeStartWidth + deltaX);
                    break;
                case ResizeEdge.Bottom:
                    newHeight = Math.Max(100, _resizeStartHeight + deltaY);
                    break;
                case ResizeEdge.TopLeft:
                    newWidth = Math.Max(150, _resizeStartWidth - deltaX);
                    newHeight = Math.Max(100, _resizeStartHeight - deltaY);
                    newLeft = _resizeStartLeft + deltaX;
                    newTop = _resizeStartTop + deltaY;
                    break;
                case ResizeEdge.TopRight:
                    newWidth = Math.Max(150, _resizeStartWidth + deltaX);
                    newHeight = Math.Max(100, _resizeStartHeight - deltaY);
                    newTop = _resizeStartTop + deltaY;
                    break;
                case ResizeEdge.BottomLeft:
                    newWidth = Math.Max(150, _resizeStartWidth - deltaX);
                    newHeight = Math.Max(100, _resizeStartHeight + deltaY);
                    newLeft = _resizeStartLeft + deltaX;
                    break;
                case ResizeEdge.BottomRight:
                    newWidth = Math.Max(150, _resizeStartWidth + deltaX);
                    newHeight = Math.Max(100, _resizeStartHeight + deltaY);
                    break;
            }

            // 应用新属性
            this.Width = newWidth;
            this.Height = newHeight;
            this.Left = newLeft;
            this.Top = newTop;
        }

        /// <summary>
        /// 鼠标释放:结束缩放
        /// </summary>
        private void TesterView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (_isResizing)
            {
                _isResizing = false;
                this.ReleaseMouseCapture();
                this.Cursor = Cursors.Hand;
            }
        }

        /// <summary>
        /// 检测鼠标所在的缩放边缘
        /// </summary>
        private ResizeEdge DetectResizeEdge(Point mousePos)
        {
            bool isLeft = mousePos.X <= ResizeThreshold;
            bool isRight = mousePos.X >= this.ActualWidth - ResizeThreshold;
            bool isTop = mousePos.Y >= 30 && mousePos.Y <= 30 + ResizeThreshold;
            bool isBottom = mousePos.Y >= this.ActualHeight - ResizeThreshold;

            if (isLeft && isTop) return ResizeEdge.TopLeft;
            if (isRight && isTop) return ResizeEdge.TopRight;
            if (isLeft && isBottom) return ResizeEdge.BottomLeft;
            if (isRight && isBottom) return ResizeEdge.BottomRight;
            if (isLeft) return ResizeEdge.Left;
            if (isRight) return ResizeEdge.Right;
            if (isTop) return ResizeEdge.Top;
            if (isBottom) return ResizeEdge.Bottom;

            return ResizeEdge.None;
        }

        /// <summary>
        /// 根据边缘类型切换光标
        /// </summary>
        private Cursor GetCursorByEdge(ResizeEdge edge)
        {
            return edge switch
            {
                ResizeEdge.Left or ResizeEdge.Right => Cursors.SizeWE,
                ResizeEdge.Top or ResizeEdge.Bottom => Cursors.SizeNS,
                ResizeEdge.TopLeft or ResizeEdge.BottomRight => Cursors.SizeNWSE,
                ResizeEdge.TopRight or ResizeEdge.BottomLeft => Cursors.SizeNESW,
                _ => Cursors.Hand
            };
        }

        /// <summary>
        /// 缩放边缘枚举
        /// </summary>
        private enum ResizeEdge
        {
            None,
            Left,
            Top,
            Right,
            Bottom,
            TopLeft,
            TopRight,
            BottomLeft,
            BottomRight
        }
        #endregion
    }
}

步骤 2:创建 MainWindow 主窗体(承载 Tester 容器)

MainWindow 作为 Tester 的父容器,实现 Tester 的创建、拖动、层级置顶等逻辑。

2.1 MainWindow.xaml(布局文件)
XML 复制代码
<Window x:Class="WpfTester.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Tester容器演示" Height="800" Width="1200"
        WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.ColumnDefinitions>
            <!-- 左侧操作区 -->
            <ColumnDefinition Width="200"/>
            <!-- 右侧Tester承载区 -->
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 左侧操作区 -->
        <Grid Grid.Column="0" Background="#F5F5F5" Padding="10">
            <StackPanel VerticalAlignment="Top">
                <Button x:Name="CreateTesterBtn"
                        Content="创建Tester容器"
                        Width="180" Height="40"
                        Margin="0,0,0,10"
                        Click="CreateTesterBtn_Click"/>
                <TextBlock Text="操作说明:" FontSize="12" FontWeight="Bold" Margin="0,0,0,5"/>
                <TextBlock Text="1. 点击工具栏拖动Tester" FontSize="11" Margin="0,0,0,2"/>
                <TextBlock Text="2. 鼠标移到边缘缩放Tester" FontSize="11" Margin="0,0,0,2"/>
                <TextBlock Text="3. 点击Tester任意区域置顶" FontSize="11" Margin="0,0,0,2"/>
                <TextBlock Text="4. 点击×关闭Tester" FontSize="11" Margin="0,0,0,2"/>
            </StackPanel>
        </Grid>

        <!-- 右侧Tester承载区(Canvas支持自由布局) -->
        <Canvas x:Name="TesterCanvas" Grid.Column="1" Background="#FFFFFF"/>
    </Grid>
</Window>
2.2 MainWindow.xaml.cs(逻辑文件)
cs 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfTester
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        // 存储所有创建的Tester容器
        private readonly List<TesterView> _testerList = new List<TesterView>();
        // 拖动状态变量
        private bool _isDragging;
        private Point _dragStartMousePos;
        private double _dragStartLeft;
        private double _dragStartTop;
        private TesterView _currentDraggingTester;

        public MainWindow()
        {
            InitializeComponent();
        }

        #region 创建Tester容器
        private void CreateTesterBtn_Click(object sender, RoutedEventArgs e)
        {
            // 1. 创建Tester实例
            var tester = new TesterView();
            // 2. 设置初始位置(避免重叠)
            int testerCount = _testerList.Count;
            tester.Left = 50 + testerCount * 30;
            tester.Top = 50 + testerCount * 30;
            // 3. 注入示例内容控件
            tester.SetContent(CreateTestContentControl($"Tester {testerCount + 1} 内容区"));
            // 4. 绑定拖动逻辑
            BindTesterDrag(tester);
            // 5. 绑定关闭逻辑
            tester.OnClose += Tester_OnClose;
            // 6. 绑定层级置顶逻辑
            tester.MouseDown += Tester_MouseDown;
            // 7. 添加到列表和Canvas
            _testerList.Add(tester);
            tester.Owner = this; // 设置父窗口
            tester.Show(); // 显示Tester(Window类型需Show,UserControl需添加到Canvas)
        }

        /// <summary>
        /// 创建示例内容控件(可替换为任意自定义控件)
        /// </summary>
        private UIElement CreateTestContentControl(string text)
        {
            var grid = new Grid();
            grid.Background = new SolidColorBrush(Color.FromRgb(248, 249, 250));
            
            var textBlock = new TextBlock
            {
                Text = text,
                FontSize = 16,
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                Foreground = new SolidColorBrush(Color.FromRgb(51, 51, 51))
            };
            grid.Children.Add(textBlock);
            
            return grid;
        }
        #endregion

        #region Tester拖动逻辑
        private void BindTesterDrag(TesterView tester)
        {
            // 工具栏鼠标按下:初始化拖动
            tester.ToolBarMouseDown += (s, e) =>
            {
                if (e.LeftButton != MouseButtonState.Pressed) return;

                _isDragging = true;
                _currentDraggingTester = tester;
                // 记录起始位置(相对于屏幕)
                _dragStartMousePos = Mouse.GetPosition(null);
                _dragStartLeft = tester.Left;
                _dragStartTop = tester.Top;
                // 捕获鼠标
                tester.GetToolBar().CaptureMouse();
                // 置顶当前Tester(拖动时自动置顶)
                BringTesterToFront(tester);
            };

            // 工具栏鼠标移动:执行拖动
            tester.ToolBarMouseMove += (s, e) =>
            {
                if (!_isDragging || _currentDraggingTester != tester) return;

                Point currentMousePos = Mouse.GetPosition(null);
                double deltaX = currentMousePos.X - _dragStartMousePos.X;
                double deltaY = currentMousePos.Y - _dragStartMousePos.Y;

                // 更新Tester位置
                tester.Left = _dragStartLeft + deltaX;
                tester.Top = _dragStartTop + deltaY;
            };

            // 工具栏鼠标释放:结束拖动
            tester.ToolBarMouseUp += (s, e) =>
            {
                if (_isDragging && _currentDraggingTester == tester)
                {
                    _isDragging = false;
                    _currentDraggingTester = null;
                    tester.GetToolBar().ReleaseMouseCapture();
                }
            };
        }
        #endregion

        #region Tester层级置顶逻辑
        private void Tester_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (sender is TesterView tester)
            {
                BringTesterToFront(tester);
            }
        }

        /// <summary>
        /// 将指定Tester置顶,并高亮工具栏
        /// </summary>
        private void BringTesterToFront(TesterView targetTester)
        {
            // 1. 重置所有Tester的工具栏样式
            foreach (var tester in _testerList)
            {
                tester.GetToolBar().Background = new SolidColorBrush(Color.FromRgb(46, 134, 171));
            }

            // 2. 获取当前最大Topmost值(Window层级)
            int maxTopmost = _testerList.Max(t => t.Topmost ? 1 : 0);
            // 3. 设置目标Tester置顶
            targetTester.Topmost = true;
            // 4. 高亮目标Tester工具栏
            targetTester.GetToolBar().Background = new SolidColorBrush(Color.FromRgb(52, 152, 219));

            // 5. 重置其他Tester的Topmost(可选,避免多个置顶)
            foreach (var tester in _testerList.Where(t => t != targetTester))
            {
                tester.Topmost = false;
            }
        }
        #endregion

        #region Tester关闭逻辑
        private void Tester_OnClose(object sender, RoutedEventArgs e)
        {
            if (sender is TesterView tester)
            {
                // 从列表移除
                _testerList.Remove(tester);
                // 关闭窗口
                tester.Close();
            }
        }
        #endregion
    }
}

步骤 3:创建示例子控件(可选,替换为业务控件)

如果需要注入自定义业务控件,可创建如下示例:

cs 复制代码
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfTester
{
    /// <summary>
    /// 自定义业务控件示例
    /// </summary>
    public class CustomBusinessControl : UserControl
    {
        public CustomBusinessControl(string title)
        {
            this.Width = double.NaN;
            this.Height = double.NaN;
            this.Background = new SolidColorBrush(Color.FromRgb(230, 240, 250));

            var stackPanel = new StackPanel
            {
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                Margin = new Thickness(10)
            };

            var titleText = new TextBlock
            {
                Text = title,
                FontSize = 14,
                FontWeight = FontWeight.FromOpenTypeWeight(600),
                Margin = new Thickness(0, 0, 0, 10)
            };

            var contentText = new TextBlock
            {
                Text = "这是自定义业务控件内容\n支持按钮、表格等任意WPF元素",
                FontSize = 12,
                TextWrapping = TextWrapping.Wrap,
                HorizontalAlignment = HorizontalAlignment.Center
            };

            stackPanel.Children.Add(titleText);
            stackPanel.Children.Add(contentText);
            this.Content = stackPanel;
        }
    }
}

三、功能测试与验证

  1. 运行项目:点击 "创建 Tester 容器" 按钮,生成带工具栏和内容区的 Tester;
  2. 拖动测试:点击 Tester 工具栏,可自由拖动 Tester 到任意位置;
  3. 缩放测试:鼠标移到 Tester 边缘(如右下角),光标切换为缩放样式,拖拽可缩放;
  4. 置顶测试:创建多个 Tester 并重叠,点击任意 Tester 可置顶;
  5. 关闭测试:点击 Tester 右上角 "×",可关闭并移除 Tester;
  6. 内容注入 :修改CreateTestContentControl方法,注入自定义控件,验证内容正常显示。

四、常见问题排查

问题 1:ContentControl 不显示内容

  • 解决方案:
    1. 确保注入的控件设置HorizontalAlignment/ VerticalAlignment = Stretch
    2. 调用UpdateLayout()强制刷新布局;
    3. 检查 Tester 的高度是否大于工具栏高度(至少 100px);
    4. 给 ContentControl 添加背景色,确认区域是否可见。

问题 2:缩放时 Tester 移出屏幕

  • 解决方案:在缩放逻辑中添加边界限制:

    cs 复制代码
    // 缩放后限制位置
    this.Left = Math.Max(0, Math.Min(SystemParameters.WorkArea.Width - this.Width, newLeft));
    this.Top = Math.Max(0, Math.Min(SystemParameters.WorkArea.Height - this.Height, newTop));

问题 3:拖动时 Tester 卡顿

  • 解决方案:
    1. 移除不必要的Effect(如 DropShadowEffect);
    2. 拖动时使用Mouse.GetPosition(null)(屏幕坐标)而非相对坐标;
    3. 避免拖动时频繁更新布局。

五、扩展方向

  1. 布局保存 / 恢复:记录 Tester 的位置、尺寸、内容,重启后自动恢复;
  2. 多内容切换:在 Tester 工具栏添加标签,支持多个子控件切换;
  3. 快捷键支持:ESC 关闭当前 Tester、Ctrl + 滚轮缩放、Shift + 拖动等比例缩放;
  4. 样式定制:通过资源字典统一管理 Tester 的颜色、字体、圆角等样式;
  5. 批量管理:添加 Tester 列表,支持批量关闭、置顶、重置位置。

六、总结

本文提供的 Tester 控件方案基于 WPF 原生 API 实现,无第三方依赖,包含布局、内容注入、拖动、缩放、置顶、关闭等核心功能,代码完整可直接运行。方案采用解耦设计,Tester 控件与主窗体逻辑分离,便于扩展和维护,可广泛应用于测试工具、仪表盘、自定义窗口等场景。

相关推荐
豫狮恒1 小时前
OpenHarmony Flutter 原子化服务开发实战:轻量、跨端、分布式的全场景落地
flutter·wpf·openharmony
Scout-leaf17 小时前
WPF新手村教程(一) - 走不出新手村别找我
c#·wpf
5008418 小时前
鸿蒙 Flutter 分布式硬件调用:跨设备摄像头 / 麦克风共享
分布式·flutter·华为·electron·wpf·开源鸿蒙
SEO-狼术1 天前
Telerik UI for WPF and WinForms 2025 Q4 Crack
ui·wpf
武藤一雄1 天前
[WPF] 万字拆解依赖属性与附加属性
前端·microsoft·c#·.net·wpf
Macbethad1 天前
工业设备配方管理程序技术方案
wpf
我好喜欢你~1 天前
.net---图表库(LiveCharts.Wpf)
c#·wpf
分布式存储与RustFS1 天前
RustFS:基于Rust的高性能分布式对象存储,重新定义数据存储新标准!
人工智能·wpf·对象存储·minio·高可用·企业存储·rustfs
500841 天前
鸿蒙 Flutter 分布式数据同步:DistributedData 实时协同实战
分布式·flutter·华为·electron·开源·wpf·音视频