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
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.
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.
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 (e.g. grid cells). An AutomationId is useful for creating test automation scripts.
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}");
}
Related
I’m exploring the use of custom TypeDescriptors to intercept when a data-bound property on a WPF control sets a CLR property. Due to reasons I need to figure out which exact control is setting a value.
If I use INotifyPropertyChanged I’ll get all of the different controls that subscribe to any property in my view model, not all that helpful. So I figured I’d go down the custom TypeDescriptor path and try the ProperyDescriptor.AddValueChanged and PropertyDescriptor.OnValueChanged way.
I can see that WPF calls ProperyDescriptor.AddValueChanged as expected, but my call to ProperyDescriptor.OnValueChanged never seems to trigger a call to PropertyDescriptor.GetValue and an update in the UI.
Are there some gotchas to using this stuff that I’m missing? Or are there other ways to figure out who’s subscribing to a CLR property. My alternative as I see it now would be to emit a custom proxy for the whole shebang, but I’d very much like to not have do that.
Edit: On looking at bit closer, I noticed that the delegates I get through PropertyDescriptor.AddValueChanged go to some internal MS helper class, so without some unreliable reflection-fu there's no way to use that to get the source control. I think I'll have to go with a custom proxy with dependency properties.
There's an alternative that may be useful. In a binding, you can set the NotifyOnSourceUpdated to True, which will raise the control's SourceUpdated event when WPF updates the CLR property it's data-bound to. For example, in a TextBox you can write:
<TextBox
Text="{Binding MyText, NotifyOnSourceUpdated=True}"
SourceUpdated="TextBox_OnSourceUpdated"
/>
In the code-behind, you can get the exact control (the sender) and the name of the dependency property that updated the CLR property:
private void TextBox_OnSourceUpdated(object sender, DataTransferEventArgs e)
{
var control = (TextBox)sender;
var propertyName = e.Property.Name;
}
With this method, you need to know that the control was a TextBox, but you can probably update it to test different kinds of controls. It depends on what you need this for, really.
With WinForms programs I've become accustomed to marking the Modifiers property of a control as 'Private' to prevent external classes and whatever else have you from being able to see and mess with them.
Being still very green with WPF, I see no obvious equivalent in WPF that allows me to make it so external classes cannot see a control I drop onto a form or another user control or what not. I did notice something of x:FieldModifier = "Private" but I get the error "x:FieldModifier = "Private" is not valid for the language C#".
How do I mark a control as Private so it cannot be viewed or accessed by external class objects?
TL;DR
Most of the time you don't need to worry about this in WPF. However:
If you name a XAML element using the x:Name attribute, then you can use the x:FieldModifier attribute to control the visibility of the auto-generated field representing that element. This attribute value is language- and case-specific.
If you don't name a XAML element, then don't bother using the x:FieldModifier attribute.
Read on for a more detailed explanation.
Explicit naming and generated fields
If you create a new WPF application project in Visual Studio, it will create a MainWindow class, the XAML for which looks something like this:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
If you look at the code-behind class for this window, it will look like this:
// Several using statements...
namespace StackOverflow
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Note the use of the partial keyword to denote this as a partial class. If you navigate to the project's obj\Debug folder using Windows Explorer, you will find a file called MainWindow.g.cs: it is this file that contains the code generated by the IDE from your XAML (it is basically the equivalent of the *.Designer.cs file from WinForms).
Your window has a Grid on it, but note that it is not surfaced directly anywhere in the code for MainWindow. Now edit your XAML to give the Grid a name:
<Grid x:Name="_myGrid">
Compile the application, and open the MainWindow.g.cs file again. You will see that the following line has been added:
internal System.Windows.Controls.Grid _myGrid;
Setting the x:Name property of the element in the XAML has caused the code generator to add a field with that name. The field is marked as internal which means it is accessible to all types in your project, but not to any other projects that reference your project.
So basically, if you do not explicitly name an element in the XAML using the x:Name attribute, the code generator will not create a named field for the element in the code-behind class, and your element will effectively be private (this means that the class itself cannot access the element directly either).
Nameless UI elements can still be accessed from code (if you have an instance)
An element without a name can still be accessed via code, by "walking" the visual tree of a Window instance. For example, because the window's content is set to a single Grid element, you can access that grid through code like so:
Grid grid = (Grid) this.Content;
this here refers to the MainWindow class instance.
WinForms has exactly the same "problem" as WPF in this regard: even controls that are not explicitly named can still be accessed through code. Imagine a WinForms Form with a single Button control on it. You can access that button like so:
Button button = (Button) this.Controls[0];
The fact that the button had a default Modifiers value of "Private" did not stop the code from being able to access it.
The FieldModifier attribute controls generated field visibility
Coming back to WPF, particularly if you're using the Model-View-ViewModel (MVVM) pattern, you will rarely need to explicitly name your elements in the XAML, hence the default behaviour will be fine. However, if you do find that you need to name your XAML elements, and you wish to "hide" these elements, then you can use the x:FieldModifier attribute to set the visibility of an element to private instead of the default internal. The value used for the attribute is language-dependent and case-sensitive, eg. for C#:
<Grid x:Name="_myGrid" x:FieldModifier="private">
Is there a way to manipulate to order in which Dependency Properties receive their values :
XAML :
<Window local:MyHelper.SomeAttachedProp="{Binding Something , Mode=OneWay}">
<Grid>
<TextBlock Text="Let him go first"
local:MyHelper.AnotherAttachedBooleanProp="True" />
</Grid>
</Window>
CS :
public static class MyHelper
{
propa ....
OnSomeAttachedPropChanged( .... )
{
// I WANT TO GET HERE FIRST
}
propa .....
OnAnotherAttachedBooleanPropChanged( .... )
{
// I WANT TO GET HERE SECOND
}
}
Currently i'm reaching AnotherAttachedBooleanPropChanged before OnSomeAttachedPropChanged
is there any way to control to order in which Dependency Properties are updated ?
Edit :
I just remembered/realized something , the DP with a direct assignment will get updated before the bound one .
I don't know of any direct way to do that. And i personally never had the desire to do so. When you only use the OnXYZChanged methods to register to events (which is imo the most common case) you could register to an event which better suits the order of your desired code execution. For example, in the OnXYZChanged register to Loaded event and handle that, the order of these should be afair from Root to child, and therefore in the correct order.
Another way would be to test which one was called second, and do your logic in there.
Another idea would be to use the Dispatcher and do all the logic at a later time where its sure that everything is loaded.
And a final idea, You could traverse with the VisualTreeHelper from the child element in the OnXYZChanged to the underlying window and set the property on the window first and then handle the logic for the child.
#Your edit:
If you think about it, it makes sense. The visual tree must be created and everything must be chained before the Binding can traverse the tree to find the appropiate DataContext.
My application has a set of base classes derived from almost all the UI controls
XCombobox,XButton,XMenu etc. All these classes have some additional functionality apart from standard functionality (ex: Serialization capability)
I use all these controls in my test windows application.
I want to implement a concept similar to themes in my application.
Ex: Say if i need a blue background
I have a public property exposed in all controls which sets my controls
(i.e XButton and XMenu) backcolor to blue
I feel this approach is lame. Tomorrow if my manager wants some other styling.
I will have to go to every control and change the backcolor one by one ...not that i mind ;)....but this never makes me a good programmer.
Time for some application of a design pattern (strategy pattern shall i say ?)
Is there a possibility wherein i can just change in one central place and the property shall get applied to all of my UI controls ?
If yes... Please help me in realising the same.
If not ... Suggestions for some more better ideas are welcome.
Define a class that has properties for the colors (you can also add in properties for logos etc).
Then either have your constants in the code of that class, or read them in from some XML.
You answered your question already.
Define constants for styling in central properties, and use these styling constants in your UI components :).
I would use a dependency injection container such as Unity for this, it will allow you to register you theme in a single location and have all of your controls resolve the theme whenever they need it.
For example, implement a simple interface which has the properties of the theme that each control will use.
interface IXTheme {
Color BackColor { get; }
}
Wherever your control needs to fetch this, such as in OnPaintBackground, you make a simple call to fetch the color, using the CommonServiceLocator (This comes packaged with Unity already.)
protected override void OnPaintBackground(PaintEventArgs pevent)
{
Color backColor = ServiceLocator.Current.GetInstance<IXTheme>().BackColor;
///...
}
Now, you can create you theme somewhere else based on that interface.
class XBlueTheme : IXTheme {
Color IXTheme.BackColor { get { return Color.Blue; } }
}
You then register the theme with the container and use the container to resolve your form.
IUnityContainer container = new UnityContainer()
.RegisterType<IXTheme, XBlueTheme>();
IServiceLocator locator = new UnityServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => locator);
XForm myForm = container.Resolve<XForm>();
From what you're saying, it seems like if you just want to change the color, the easiest thing would just be to save the color in a variable somewhere, or have some kind of initialization function that every component calls where you can specify shared things like the color.
Also, since you mentioned that you're adding additional functionality to components, you may want to use a Decorator design pattern for that purpose.
You can make something like .skin file provided in .net web projects. Or just an xml file will do; For instance,
Make a .skin file and add your controls and styling definitions there.
Add the .skin file path in some public property of your control's base class.
Add an event OnDraw, the all the controls override and color itself with the color defined in .skin.
Better try utilizing the existing .skin file; though I am not sure how would you plan to parse the .skin file.
<TextBox BackColor="Red" ForeColor="Blue"/>
<Button BackColor="White" ForeColor="Green"/>
<DropDownList BackColor="Cyan" ForeColor="Pink"/>
You can even extend the skin file by adding the cssClass property; and add styling according to css class defined in the css file.
<TextBox cssClass="TextLoginField"/>
<DropDownList cssClass="ComboLongList" />
Just thinking out loud! Btw, this article might give you an insight.
I guess the strategy pattern that you've mention fits the best.
That book Head Fisrt Design patterns describes this issue very well.
you need an interface say IStyle defined as a member of your base class so that the style behavior will be independent of your control inheritance.
I am working on a Customer Server Control that extends another control. There is no problem with attaching to other controls on the form.
in vb.net: Parent.FindControl(TargetControlName)
I would like to pass a method to the control in the ASPX markup.
for example: <c:MyCustomerControl runat=server InitializeStuffCallback="InitializeStuff">
So, I tried using reflection to access the given method name from the Parent.
Something like (in VB)
Dim pageType As Type = Page.GetType
Dim CallbackMethodInfo As MethodInfo = pageType.GetMethod( "MethodName" )
'Also tried
sender.Parent.GetType.GetMethod("MethodName")
sender.Parent.Parent.GetType.GetMethod("MethodName")
The method isn't found, because it just isn't apart of the Page. Where should I be looking? I'm fairly sure this is possible because I've seen other controls do similar.
I forgot to mention, my work-around is to give the control events and attaching to them in the Code-behind.
If you want to be able to pass a method in the ASPX markup, you need to use the Browsable attribute in your code on the event.
VB.NET
<Browsable(True)> Public Event InitializeStuffCallback
C#
[Browsable(true)]
public event EventHandler InitializeStuffCallback;
Reference:
Design-Time Attributes for Components and BrowsableAttribute Class
All the events, properties, or whatever need to be in the code-behind of the control with the browsable attribute to make it so you can change it in the tag code.
Normally you wouldn't need to get the method via reflection. Inside your user control, define a public event (sorry I do not know the vb syntax so this will be in c#)
public event EventHandler EventName;
Now, inside your aspx page, or whatever container of the user control, define a protected method that matches the EventHandler:
protected void MyCustomerControl_MethodName(object sender, EventArgs e) { }
Now, inside your markup, you can use
<c:MyCustomerControl id="MyCustomerControl" runat=server OnEventName="MyCustomerControl_MethodName">
Your workaround is actually the better answer. If you have code that you must run at a certain part of your control's lifecycle, you should expose events to let the container extend the lifecycle with custom functionality.
buyutec and Jesse Dearing both have an acceptable answer.
[Browsable(true)]
lets you see the property in the Properties window. However, the event doesn't show up, which makes no difference to me.
The thing I overlooked earlier was the fact that when you reference a control's even from the tag, it prep-ends On.
Every ASP.NET page is class of its own inherited from Page as in:
class MyPage : Page
Therefore, to find that method via Reflection, you must get the correct type, which is the type of the page class that stores the page code.
I suppose you need to support multiple pages for this control to be instantiated in I believe you can find the child type of any instance of Page via Reflection, but I do not remember how, but you should be able to do it.
but... like everyone else has said, such case is what events are for.