Team Foundation Server plugin WorkItemChangedEvent - c#

I am trying to create a TFS plugin that binds to the WorkItemChangedEvent and prevents the change based on some rules I will implement later. I have found some example code online and this is what I've got so far, however I would expect this to prevent all changes to work items but it doesn't seem to have any effect. There is no errors in the event viewer for TFS.
public class CwoWorkItemChangedEventHandler : ISubscriber
{
public Type[] SubscribedTypes()
{
return new[] { typeof(WorkItemChangedEvent) };
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType,
object notificationEventArgs, out int statusCode, out string statusMessage,
out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
return EventNotificationStatus.ActionDenied;
}
public string Name
{
get { return "CwoWorkItemChangedEventHandler"; }
}
public SubscriberPriority Priority
{
get { return SubscriberPriority.High; }
}
}
}

The work item changed event is not a decision out and you can't deny it.
But the time that you have the event it has already happened. Only some events have decision points.

Related

Change value of property depending another in class

I have a class named ValidationsResult with this properties:
public class ValidationsResult
{
public bool IsValid { get; set; }
public string[] Errors { get; set; }
public void AddError(string error)
{
Errors.Append(error);
}
}
But I want that the property IsValid to be read only, and depending if the object has Errors or not modify that property automatically.
How can I do that?
public class ValidationsResult
{
public bool IsValid { get => Errors != null && Errors.Length == 0; } // no errors = valid
public string[] Errors { get; set; }
public void AddError(string error)
{
Errors.Append(error);
}
}
That will make it readonly and it will tell you if you have errors
Based on the comment, yes. Better if you designed it in the following fashion.
public class ValidationsResult
{
public bool IsValid { get => Errors.Count == 0; } // or !Errors.Any()
public List<string> Errors { get; } = new List<string>();
public void AddError(string error)
{
Errors.Add(error);
}
}
You initialize the errors but outside consumer can still use it. Hence - next evolution
public class ValidationsResult
{
private List<string> _errors = new List<string>(); // private member
public bool IsValid { get => _errors.Count == 0; } // or !Errors.Any()
public string[] Errors { get => _errors.ToArray(); }
public void AddError(string error)
{
_errors.Add(error);
}
}
Now you encapsulating your error collection not letting consumer to modify it directly, but via AddError
As an answer is already posted and accepted, I just want to point to a different thing.
Never implement this type of mechanism in a class! Keep classes as simple as possible.
Please use classes as POCOs, and implement this type of logic in a different layer of the application (e.g. Business Logic layer).
Otherwise, over time, your application will become complex and convoluted and hence hard to maintain.

Property edit style as DropDown in a VS .NET custom component

