Need to avoid asserting for custom properties for MS controls - c#

I have prepared the simple sample(WindowsForms) with custom control(ButtonAdv) which derived from Microsoft Button control.And implemented CodedUI Extension support for that custom control with my custom properties(BackColor and ForeColor,etc) by refferring the below blog,
https://blogs.msdn.microsoft.com/devops/2011/10/28/coded-ui-test-extension-for-3rd-party-windows-forms-controlshow-to/
All is working fine, when asserting my custom control the custom properties are added properly in CodedUITestBuilder property window.
Problem:
When i try to assert the Microsoft Button, there also my custom properties(BackColor,ForeColor) are displayed with empty values in CodedUITestBuilder property window.
I don't know why it is. Please anyone suggest me how to avoid of asserting custom properties for MicroSoft controls?
Here is minimum code of my implementation,
public class SfButtonAdv1:WinControl
{
public SfButtonAdv1(UITestControl control)
: base(control)
{
SearchProperties.Add(UITestControl.PropertyNames.ControlType, ControlType.Button.Name);
}
new public abstract class PropertyNames : WinControl.PropertyNames
{
public static readonly string BackColor = "BackColor";
public static readonly string ForeColor = "ForeColor";
}
}
public class SfButtonAdv : Button
{
public SfButtonAdv()
{
}
protected override AccessibleObject CreateAccessibilityInstance()
{
return new SfButtonAccessiblity(this);
}
}
public class SfButtonAccessiblity : ControlAccessibleObject
{
//
public override string Description
{
get
{
return this.sfButtonAdv.Style.BackColor.Name
+ "," + this.sfButtonAdv.Style.ForeColor.Name
}
}
}
Please let me know if you need any details further,
Thanks in Advance,

Related

How to Create such a Task Panel?

In Visual Studio 2008,
If you create a Form and put a Control on it,
you can edit the control's properties via the Properties window.
Some controls enable changing their properties in another way,
in addition to the Properties window.
It looks like this:
It seems that all controls that has this pane, has it in the same style,
meaning it's something that is provided by Visual Studio,
and the maker of the control just chooses the items to include inside,
like Fields, and Clickable Links that open some windows.
So my question:
What is the name of this pane control,
and how do I create one?
That menu is called Smart Tags or Designer Actions and you can add smart tag to your control. To do so, you need to create a custom Designer for your control and in the designer, override its ActionLists property.
Example
Let's say we have created a control having some properties, and we want to show the following properties of out control in smart tags window:
public Color SomeColorProperty { get; set; }
public string[] Items { get; set; }
And the expected result for us is:
MyControl
Here we decorate the control with Designer attribute to register the custom designer:
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[Designer(typeof(MyControlDesigner))]
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
void InitializeComponent() { }
public Color SomeColorProperty { get; set; }
public string[] Items { get; set; }
}
MyControlDesigner
Here we override ActionLists and return a new DesignerActionListCollection containing the action list items which we need:
public class MyControlDesigner : ControlDesigner
{
private DesignerActionListCollection actionList;
public override DesignerActionListCollection ActionLists
{
get
{
if (actionList == null)
actionList = new DesignerActionListCollection(new[] {
new MyControlActionList(this) });
return actionList;
}
}
}
MyControlActionList
Here we create properties which get/set out control properties. Also we create methods which are responsible to show custom editor for some properties or do some actions. Then return a list of action items by overriding GetSortedActionItems:
public class MyControlActionList : DesignerActionList
{
ControlDesigner designer;
MyControl control;
public MyControlActionList(ControlDesigner designer) : base(designer.Component)
{
this.designer = designer;
control = (MyControl)designer.Control;
}
public Color SomeColorProperty
{
get { return control.SomeColorProperty; }
set {
TypeDescriptor.GetProperties(
(object)this.Component)["SomeColorProperty"]
.SetValue((object)this.Component, (object)value);
}
}
public void EditItems()
{
var editorServiceContext = typeof(ControlDesigner).Assembly.GetTypes()
.Where(x => x.Name == "EditorServiceContext").FirstOrDefault();
var editValue = editorServiceContext.GetMethod("EditValue",
System.Reflection.BindingFlags.Static |
System.Reflection.BindingFlags.Public);
editValue.Invoke(null, new object[] { designer, this.Component, "Items" });
}
public override DesignerActionItemCollection GetSortedActionItems()
{
return new DesignerActionItemCollection() {
new DesignerActionMethodItem(this, "EditItems", "Edit Items", true),
new DesignerActionPropertyItem("SomeColorProperty", "Some Color"),
};
}
}
For more information about this topic, take a look at this MSDN Walkthrough.
Download Example
You can download a working example from the following repository:
r-aghaei/ControlSmartTagsExample
Zip File
This is called 'DesignerActionList' or SmartTag. Smart tags are menu-like user interface (UI) elements that supply commonly used design-time options.
Step:
You must add a reference to the design-time assembly, System.Design.dll
Create DesignerActionList class and get the reference to control in the constructor.
public class MyControlTasks : System.ComponentModel.Design.DesignerActionList
{
private MyControl myControl;
private DesignerActionUIService designerActionUISvc = null;
public MyControlTasks( IComponent component ) : base(component)
{
this.myControl = component as MyControl;
this.designerActionUISvc =
GetService(typeof(DesignerActionUIService))
as DesignerActionUIService;
}
}
Add methods and properties that you want to associate to smart-tag items
Create base designer for the control
public interface IDesigner {
void Dispose();
void Initialize(IComponent component);
IComponent Component {
get;
}
}
Return a new instance of the MyControlTasks class that you created earlier.
public override DesignerActionListCollection ActionLists
{
get
{
var actionLists = new DesignerActionListCollection();
actionLists.Add(new MyControlTasks(this.Component));
return actionLists;
}
}

