简体   繁体   中英

Is there any unique identifier for wpf UIElement?

For logging user actions in my WPF forms, I added some global event handlers

I want to log exactly which control fire the event, is there some unique identifier for a wpf UIElement like ClientId in ASP.Net?

Why don't you use the Hash Code.

You can compare the values to make sure they are the same object, and its easy to get them with .GetHashCode()


Edit

Obviously this is different every time you run the program, so actually this is prolly a bad idea, unless you want to update the log each time the process is logged. Still possible though

I mean you could store a hash value for each object at the time the log is created, but i don't know if I like that

One way you can do this is with a custom attribute. Like so...

The UIElement you want to log (UserControl for example):

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

Then you need the custom attribute class

[System.AttributeUsage(AttributeTargets.Class)]
public class UserInterfaceIDAttribute : Attribute
{
    public Guid ID { get; set; }
}

Now in your code, you can do something like this:

MyUserControl control = new MyUserControl();
foreach(object att in control.GetCustomAttributes(typeof(UserInterfaceAttribute),false))
{
    UserInterfaceAttribute uiAtt = (UserInterfaceAttribute)att;
    Guid theID = uiAtt.ID;
}

Because you are tagging the control with an attribute in the code, the unique identifier never changes no matter how many times you kill / launch the application.

Of course this is a basic example that shows how to access the ID but you will probably want to use some type of Aspect Oriented Programming. I do exactly this kind of thing using Castle Windsor Interceptors, but that is out of the scope of this post.

Ideally, you will be accessing this ID when there is some kind of event that gets fired. Using interceptors allows you go capture method calls before they are invoked wherein you can look up the ID as shown above and log the action. Alternatively, you can just use

this.GetCustomAttributes(...)

in some method when an event is fired on the control and embed your Logging code there. This pattern is not the best because you're sprinkling cross-cutting concerns all over making some type of Aspect Oriented Programming approach better...but again I digress and it is out of the scope of this post...but you get the idea.

Hope this helps.

Seems I found answer to my question, the answer is No, now way to do that, As noted in MSDN here (http://msdn.microsoft.com/en-us/magazine/dd483216.aspx)

Notice that the top-level Window control definition does not contain a Name attribute. This is significant because, as we'll see shortly, when you write test automation, an easy way to get a reference to a control using the MUIA library is to access the AutomationId property, which is generated by the compiler from the control's Name attribute. Controls without a XAML Name attribute will not receive an AutomationId property. This idea is a specific, low-level example of the importance of considering application design issues for things such as security, extensibility, and test automation.

see FrameworkElement.Tag Property

The Tag property can be used. It is of type object and can be set to anything.

I think UIElement.Uid should work. Assign it in XAML and access it in code.

<Label Uid="FirstName"/>

OR in a style property

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

Then refer in C# code:

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

You're just looking at adding a Name or x:Name so that the Window / UserControl / Page exposes the control to the rest of the class with the specified name.

<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.
    }
}

Also, you can always use the FrameworkElement.Tag object. It's meant to store arbitrary information, so you can use this as a unique identifier if you want to.

I believe, for logging user actions, you can use the UIAutomation tree and the AutomationElement.AutomationId property, because this approach is supported in all standard UI controls by default. Many third-party controls are also supports the AutomationId for their elements (eg grid cells). An AutomationId is useful for creating test automation scripts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM