14

我想构建一个新的自定义控件。我发现很少有教程给了我一些关于如何实现这一点的线索。据我了解,创建新的自定义控件总是通过扩展当前控件来完成,甚至可以从层次结构的最基本级别扩展控件,例如,您甚至可以扩展:

  • 界面元素
  • 框架元素
  • 控制
  • 内容控制
  • 标头内容控件
  • 项目控制
  • 选择器
  • 范围基础

如以下教程中所写:http ://wpftutorial.net/HowToCreateACustomControl.html

所以,我按照教程创建了一个自定义控件库类型的新项目,得到了我的通用 .xaml 和我的代码。到现在为止还挺好。

我可以区分 3 种类型或类别的事件。

  1. 由将使用我的控件的窗口(或容器)消耗的事件:这些是我想向外部公开的事件。这些怎么写?

  2. 与控件本身相关且未暴露在外部的事件;例如,如果鼠标在我的控制上,我想对此做出反应。我可以在 XAML 中做到这一点:

    <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="true">  
        <Setter Property="Fill" TargetName="LeftTraingularIndicator">  
          <Setter.Value>  
            <SolidColorBrush Color="Yellow" />  
          </Setter.Value>  
        </Setter>
      </Trigger>
    </ControlTemplate.Triggers>  
    

假设我的 ControlTemplate 中有一个带有 fill 属性的元素,名为 x:name="LeftTraingularIndicator"

问题:

现在我想在我的 XAML 中对 IsMouseDown 做出反应。我怎么做?没有“IsMouseDown”触发器。此外,如果我想在后面的代码中对此做出反应怎么办?更进一步,如果我想从后面的代码中更改 LeftTraingularIndicator "Fill" 怎么办?

  1. 与子元素相关的事件,这些子元素是我的 ControlTemplate 的可视化结构的一部分,例如,如果我想对 XAML/甚至代码隐藏中的“LeftTraigularIndicator”的“IsMouseOver”做出反应怎么办?甚至可能两者兼而有之。

我现在正在尝试 2 天......感觉我在理解事物的运作方式时遗漏了一些东西。没有找到任何深入解释这些问题的教程。

对于我在这里提出的每个问题,我想看几行示例。

谢谢。

4

2 回答 2

12

经过广泛研究...

1)将事件暴露给外界......就像你从任何其他班级暴露一样。

public delegate void myDelegate(int someValue);  
public event myDelegate myEvent;

在您的代码中的某处:

if(myEvent!=null)
  myEvent(5);

那部分没有什么新东西。

2)从后面的代码创建一个实例构造函数

public MyCustomControl()
{
    MouseMove += MyCustomControl_MouseMove;
}


void MyCustomControl_MouseMove(object sender, MouseEventArgs e)
{
   //now you can react to the movement of the mouse.
   //if for example I want to address an element, let's say a rectangle:

   var ele = (Rectangle)Template.FindName("myRect",this);
   ele.Fill=myNewBrush;

   // provided that we have an element named "myRect" (x:name="myRect") in
   // the generic.xaml style->Control Template-> which corresponds to that name.

}

3) 不推荐 - 因为它属于用户控件而不是自定义控件的范围。自定义控件是“原子”,用户控件更适合组合控件的目的。

但也不是不可能...

var myButton = (Button)Template.FindName("myButton",this);
myButton.OnMouseMove += ....

请记住:

  • 任何应该在代码隐藏中知道的东西都必须命名。

  • 您的 xaml 应该不知道背后的代码是做什么的。(除了!-继续阅读)

  • 设计 XAML 时,代码隐藏中应该知道的部分必须具有正确的名称。

我真的希望这能帮助其他在尝试开发自己的自定义控件时遇到“墙”的人。

于 2013-02-19T12:26:28.330 回答
8

如果我正确理解了您的问题 - 以下是我的解释。

WPF 事件系统提供 3 种类型的事件:1. 冒泡 2. 隧道 3. 直接

如果一个事件(本质上)被定义为一个冒泡事件,那么该事件就会从源冒泡到它的容器。例如:如果 Grid 包含两个按钮 - Rectangle1 和 Rectangle2,那么对这些矩形的任何点击都会冒泡到 Grid。隧道是相反的,事件从父级隧道传输到子级。

有非常特殊的直接事件——它们只适用于事件没有意义被冒泡的情况。例如,在上述场景中,在任一 Rectangle 上冒泡 MouseOver 事件是没有意义的——因为每次鼠标进入矩形时,事件都会冒泡到 Grid——这是人们可能期望的。

你的问题:

将使用我的控件的窗口(或容器)可消耗的事件:这些是我想向外部公开的事件..如何编写这些?

您基本上需要编写一个冒泡事件。下面是一个来自 msdn 的示例,用于在 Button 上编写冒泡点击事件(我自己没有尝试过)路由事件示例

public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));

你的问题:

问题:现在我想在我的 XAML 中对 IsMouseDown 做出反应……我该怎么做?没有“IsMouseDown”触发器。如果我想在后面的代码中对此做出反应怎么办?甚至如果我想从后面的代码中更改 LeftTraingularIndicator "Fill" 怎么办?

顺便说一句,您似乎对事件和触发器有些困惑(因为我看到您可以互换使用它们)。虽然它们是相关的,但它们并不相同。

回到您的问题 - WPF 允许触发属性更改 - 这本质上是您要挂钩的 - 在这种情况下是 IsMouseOver。但是缺少的是该属性是 ControlTemplate 的目标(我不知道它是什么)。我只是推断 ControlTemplate 的目标是一个矩形。在这种情况下,它没有“IsMouseDown”属性供您挂钩属性触发器。这就是你找不到它的原因。您必须编写一个 AttachedEvent 的另一个选项(它们与 AttachedProperties 非常相似).. 完全是另一个主题:(

我希望我尽力了:)

于 2013-02-19T17:54:22.837 回答