WPF设计标准学习记录25

namespace HelloWorld.MVVM
{
    public class SalesManager
    {
        public static readonly RoutedEvent CheckEvent = EventManager.RegisterRoutedEvent(
            "CheckEvent", 
            RoutingStrategy.Bubble, 
            typeof(RoutedEventHandler), 
            typeof(SalesManager));
 
        public static void AddCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.AddHandler(CheckEvent, handler);
            }            
        }
 
        public static void RemoveCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.RemoveHandler(CheckEvent, handler);
            }
        }
    }
}
/// <summary>
/// MVVM模式销售事件管理器(全局路由事件中枢)
/// [!] 当前未实现WeakEvent模式(可能引发内存泄漏)
/// </summary>
namespace HelloWorld.MVVM 
{
    public class SalesManager 
    {
        /// <summary>
        /// 注册全局冒泡检查事件(跨组件通信)
        /// [!] 建议扩展自定义事件参数(如携带校验结果)
        /// </summary>
        public static readonly RoutedEvent CheckEvent = EventManager.RegisterRoutedEvent(
            "CheckEvent", 
            routingStrategy: RoutingStrategy.Bubble, // 冒泡传递至父容器 
            handlerType: typeof(RoutedEventHandler),
            ownerType: typeof(SalesManager));
 
        /// <summary>
        /// 动态添加事件处理器(支持任意UI元素订阅)
        /// [!] 未处理非UIElement类型(静默失败)
        /// </summary>
        /// <param name="dependencyObject">需实现UIElement接口的对象</param>
        public static void AddCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.AddHandler(CheckEvent, handler); // [!] 建议添加Try/Catch块 
            }            
        }
 
        /// <summary>
        /// 动态移除事件处理器(防止冗余订阅)
        /// [!] 未验证处理器是否存在(可能抛出异常)
        /// </summary>
        public static void RemoveCheckHandler(DependencyObject dependencyObject, RoutedEventHandler handler)
        {
            if (dependencyObject is UIElement uiElement)
            {
                uiElement.RemoveHandler(CheckEvent, handler);
            }
        }
    }
}
graph TD 
    A[注册CheckEvent路由事件] --> B{调用AddCheckHandler}
    B --> C[类型检查是否为UIElement]
    C -->|是| D[绑定事件处理器]
    C -->|否| E[静默失败不处理]
    
    F[调用RemoveCheckHandler] --> G{类型检查}
    G -->|是| H[解绑事件处理器]
    G -->|否| E 
    
    style D stroke:#4CAF50 
    style H stroke:#FF5722 
    classDef alert fill:#FFEBEE,stroke:#EF5350;
    class E alert 

架构增强建议

// 增强版方法(带异常处理)
public static void SafeAddHandler(DependencyObject obj, RoutedEventHandler handler)
{
    try 
    {
        if (obj is UIElement ele && handler != null)
        {
            WeakEventManager<UIElement, RoutedEventArgs>
                .AddHandler(ele, CheckEvent, handler); // 弱事件绑定 
        }
    }
    catch (ArgumentException ex)
    {
        Debug.WriteLine($"处理器绑定失败: {ex.Message}");
    }
}
<controls:Widget Value="{Binding ElementName=slider,Path=Value}" 
                 Target="1000000"
                 Completed="Widget_Completed"
                 helper:SalesManager.Check="Widget_Check"
                 Title="第四季度北美市场总销售额统计" 
                 Canvas.Left="116" Canvas.Top="17" 
                 Height="103" Width="269"/>
xmlns:helper="clr-namespace:HelloWorld.MVVM"
private void Widget_Completed(object sender, RoutedEventArgs e)
{
    Widget widget = sender as Widget;
    listBox.Items.Insert(0, $"完成目标销售额:{widget.Value}");
    widget.RaiseEvent(new RoutedEventArgs(SalesManager.CheckEvent));
}
 
private void Widget_Check(object sender, RoutedEventArgs e)
{
    Widget widget = sender as Widget;
    if (((int)widget.Value) % 500000 < 5000)
    {
        listBox.Items.Insert(0, $"当前业绩:{widget.Value},每累计50万发奖金啦:{widget.Value*0.5}");
    }
}

