WPF UIElement是否有唯一标识符?

11

为了记录我的WPF表单中的用户操作,我添加了一些全局事件处理程序。

我想记录触发事件的精确控件,是否有像 ASP.Net 中的 ClientId 一样的 WPF UIElement 的唯一标识符?


1
你尝试过使用FrameworkElement.Name属性吗? - DmitryG
是的,Dmitry,但是名称可以为空,我不想为了记录目的而在每个控件上都放置名称。 - Arsen Mkrtchyan
@ArsenMkrt,PersistId 属性似乎是您正在寻找的内容。不幸的是,它现在已经过时,而且显然没有替代品。也许您可以退而求其次,自己生成唯一标识符,如果是这种情况,请参见此问题 - Frédéric Hamidi
@FrédéricHamidi,自己生成ID是个好主意,但你在帖子中提到标识符将在应用程序重新启动后更改...这将使日志无法使用,我认为没有任何方法可以生成稳定的标识符。 - Arsen Mkrtchyan
我的理解是,XAML构建页面的方式不需要为UI元素分配唯一标识符。如果没有分配唯一标识符,我认为你不会找到一个。GetHashCode可能起作用。 - paparazzo
7个回答

10

为什么不使用哈希码。

您可以比较值以确保它们是相同的对象,并且使用.GetHashCode()很容易获取它们。


编辑

显然,每次运行程序时都会有所不同,因此实际上这可能是一个不好的想法,除非您想在记录过程每次记录时更新日志。不过仍然是有可能的

我的意思是,您可以在创建日志时为每个对象存储哈希值,但我不知道我是否喜欢这种方式。


我进行了测试,问题在于GetHashCode()仅适用于该会话。如果关闭应用程序并重新启动UI元素,则所有元素的哈希码都将更新。但是还是+1。 - paparazzo
@ExitMusic,GetHashCode 将在应用程序重新启动后至少更改一次,这将使日志无法使用...我希望拥有更稳定的标识符。 - Arsen Mkrtchyan

2

看起来我找到了我的问题的答案,答案是否定的,没有办法做到这一点。正如在MSDN中所指出的那样(http://msdn.microsoft.com/en-us/magazine/dd483216.aspx)

请注意,顶级窗口控件定义不包含名称属性。这很重要,因为我们很快将看到,在编写测试自动化时,使用MUIA库获取对控件的引用的简单方法是访问AutomationId属性,该属性由编译器从控件的名称属性生成。没有XAML名称属性的控件将不会收到AutomationId属性。这个想法是考虑应用程序设计问题的特定低级示例,例如安全性、可扩展性和测试自动化的重要性。


2

你可以通过自定义属性来实现这一点,例如:

你想要记录的UIElement(例如UserControl):

[UserInterfaceID(ID = "{F436E9B3-C2F6-4CF8-8C75-0A2A756F1C74}")]
public partial class MyUserControl : UserControl
{
    InitializeComponent();
    // or whatever...
}

然后您需要自定义属性“class”。
[System.AttributeUsage(AttributeTargets.Class)]
public class UserInterfaceIDAttribute : Attribute
{
    public Guid ID { get; set; }
}

现在在你的代码中,你可以这样做:
MyUserControl control = new MyUserControl();
foreach(object att in control.GetCustomAttributes(typeof(UserInterfaceAttribute),false))
{
    UserInterfaceAttribute uiAtt = (UserInterfaceAttribute)att;
    Guid theID = uiAtt.ID;
}

由于您在代码中使用属性标记控件,因此唯一标识符不会因为您杀死/启动应用程序的次数而改变。
当然,这只是一个基本示例,展示了如何访问ID,但您可能希望使用某种类型的面向方面编程。我使用Castle Windsor Interceptors完全做到了这一点,但这超出了本文的范围。
理想情况下,当触发某种事件时,您将访问此ID。使用拦截器允许您在调用方法之前捕获方法调用,在其中查找上面所示的ID并记录操作。或者,您可以直接使用。
this.GetCustomAttributes(...)

在某些方法中,当控件上触发事件时,嵌入您的日志记录代码。这种模式并不是最好的,因为您会在各个地方散布横切关注点,使得某种面向方面编程方法更好...但我又岔开话题了,它超出了本篇文章的范围...但你明白我的意思吧。
希望这有所帮助。

为什么我需要一个属性?我可以设置所有UI元素的名称属性,但我只想找到一种不设置任何东西的方法,因为在winform中这是可能的。 - Arsen Mkrtchyan

0

你只需要添加一个Namex:Name,这样Window/UserControl/Page就会使用指定的名称将控件公开给类的其余部分。

<Window ...>
   <Grid>
       ...

       <!-- These controls are named, so you can access them directly by name -->
       <Button x:Name="btnMyNamedButton" ... />
       <Button Name="btnMyOtherNamedButton" ... />

       <!-- This control is not named, so you can not directly access it by name -->
       <Button ... />
   <Grid>
</Window>

public partial class MyWindow : Window
{
    public MyWindow()
    {
        InitializeComponent();

        //btnMyNamedButton can be accessed
        //btnMyOtherNamedbutton can also be accessed

        //The third button can be accessed, but not directly by name.
    }
}

此外,您始终可以使用 FrameworkElement.Tag 对象。它旨在存储任意信息,因此如果您想要,可以将其用作唯一标识符。

感谢@m-y的回答,但是Name不是我要找的,首先我不想为了日志记录而专门给所有UI控件命名,其次对于一个有很多编辑控件的单元格模板的网格来说,这可能是不可能的... - Arsen Mkrtchyan

0

0

我认为UIElement.Uid应该可以工作。在XAML中分配它并在代码中访问它。

<Label Uid="FirstName"/>

或者在样式属性中:

<Setter Property="Uid" Value="SecondName"/>

然后在C#代码中引用:

    if (Keyboard.FocusedElement is UIElement uiElement)
    {
        Debug.WriteLine(this, $"{uiElement.Uid}");
    }


0

我相信,为了记录用户操作,您可以使用UIAutomation树AutomationElement.AutomationId属性,因为这种方法在所有标准UI控件中都默认支持。许多第三方控件也支持其元素的AutomationId(例如网格单元格)。AutomationId对于创建测试自动化脚本非常有用。


哦,看起来 AutomationId 也应该被设置,如果没有设置,它会返回 Name,而在我们的情况下 Name 是空的... 我猜没有办法做到我想要的... - Arsen Mkrtchyan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接