I'd like to use the functionality of a ComboBox as edit option for a var in the properties window of a custom control / component. Not the ComboBox component itself.
As example:
private string[] _stringArray = { "string0", "string1" };
public string[] StringArray
{
get { return _stringArray; }
//callback
//set { _stringArray = value; }
}
As you know this will give me the object browser as view/edit option in the property window.
Funny thing that I can edit the values even with no setter.
In my researches I found out that it is possible ("UITypeEditorEditStyle.DropDown"). But I have no idea how to implement that.
Or what [Instructions] I could set for the "StringArray".
My final goal is a copy of the object selector drop-down of visual studio as a property parameter:
With custom event handling of course. But as you see I'm far away to realize that. :(
I have been looking for a tutorial on the following topics for a long time:
[Designer] instructions reference
A basic tutorial how to manage the display style of properties ✔
However I'm tired of my unsuccessful researches. Some good links are always welcome.
UPDATE:
After I more or less understood the principle (from the link in the comments, thanks) I came to an interim solution.
I realized that I need at least an int var to set a selected `index`. I thought / hoped that VS can do this automatically. Like it does with enums. And my lack of knowledge concerning [Instructions].
I could define a string variable as a placeholder for the selected index of the array in order to do without the TypeConverter, but that would make even less sense. I really don't need another abstract variable for nothing.
So the basis drop-down, which e.g. can display enums directly, does not appear to be applicable. So they use a trick with "UITypeEditorEditStyle.DropDown", which actually isn't a drop-down. It's just a button where you can place the control of your choice. In my case a ListView. Since the "drop" of the "down" already exists. Looks like cheating. ;)
//...
[TypeConverter(typeof(StringArrayConverter))]
public interface IStringArray
{
int SelectedIndex { get; set; }
string[] StringArray { get; set; }
}
public class DropDownStringArray : IStringArray
{
private string[] _stringArray = { "string0", "string1", "string2", "string3", "string4", "string5", "string6" };
public int SelectedIndex { get; set; }
public string[] StringArray
{
get { return _stringArray; }
set { _stringArray = value; }
}
}
private DropDownStringArray _ddsa = new DropDownStringArray();
[Editor(typeof(StringArrayTypeEditor), typeof(UITypeEditor))]
public DropDownStringArray MyDropDownStringArray
{
get { return _ddsa; }
set { _ddsa = value; }
}
//...
public class StringArrayConverter : TypeConverter
{
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
var sa = value as IStringArray;
if (sa != null) { return sa.StringArray[sa.SelectedIndex]; }
}
return "(none)";
}
}
public class StringArrayTypeEditor : UITypeEditor
{
private IWindowsFormsEditorService _editorService;
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; }
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
_editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
DropDownStringArray ddsa = (DropDownStringArray)value;
ListBox lb = new ListBox();
lb.SelectionMode = SelectionMode.One;
for (int i = 0; i < ddsa.StringArray.Length; i++) { lb.Items.Add(ddsa.StringArray[i]); }
lb.SetSelected(ddsa.SelectedIndex, true);
lb.SelectedValueChanged += OnListBoxSelectedValueChanged;
_editorService.DropDownControl(lb);
if (lb.SelectedItem != null) { ddsa.SelectedIndex = lb.SelectedIndex; }
return value;
}
private void OnListBoxSelectedValueChanged(object sender, EventArgs e)
{
_editorService.CloseDropDown();
}
}
Which actually copy the entire class just to change the SelectedIndex. The right thing would be to abuse the SelectedIndex and convert it to a string or something like that. I think I do not care about that anymore. Rather to catch some fresh air. ;)
Maybe that will help someone else.
Note: This is not a practical propose. As example SelectedIndex will not be updated if you change the (length) of the array. I've choosen string[] because it's a really basic and well known type. I am aware that my "program" has no real use. It was just about understanding the principle.

C# PowerShell module converting PSObject to C# Class