/// <summary>
/// 销售目标完成事件处理器(冒泡事件响应)
/// [!] 未处理空引用风险(sender可能非Widget类型)
/// </summary>
private void Widget_Completed(object sender, RoutedEventArgs e)
{
    // 动态类型转换(需验证sender有效性)
    Widget widget = sender as Widget;
    // 在列表顶部插入业务记录(UI线程操作风险)
    listBox.Items.Insert(0, $"完成目标销售额:{widget.Value}");
    // 触发全局检查事件(形成事件链式反应)
    widget.RaiseEvent(new RoutedEventArgs(SalesManager.CheckEvent));
}
 
/// <summary>
/// 销售业绩检查事件处理器(奖金发放逻辑)
/// [!] 浮点数精度问题(建议使用decimal类型)
/// </summary>
private void Widget_Check(object sender, RoutedEventArgs e)
{
    Widget widget = sender as Widget;
    // 非精确数值检测逻辑(500000为50万单位)
    if (((int)widget.Value) % 500000 < 5000)
    {
        // 奖金计算(未考虑税务计算场景)
        listBox.Items.Insert(0, 
            $"当前业绩:{widget.Value},每累计50万发奖金啦:{widget.Value*0.5}");
    }
}
graph TD 
    subgraph Widget_Completed方法 
        A[收到Completed事件] --> B{类型转换验证}
        B -->|成功| C[插入销售完成记录]
        C --> D[触发CheckEvent事件]
        B -->|失败| E[静默忽略]
    end 
 
    subgraph Widget_Check方法 
        F[收到CheckEvent事件] --> G{数值检测条件}
        G -->|余数<5000| H[计算奖金数额]
        H --> I[插入奖金发放记录]
        G -->|余数≥5000| J[不做处理]
    end 
 
    style C stroke:#4CAF50 
    style I stroke:#2196F3 
    style D stroke:#FF9800 
    classDef error fill:#FFEBEE,stroke:#EF5350;
    class E,J error 

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>
元素类型 属性/特性 技术说明
Canvas Width="200" Height="200" 创建200x200像素的绘画区域
Rectangle Canvas.Left="100" Canvas.Top="100" 基于Canvas的绝对定位系统,距左/顶各100像素
Width="50" Height="50" 构建50x50像素的正方形
Fill="RoyalBlue" 使用CSS4标准颜色(RGB值65/105/225)
Opacity="1.0" 完全不透明(支持0.0-1.0的浮点精度控制)
RotateTransform Angle="45" 坐标系变换(以元素中心为原点顺时针旋转45度)
graph TD 
    A[Canvas] --> B[布局属性]
    A --> C[子元素体系]
    
    B --> B1(Width=200)
    B --> B2(Height=200)
    
    C --> C1[Rectangle]
    C1 --> C1a[布局定位]
    C1a --> C1a1(Canvas.Left=100)
    C1a --> C1a2(Canvas.Top=100)
    
    C1 --> C1b[几何属性]
    C1b --> C1b1(Width=50)
    C1b --> C1b2(Height=50)
    
    C1 --> C1c[视觉样式]
    C1c --> C1c1(Fill=RoyalBlue)
    C1c --> C1c2(Opacity=1.0)
    
    C1 --> C1d[图形变换]
    C1d --> C1d1(RenderTransform)
    C1d1 --> C1d1a(RotateTransform)
    C1d1a --> C1d1a1(Angle=45°)

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" CenterX="25" CenterY="25" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>
graph TD 
    A[Canvas] --> B[容器属性]
    A --> C[渲染引擎]
    
    B --> B1("量子画布尺寸: 200×200px")
    C --> C1("光线追踪渲染管线")
    
    A --> D[图形元素]
    D --> D1{Rectangle}
    
    D1 --> E1[定位系统]
    E1 --> E1a("Canvas.Left=100")
    E1 --> E1b("Canvas.Top=100")
    
    D1 --> E2[几何参数]
    E2 --> E2a("Width=50")
    E2 --> E2b("Height=50")
    
    D1 --> E3[视觉呈现]
    E3 --> E3a("Fill=RoyalBlue")
    E3 --> E3b("Opacity=1.0")
    
    D1 --> E4[空间变换]
    E4 --> E4a("RotateTransform")
    E4a --> E4a1("Angle=45°")
    E4a --> E4a2("CenterX=25")
    E4a --> E4a3("CenterY=25")

Angle属性表示获取或设置顺时针旋转的角度(以度为单位)。默认值是0度。CenterX 和CenterY 表示获取或设置旋转中心点的 x y坐标,Value属性表示当前转换的矩阵。通常我们只需要设置Angle、CenterX 和CenterY即可

<Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" 
                Width="100" 
                Height="25" 
                Content="RotateTransform" 
                HorizontalAlignment="Center