Get the current filename from a Visual Studio text adornment extension

I'm new to VS extension development. I'm currently working with the text adornment sample in VS 2015 and have been able to get coloured boxes showing correctly. Now I want to extend the sample so the adornment only appears on certain file names.
Googling has said I can use ITextDocumentFactoryService.TryGetTextDocument interface with the IWpfTextView.TextBuffer property to get a filename. This sounds great. But I can't seem to actually get the interface.
In my class I have:
[Import]
public ITextDocumentFactoryService TextDocumentFactoryService = null;
But it is always NULL.
How can I get ITextDocumentFactoryService?
namespace Test
{
internal sealed class TestAdornment
{
[Import]
public ITextDocumentFactoryService TextDocumentFactoryService = null;
public TestAdornment(IWpfTextView view)
{
}
/// <summary>
/// Adds the scarlet box behind the 'a' characters within the given line
/// </summary>
/// <param name="line">Line to add the adornments</param>
private void CreateVisuals(ITextViewLine line)
{
// TextDocumentFactoryService is NULL
}
}
}
TextAdornmentTextViewCreationListener.cs
[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class TextAdornmentTextViewCreationListener : IWpfTextViewCreationListener
{
[Import]
public ITextDocumentFactoryService textDocumentFactory { get; set; }
//...
public void TextViewCreated(IWpfTextView textView)
{
new TextAdornment(textView, textDocumentFactory);
}
}
TextAdornment.cs
internal sealed class TextAdornment
{
private readonly ITextDocumentFactoryService textDocumentFactory;
private ITextDocument TextDocument;
//...
public TextAdornment(IWpfTextView view, ITextDocumentFactoryService textDocumentFactory)
{
//...
this.textDocumentFactory = textDocumentFactory;
//...
}
internal void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
var res = this.textDocumentFactory.TryGetTextDocument(this.view.TextBuffer, out this.TextDocument);
if (res)
{
//this.TextDocument.FilePath;
}
else
{
//ERROR
}
}
}
You got it via dependency injection.
As you only submitted 2 lines of code I suppose your context is set up, either explicitly by you, either implicitly by some environment who calls your code.
You should declare property instead of field
It should be public
Then automagically big brother will set it for you before you first access to it.
...or...
You can use constructor injection instead. Note: It is not you who will create your class.
private readonly ITextDocumentFactoryService _textDocumentFactoryService;
[ImportingConstructor]
internal YourClass(ITextDocumentFactoryService textDocumentFactoryService)
{
_textDocumentFactoryService = textDocumentFactoryService;
}
So in my case I needed to put the import statement into the AdornmentTextViewCreationListener. This implements IWpfTextViewCreationListener and is the one with the following decorating the class.
[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
Then I can add
private readonly ITextDocumentFactoryService _textDocumentFactoryService;
[ImportingConstructor]
to my class

ASP.NET Custom WebControl Nested Properties as Object

I am trying to implement a Custom Composite WebControl with "nested" properties, i.e., encapsulate a group of properties into a class.
For example, in this composite control, I have placed a button. I would like to be able to encapsulate relevant properties for the button into a class (e.g., buttonText, buttonStyle, etc.). This would make defining properties in multi-button/controls composite-control easier and consistent and intuitive.
Note: I would like for the encapsulated properties to appear grouped in the Properties dialog in VisualStudio, in a manner very similar to Style/Font.
Sample:
public class fooButtonProperties
{
[Category("Appearance"), Description("URL for the Profile page")]
public string URL { get; set; }
[Category("Appearance"), Description("Text to display"), DefaultValue("Profile")]
public string ButtonText { get; set; }
/// <summary>
/// Position of the control on the page, default is Right-Aligned
/// </summary>
[Category("Appearance"), Description("Position in the Header"), DefaultValue(PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions.Right)]
///Here is the composite control
public PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions PositionInHeader { get; set; }
}
public class myCustomClass: System.Web.UI.WebControls.CompositeControl
{
protected System.Web.UI.HtmlControls.HtmlLink myButton;
[Category("Appearance")]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
private fooButtonProperties _myeButtonProp;
#region Constructor
public myCustomClass()
{
this._myeButtonProp = new fooButtonProperties ();
}
#endregion
}
Unfortunately, this approach dos not work. The new property myButtonProperties does not appear at all in the "Properies" dialog.
To create a nested property use the System.ComponentModel.DesignerSerializationVisibility attribute in your control like this:
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
The final property name will be "myButtonProperties-URL" (with a hyphen). You can also add this attribute to properties in your fooButtonProperties class for even more nesting.
Please note that you may have to close the aspx file and rebuild the solution to refresh the Properties window.
The Category attribute works in your control and in your nested class.
The Description attribute for the descriptions seems correct BUT it does not work which could be a bug in Visual Studio. I found this link:
https://www.beta.microsoft.com/VisualStudio/feedback/details/653335/webcontrol-property-descriptions-do-not-appear-in-property-window
Also I observed that no properties show descriptions.
Regards
Oli

Workaround for generic event handler in Windows Forms

Quite some time ago, I noticed that the Windows Forms editor of Visual Studio does not support events which contain generic type parameters. For example, an event like
public event EventHandler<ListEventArgs<int>> MyStrangeEvent { add { ... } remove { ... } }
where
public class ListEventArgs<T> : EventArgs { List<T> args; }
does not even show up in the event list in the property manager of Visual Studio. Now, this is a somewhat artificial example that could easily be modified to work in Visual Studio by rewriting the classes and their events. However, I am currently working on a project where I cannot change some classes for compatibility reasons. The only thing I can do is to change the events of my user control. The events of this control currently look like this:
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
Note that the underlying Plane class (represented by the _Plane instance which is a protected field) cannot be changed. Its DrawingError event and its EventArgs type are declared in the Plane class like this:
public class Plane<T> where T : ISurface
{
...
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
...
public class DrawingErrorEventArgs : EventArgs { ... /* Uses T */ ... }
}
Of course, the Windows Forms editor of Visual Studio does not show any of the events of my user control. I have been looking for a number of workarounds to get them shown again, but have not been able to find a workaround that actually works. Here are some things that I tried:
Created a MyPlane class which inherits from Plane and used that instead: public event EventHandler<MyPlane.DrawingErrorEventArgs> DrawingError .... For reasons unknown to me, the events still don't show up in the editor. Perhaps this is due to the parameters of the event, some of which still are generic. Find a minimal working example below.
Created a helper class which defines implicit conversion operators between EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> and EventHandler<GDIPlane.DrawingErrorEventArgs> where GDIPlane is just a dummy class which inherits from Plane<GDISurface>. This does work to some extent, but duplicates event calls since the conversion creates new event handlers which are passed down to _Plane which cannot be removed/unregistered properly.
Tried to inherit from EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>, which obviously does not work since EventHandler<T> is sealed.
Are there any other ways to make my events visible again in the Windows Forms editor?
Best regards
Andreas
EDIT: Minimal working example for 1:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
public class GDIPlane : Plane<GDISurface> { }
GDIPlane _Plane = null;
public event EventHandler<GDIPlane.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
DrawingError does not show up in the list of events in the property manager when clicking on a TestControl instance.
EDIT2: This is the original problem (without any workarounds) where the DrawingError event does of TestControl does not show up either:
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError = null;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
Plane<GDISurface> _Plane = null;
public event EventHandler<Plane<GDISurface>.DrawingErrorEventArgs> DrawingError { add { _Plane.DrawingError += value; } remove { _Plane.DrawingError -= value; } }
}
This is behavior specific to Visual Studio, and the cause is rooted in the fact that EventHandler<> does not specify covariance on its 'TEventArgs' (it would impose seemingly silly restrictions) and the tools do not perform enough introspection of your code to suss out an appropriate type (even though you've left a trail of type data in constructing the control.) Thus, it seems as though VS does not support generic event properties. You may consider filing a feature request on Microsoft Connect, I wouldn't suggest filing it as a bug as they may label it "by design" and close it.
As a general rule, if you need generic type parameters on your events and you need design time support for them (which are different implementation concerns), you're looking at wrapping them in a presentation-specific facade (e.g. "extra layer of code to facilitate design-time needs".)
Personally, I would reduce the generic typing you have in play now, it seems a bit excessive and if you don't understand covariance/contravariance in generic types it might put you in a tight spot at some point, such as now.
However, to work around your problem:
Consider using a custom event args class which could transport data in a non-generic property, and also use a non-generic EventHandler event/property. Understanding the 'type' of the event is then shifted away from generic type parameters and made the responsibility of your non-generic event args instead. If the 'class' of the event args is insufficient, you can add a property to convey the event type (or data type) so that receiving code can properly interpret it (assuming, of course, that it does not already know by some other means.):
public class DataEventArgs : EventArgs
{
//public string EventTypeOrPurpose { get; set; }
public object Data { get; set; }
}
This is most often only used to ferry data through an event chain, and it is usually implemented as follows:
public class DataEventArgs<T> : EventArgs
{
public T Data { get; set; }
}
Unfortunately, this also has a covariance problem, to resolve it you would actually want something more like this:
public interface IDataArgs<out T>
{
T Data { get; }
}
public class DataEventArgs<T> : EventArgs, IDataArgs<T>
{
public DataEventArgs<T>(T data)
{
_data = data;
}
private T _data;
public T Data { get { return _data; } }
}
Even so, these generic versions still don't work around Visual Studio's limitations, this is merely more proper alternative forms of what you already have shown us.
UPDATE: As requested, here is what a "purpose built facade" might look like in the most basic sense. Note that the usercontrol functions as a facade layer in this case as the eventhandler it exposes delegates to the underlying object model. There is no direct access to underlying object model from the user control (from consumer/designer perspective.)
Please note the reference tracking for event handlers is not necessary unless you dispose of these user controls throughout the lifetime of the app (it is only done to ensure proper delegate removal based on the delegate provided, which is wrapped in a closure/delegate, as you see below.)
Also worth noting I did not test-run this code beyond verifying that the designer shows DrawingError in the property grid when dropped onto a form.
namespace SampleCase3
{
public interface ISurface { }
public class GDISurface : ISurface { }
public class Plane<T> where T : ISurface
{
public event EventHandler<DrawingErrorEventArgs> DrawingError;
public class DrawingErrorEventArgs : EventArgs { T stuff; }
}
public class TestControl : UserControl
{
private Plane<GDISurface> _Plane = new Plane<GDISurface>(); // requires initialization for my own testing
public TestControl()
{
}
// i am adding this map *only* so that the removal of an event handler can be done properly
private Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>> _cleanupMap = new Dictionary<EventHandler, EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>>();
public event EventHandler DrawingError
{
add
{
var nonGenericHandler = value;
var genericHandler = (EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>)delegate(object sender, Plane<GDISurface>.DrawingErrorEventArgs e)
{
nonGenericHandler(sender, e);
};
_Plane.DrawingError += genericHandler;
_cleanupMap[nonGenericHandler] = genericHandler;
}
remove
{
var nonGenericHandler = value;
var genericHandler = default(EventHandler<Plane<GDISurface>.DrawingErrorEventArgs>);
if (_cleanupMap.TryGetValue(nonGenericHandler, out genericHandler))
{
_Plane.DrawingError -= genericHandler;
_cleanupMap.Remove(nonGenericHandler);
}
}
}
}
}
To complement the above, here is what a non-generic event handler would now look like:
private void testControl1_DrawingError(object sender, EventArgs e)
{
var genericDrawingErrorEventArgs = e as Plane<GDISurface>.DrawingErrorEventArgs;
if (genericDrawingErrorEventArgs != null)
{
// TODO:
}
}
Note that the consumer here has to have knowledge of the type for e to perform conversion. The use of the as operator will bypass ancestry checks under the assumption that the conversion should succeed.
Something like this is as close as you're going to get. Yes it is ugly by most of our standards, but if you absolutely 'need' design-time support on top of these components and you cannot change Plane<T> (which would be more appropriate) then this, or something close to this, is the only viable workaround.
HTH

Change value of control on a form from class (C#)

This should be quite simple really - not sure what the problem is.
I have a C# Class (Public.cs) and a windows form (Form1.cs). Through a function in Public.cs, I want to get the value of a control on Form1 (without having to use object parameters).
// This code appears in Public.cs
public string MyFunction(int num_val)
{
if (chk_num.checked == true)
{
// Something here...
}
}
The issue is that my class cannot find the control on my form. Is there some way that I must reference it in C#?
Thank you.
I would strongly suggest exposing the Checked property via a specific property on Form1 (perhaps with a more meaningful name). This will help to hide the implementation details (i.e. control structure) of the Form1 from it's caller and instead expose only the logic that is required for other consumers to do their job
For example:
public bool IsNumberRequested
{
get { return chk_num.Checked; }
}
Or alternatively, if you still really want to access the control directly, from the designer you can select the control and change it's Modifier property to public (or something else) enabling you to access the control object using the code you originally wrote above.
EDIT: (Response based on comment)
Public.cs will still need a reference to Form1 and then will call the IsNumberRequested property of that object.
// Public.cs
public class Public
{
private Form1 _ui;
public Public(Form1 ui) { _ui = ui };
public string MyFunction(int num_val)
{
if (_ui.IsNumberRequested)
{
// Stuff
}
// Else, default Stuff
}
}
Alternatively, you could pass the form as a parameter to the MyFunction too rather than using it as an instance variable.
I would have the set up the other way around
public class Public
{
public bool CheckNumber {get;set;}
public string MyFunction(int val)
{
if(CheckNumber)
{
//do that thing
}
return ...
}
}
public partial class Form1 : Form
{
Public myinstance = new Public();
public Form1()
{
InitializeComponent();
}
private void CheckBoxChanged(object sender, EventArgs e)
{
myinstance.CheckNumber = chk_num.checked;
}
}
You'll need to assign CheckBoxChanged to the OnChanged event handler for your check box (which I'm assuming is chk_num.
This way your class Public doesn't rely on a form, which it shouldn't.
As Reddog says, use better names, although I half suspect you've just given example names in your question.

Categories