I'm writing my first PS Module in C# over the past few days and this is extremely new to me. I had some trouble coming up with this to be honest (maybe not enough caffeine) but I've come to a solution now however I'm hoping someone may have some more elegant ideas?
i'm finding some of the documentation regarding powershell to be a little vague, at least with regard to creating modules in C#. Embedding C# in PowerShell, no problem, running powershell code within C#, also tons of information, but information on writing PS modules in C# seems scarce, or I'm looking in the wrong places?
Enough chatter, here's my situation. First, I have a quick and dirty new and get cmdlet sample below.
[Cmdlet(VerbsCommon.New, "TestItem")]
[OutputType(typeof(TestItem))]
public class NewItem : Cmdlet
{
[Parameter(Position = 0)]
public string FriendlyName
{
get { return friendlyname; }
set { friendlyname = value; }
}
private string friendlyname;
[Parameter(Position = 1)]
public string Name
{
get { return name; }
set { name = value; }
}
private string name;
[Parameter(Position = 2)]
public int ID
{
get { return id; }
set { id = value; }
}
private int id;
private TestItem item;
protected override void BeginProcessing()
{
item = new TestItem();
}
protected override void ProcessRecord()
{
item.Name = name;
item.FriendlyName = friendlyname;
item.ID = id;
}
protected override void EndProcessing()
{
WriteObject(item);
}
}
[Cmdlet(VerbsCommon.Get, "TestItem")]
[OutputType(typeof(TestItem))]
public class GetItem : Cmdlet
{
[Parameter(Position = 0)]
public string[] FriendlyName
{
get { return friendlyname; }
set { friendlyname = value; }
}
private string[] friendlyname;
[Parameter(Position = 1)]
public List<TestItem> Item { get; set; }
[Parameter(ValueFromPipeline = true)]
public PSObject InputObject
{
set { inputObject = value; }
get { return inputObject; }
}
private PSObject inputObject;
private List<TestItem> item;
protected override void BeginProcessing()
{
item = new List<TestItem>();
}
protected override void ProcessRecord()
{
WriteVerbose("processing pipline");
if (inputObject != null)
{
WriteObject(inputObject.ToClassObject<TestItem>());
}
}
protected override void EndProcessing()
{
WriteObject(item);
}
}
Then I have my quick and dirty sample Object class
public class TestItem
{
public TestItem()
{ }
public string Name { get; set; }
public string FriendlyName { get; set; }
public int ID { get; set; }
}
And now this is where I'm looking for feedback. From the above we can see that the new-item creates an item, and when passed to get-item via the pipeline it's passed as a PSObject. My goal is to turn it back into the class object it started as, but I need to be able to handle Class/type dynamically as I intend to use this as a helper for a project I'm working on. I'm really just looking for feedback as I feel like there's a more elegant solution here?
public static class Helper
{
public static T ToClassObject<T>(this PSObject pso) where T : class, new()
{
try
{
T obj = new T();
foreach (PSPropertyInfo psPropertyInfo in pso.Properties)
{
foreach (var prop in obj.GetType().GetProperties())
{
try
{
if (psPropertyInfo.Name == prop.Name)
{
PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
propertyInfo.SetValue(obj, Convert.ChangeType(psPropertyInfo.Value, propertyInfo.PropertyType), null);
}
}
catch
{
continue;
}
}
}
return obj;
}
catch
{
return null;
}
}
}
OK, what you are working on is called binary modules / binary cmdlets, that probably a word for googling (it gets me a good number of articles about that). You can also find a bunch of real life examples on Powershell Core github, for example here.
From the other side it doesn't seem to be a very common way of creating cmdlets for powershell though, and in my opinion are much more complicated than pure PS code.
Regarding to your question, as I understand your Get-Item cmdlet is getting PSObject from the pipeline, and you need to convert it to custom type using input property names. If so you probably can use ValueFromPipelineByPropertyName property instead of ToClassObject. I'm not any kind of expert in C# or binary module writing, so I'll give my example in pure PS, I hope it's not a huge problem to convert it to C#
function Get-Column { [CmdletBinding()]param(
[parameter(ValueFromPipelineByPropertyName=$true)][String]$columnName,
[parameter(ValueFromPipelineByPropertyName=$true)][String]$type
)
Process { Write-Output (New-Object System.Data.DataColumn #($columnName, $type))}
}
$ObjectList = #(
New-Object psobject -Property #{columnName="FirstName"; type="String"}
New-Object psobject -Property #{columnName="LastName"; type="String"}
New-Object psobject -Property #{columnName="Age"; type="Int32"}
)
$ObjectList | Get-Column | Format-Table
So that cmdlet collects parameter values from input object and pass it to your custom class constructor. Also, I don't really need Begin and End blocks, probably those are redundant in your code as well.
As Mike Twc says, you are creating binary cmdlets. They are less frequently used and I, personally, think it is a shame because I see so many "PowerShell" scripts that look like C#, and binary cmdlets allow for native, more strongly-typed use in other C# applications. So kudoos for trying.
I think your major problem is what type you are specifying where. For elegance, you really don't need to be so verbose in the way you write your properties.
So here is a more elegant re-write that should solve your issue:
[Cmdlet(VerbsCommon.New, "TestItem")]
[OutputType(typeof(TestItem))]
public class NewItem : Cmdlet
{
[Parameter(Position = 0)]
public string FriendlyName { get; set; }
[Parameter(Position = 1)]
public string Name { get; set; }
[Parameter(Position = 2)]
public int ID { get; set; }
protected override void ProcessRecord()
{
var item = new TestItem();
item.Name = Name;
item.FriendlyName = Friendlyname;
item.ID = ID;
WriteObject(item);
}
}
[Cmdlet(VerbsCommon.Get, "TestItem")]
[OutputType(typeof(TestItem))]
public class GetItem : Cmdlet
{
// what is the point of this parameter since it is never used?
// Is it supposed to filter? If so, I would suggest using
// `Where-Object FriendlyName -In "MyName1","MyName2","MyName3"`
// instead of trying to write your own...
[Parameter(Position = 0)]
public string[] FriendlyName { get; set; }
// This parameter is unused and, likely, is what you
// intend for the InputObject parameter
//[Parameter(Position = 1)]
//public List<TestItem> Item { get; set; }
// Whatever this type is, because it is ValueFromPipeline,
// is what the pipeline input will be converted to
[Parameter(ValueFromPipeline = true)]
public TestItem InputObject { get; set; }
private List<TestItem> items;
protected override void BeginProcessing()
{
items = new List<TestItem>();
}
protected override void ProcessRecord()
{
WriteVerbose("processing pipline");
if (InputObject != null)
{
// do you actually mean `items.add(InputObject);`?
WriteObject(InputObject);
}
}
protected override void EndProcessing()
{
// this is empty because it never has elements added?
WriteObject(items, enumerateCollection: false);
}
}
Now another comment: The Get- verb, and you can see this in MS Doc, should act similarly to the New- verb in that it gets the instances already in the system, but is different in that it does not instantiate new instances. I am not fully sure what you are trying to do, since you seem to hand off the input object to later in the pipeline, which it otherwise would already be going to without the cmdlet--unless you are doing what my comments suggest and gathering them all into a list, and outputing the list

Error Reporting

I have a class that follows the Command Pattern.
It has 2 methods which are Execute, and CanExecute which checks whether to invoke Execute or not (they derive from ICommand).
CanExecute invokes a few methods that check that all required services are running, the version is correct, etc.
After CanExecute is invoked, it may fail and return false and I need to know why. Is it because of a bad version, services, missing file, etc.
What is the best strategy to know what is the problem
One option is whenever a required condition fails I can throw an exception that will describe the error in the message field. However the possibility that it will fail is expected and you shouldn't use exceptions for regular flow of control. So I'm really not sure.
Thank you.
You can use a collection of "reasons" that will tell the users of the class why CanExecute returned false. The reasons can be a simple IEnumerable<string>.
public bool CanExecute() {
var messages = new List<string>();
if (!Condition1) {
messages.Add("Missing Condition1");
}
...
Messages = messages;
return messages.Count == 0;
}
public IEnumerable<string> Messages { get; private set; }
Then, client code can show the collection of messages to end-users.
UPDATE:
You can also associate new commands with the messages to give the users ways to fix the problems found. In this case, instead of an IEnumerable<string>, you can create your own class that encapsulates that information:
public class Message {
public string Text { get; set; }
public ICommand Command { get; set; }
}
...
public bool CanExecute() {
var messages = new List<Message>();
if (!Condition1) {
messages.Add(
new Message {
Text = "Missing Condition1",
Command = new FixCondition1Command()
}
);
}
...
Messages = messages;
return messages.Count == 0;
}
public IEnumerable<Message> Messages { get; private set; }
UPDATE: Reworked based on feedback.
Since the UI needs the reasons CanExecute() returns false, two things come to mind:
Option 1: Add an enumerable message property to the command interface and populate it as needed during the call to CanExecute(). The UI could then interrogate the property as needed. If you go this route, make sure you clear out the contents of the property each call to CanExecute() so you don't lose track of state.
public interface ICommand
{
IEnumerable<string> Messages { get; }
bool CanExecute();
void Execute();
}
public class SomeCommand : ICommand
{
public IEnumerable<string> Messages { get; private set; }
public bool CanExecute()
{
var messages = new List<string>();
var canExecute = true;
if (SomeCondition)
{
canExecute = false;
messages.Add("Some reason");
}
if (AnotherCondition)
{
canExecute = false;
messages.Add("Another reason");
}
Messages = messages;
return canExecute;
}
public void Execute() { }
}
Option 2: Have CanExecute() return an object which contains the bool as well as an enumerable messages property. This makes it obvious that the messages only apply to that call of CanExecute(). However, depending on where/how you're implementing (e.g. data binding), this could complicate other scenarios more than you're looking for.
public class CanExecuteResult
{
public bool CanExecute { get; set; }
public IEnumerable<string> Messages { get; set; }
}
public interface ICommand
{
CanExecuteResult CanExecute();
void Execute();
}
public class SomeCommand : ICommand
{
public CanExecuteResult CanExecute()
{
var result = new CanExecuteResult { CanExecute = true };
var messages = new List<string>();
if (SomeCondition)
{
result.CanExecute = false;
messages.Add("Some reason");
}
if (AnotherCondition)
{
result.CanExecute = false;
messages.Add("Another reason");
}
result.Messages = messages;
return result;
}
public void Execute() { }
}
Obviously, the specifics of how you want to handle the interfaces, enumerable types, etc. is up to you. The code is just a representation of the idea.
Bool CanExecute()
{
if(!CheckXXX)
throw new Exception("CheckXXX function throws an exception")
if(!CheckYYY)
throw new Exception("CheckYYY function throws an exception")
if(!CheckZZZ)
throw new Exception("CheckZZZ function throws an exception")
return true; //everything is working fine
}

How to sort a DataGridView that is bound to a collection of custom objects?

So I have been following this guide for data binding on Windows Forms controls (MAD props to the author, this guide is great), and I have used this to create a custom class and bind a DataGridView to a collection of this class:
class CompleteJobListEntry
{
private string _jobName;
private Image _jobStatus;
private DateTime _jobAddedDate;
private string _jobAddedScanner;
private string _jobAddedUser;
private string _jobLastActivity;
private DateTime _jobActivityDate;
private string _jobActivityUser;
public string JobName { get { return _jobName; } set { this._jobName = value; } }
public Image JobStatus { get { return _jobStatus; } set { this._jobStatus = value; } }
public DateTime JobAddedDate { get { return _jobAddedDate; } set { this._jobAddedDate = value; } }
public string JobAddedScanner { get { return _jobAddedScanner; } set { this._jobAddedScanner = value; } }
public string JobAddedUser { get { return _jobAddedUser; } set { this._jobAddedUser = value; } }
public string JobLastActivity { get { return _jobLastActivity; } set { this._jobLastActivity = value; } }
public DateTime JobActivityDate { get { return _jobActivityDate; } set { this._jobActivityDate = value; } }
public string JobActivityUser { get { return _jobActivityUser; } set { this._jobActivityUser = value; } }
}
At this point, I import a bunch of data from various SQL databases to populate the table, and it turns out great. The guide even provides an excellent starting point for adding filters, which I intend to follow a bit later. For now, though, I am stuck on the sorting of my newly generated DataGridView. Looking around, I've discovered that the DataGridView has its own Sort method, usable like:
completeJobListGridView.Sort(completeJobListGridView.Columns["JobName"], ListSortDirection.Ascending);
However, when I try to do this, I get an InvalidOperationException that tells me "DataGridView control cannot be sorted if it is bound to an IBindingList that does not support sorting." I've found both the IBindingList and IBindingListView interfaces, but making my class inherit either of these interfaces doesn't solve the problem.
How do I do this? I am completely stuck here...
If your data is in a collection, you should be able to use the BindingListView library to easily add sorting capabilities to your DGV. See How do I implement automatic sorting of DataGridView? and my answer to How to Sort WinForms DataGridView bound to EF EntityCollection<T> for more information and code snippets.

Categories

Resources