How to pass control to a method - c#

I want to create a method that changes enabled property. How do I pass the contorl name and property to a method.
If the following were my original method:
public void ChangeProperties()
{
btnAcesScore.Enabled = true;
}
I want to be able to change the "btnAcesScore" each time I call this method. How do I pass this to the method. I tried passing it as a string but that doesn't work.
Here is what I tried:
public void ChangeProperties(string category)
{
category = true;
}
ChangeProperties("btnAcesScore.Enabled");
Susan

Try this :
public void ChangeProperties(Control ctrl)
{
ctrl.Enabled = true;
}
and call it like that :
ChangeProperties(btnAcesScore);

What exactly is the purpose of this? Is it to reuse the method to arbitrarily change the Enabled property of any given control? If so, there is an easier way to accomplish it, as outlined by Canavar.
Or is the point of this method to toggle the setting? In which case, your method would look either like:
public void ChangeProperties()
{
btnAcesScore.Enabled = !btnAcesScore.Enabled;
}
or
public void ChangeProperties(Control ctrl)
{
ctrl.Enabled = !ctrl.Enabled;
}
depending on whether you wanted to hit just the one control, or provide access to many. In any event, I personally don't see much point to encapsulating a single property access within a method, and if you were insistent (and this method didn't adjust other properties), I'd at least rename it to something like ToggleEnabled.

Since the original question had a reflection tag I think she wanted a reflection answer (whether or not that is good design) so here is a Reflection answer.
the form has a controls collection and with this you can search for it and use reflection to set the property:
public void ChangeProperties(Form form, string category)
{
string[] parts = category.Split(".");
int index = form.Controls.IndexOfKey(parts[0]);
Control control = null;
if (index >= 0)
{
control = form.Controls[index].;
}
if (control != null)
{
PropertyInfo propertyInfo = control.GetType().GetProperty(parts[1]);
if (propertyInfo != null)
{
propertyInfo.SetValue(control, true);
}
}
}
if you call it from the form the control lives on
ChangeProperties(this, "btnAcesScore.Enabled");

How about also
void ChangeProperty(ref bool output)
{
output = true;
}
ChangeProperty(ref btnAcesScore.Enabled);

Not sure I totally understand your intent, but you could pass a delegate to some code that changed your property...
public void ChangeProperties(Action<bool> setprop)
{
...
setprop(true);
}
then call it:
ChangeProperties(b => btnAcesScore.Enabled = b);

I'd use reflection - use the GetType() Method on the object you send through to your method and then use the GetProperties to match against the property you send through. You can then set the values at that point.

Try this:
public void ChangeProperties(string category, object value)
{
var categoryConcat = category.Split('.');
var control = this.Controls.Cast<Control>()
.Where(x => x.Name == categoryConcat[0]).First();
control.GetType().GetProperty(categoryConcat[1])
.SetValue(control, value);
}
The example probably needs some checks on the existance of the control and the property.

Main()
{
ChangeProperties(ref category,True); //Where Category is the ID of the Textbox control i.e <asp:textbox ID="Category "></textbox>
}
public void ChangeProperties(ref TextBox category,bool val)
{
category.Enabled = val;
}

Related

How to reset delegate to original code

given this delegate
public class XYZ
{
public static Action<Profile> DoSomething = (profile) =>
{
//some default code here
return;
};
}
at some time in my main execution I override it with this:
XYZ.DoSomething = (currProfile) =>
{
// some overriding code here
}
How do I set the code back to the original default code when I need to without duplicating code?
Here's a good reason to never use public fields...
Once you set it; its gone. You can hold onto the original value though:
var originalAction = XYZ.DoSomething;
XYZ.DoSomething = ...;
XYZ.DoSomething = originalAction;
Usually it is a bad idea to rely on client code to handle this however; so if I was writing it I would expose as a property like so:
public Action<X> DoSomethingOverride {get; set;}
public Action<X> DoSomething => doSomethingOverride ?? DefaultMethod;
private void DefaultMethod (X param)
{
}
There are a number of other ways to handle this, but all involve storing off the original method. All good ways to handle this will use a property to ensure that only the declaring class is actually setting the DoSomething method and that resetting to the default is possible.
Total aside; since this is static setting the action will affect everything that uses this class. This is asking for bugs later; don't do that.
Maybe somthing like this?
public static Action<Profile> _doSomethingBase = (profile) =>
{
//some default code here
return;
};
public static Action<Profile> _doSomething = _doSomethingBase;
public static Action<Profile> DoSomething
{
get => _doSomething;
set => _doSomething = value;
}
public static void RevertDoSomething()
{
DoSomething = _doSomethingBase;
}

C# find already existing object instance using a string

How can I the value of a string variable as an instance name?
Here is some code:
TabItem selectedTabItem = this.mainTabControl.SelectedItem as TabItem;
Type t = selectedTabItem.DataContext.GetType();
object instance = Activator.CreateInstance(t);
MethodInfo method = t.GetMethod("SelectAll");
//Here is the problem
method.Invoke(t, null);
It exists 5 different classes (UserManager.cs, GroupManager.cs, PrinterManager.cs, FontManager.cs, ...) and everyone of these classes have the functions SelectAll(), UnselectAll, RemoveSelected. Now I dont want to create something like this:
TabItem selectedTabItem = this.mainTabControl.SelectedItem as TabItem;
if (selectedTabItem.Name == "UserManager")
{
if (btnSelectAll == pressed)
{
this.UserManager.SelectAll();
}
else if (btnUnselectAll == pressed)
{
this.UserManager.UnselectAll();
}
else if (btnRemoveSelected == pressed)
{
this.UserManager.RemoveSelected();
}
}
else if (selectedTabItem.Name == "PrinterManager")
{
if (btnSelectAll == pressed)
{
this.PrinterManager.SelectAll();
}
//...
}
else if (selectedTabItem.Name == "GroupManager")
{
}
//...
Now this line here:
method.Invoke(t, null);
It works fine, but I dont want to create a new instance. I need a existing instance. Now I could write
method.Invoke(this.UserManager, null);
But I think this way is not that good, because I will need again much if/else.
I need something like this:
string instance = "this.UserManager";
method.Invoke(instance, null);
This is something that can be solved by making use of a Custom Control that extends TabItem. This custom TabItem will need a manager assigned to it.
Firstly, you'll need an interface for your manager implementations.
public interface IManager
{
SelectAll();
UnselectAll();
RemoveSelected();
}
Then of course, you'll need some implementations:
public class UserManager : IManager
{
//IManager methods
}
You'll then need a TabItem which can hold an IManager. Here's an example:
public class ManagedTabItem : TabItem
{
public IManager Manager
{
get { return (IManager)GetValue(ManagerProperty); }
set { SetValue(ManagerProperty, value); }
}
public static readonly DependencyProperty ManagerProperty =
DependencyProperty.Register("Manager", typeof(IManager), typeof(ManagedTabItem), new PropertyMetadata(null));
static ManagedTabItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ManagedTabItem), new FrameworkPropertyMetadata(typeof(ManagedTabItem)));
}
}
You may want to read up on Custom Controls, you can find a nice tutorial here.
Now, to bring this all together you simply need to replace existing TabItem with the new ManagedTabItem and assign the Manager property.
<local:ManagedTabItem Manager="{Binding UserManager}" ... />
The above method assumes you are making use of DataContext and Binding. Otherwise, you can of course do this in code behind instead:
myTabItem.Manager = UserManager;
The beauty of this method is now you don't need to check which TabItem was clicked, instead you can simply call whatever method you need to on the tab's assigned manager.
ManagedTabItem selectedTabItem = this.mainTabControl.SelectedItem as ManagedTabItem;
if (btnSelectAll == pressed)
{
selectedTabItem.Manager.SelectAll();
}
...
That was quite a large workaround, however there's nothing wrong with keeping things extensible and allowing changes to be easy.

How do you create a custom collection editor form for use with the property grid?

I am trying to incorporate a property grid control with a class that has a list/collection of another class as one of the properties. Lets call them class A and the list would be containing class B for reference.
I was wanting to incorporate a form that had two list boxes. The list box on the left would contain a list of all of class B's in my program that are not currently in the list on the right. The list on the right would contain all of the class B's that are currently associated with class A. I want buttons in between to move items between the two lists.
This would be easy to design, but I'm not sure exactly how to set up the form to be used as the collection editor.
Can anyone point me in the right direction?
And also, how can I go about having setting up a drop down for a property that contains a list of id's to select from if anyone could point me in the direction for accomplishing this as well.
Okay, I was finally able to track down how to accomplish this.
I was attempting to create a custom CollectionEditor.CollectionForm which was close to what I needed to do, but it wasn't quite the right direction.
First of all, create a regular Windows Form which includes your GUI for editing your collection. Then just include button/buttons which return a DialogResult in the Form.
Now the key to accomplishing what I was looking for is not a CollectionEditor.CollectionForm as I had thought would be the correct approach, but rather a UITypeEditor.
So, I created a class that inherited from the UITypeEditor. Then you simply flesh it out as such:
public class CustomCollectionModalEditor: UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
if (context ==null || context.Instance == null)
return base.GetEditStyle(context);
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService editorService;
if (context == null || context.Instance == null || provider == null)
return value;
editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
CForm CollectionEditor = new CForm();
if (editorService.ShowDialog(CollectionEditor) == System.Windows.Forms.DialogResult.OK)
return CollectionEditor.Programmed;
return value;
//return base.EditValue(context, provider, value);
}
}
The key parts to take note of, are the functions GetEditStyle and EditValue. The part responsible for firing-off the Form you created to edit your collection, is in the EditValue override function.
CForm is the custom collection editor form I designed in this test to edit the collection. You need to fetch the IWindowsFormsEditorService associated with the IServiceProvider and simply call the .ShowDialog(formVariable) of the IWindowsFormsEditorService in order to show the form you designed to edit the collection. You can then catch the returned DialogResult value from your Form and perform any custom handling that you require.
I hope this helps someone out as it took me quite a bit of digging to determine the right way to incorporate this.
This answers Brandon's question. I too searched long and hard on how to actually replace the default propertygrid collection editor. Nathan's answer was the final solution. Brandon here is how I was able to use Nathan's solution and use my own collection editor.
using Swfd = System.Windows.Forms.Design;
using Scm = System.ComponentModel;
using Sdd = System.Drawing.Design;
public class CustomCollectionModalEditor : Sdd.UITypeEditor
{
public override Sdd.UITypeEditorEditStyle GetEditStyle(Scm.ITypeDescriptorContext context)
{
if (context == null || context.Instance == null)
return base.GetEditStyle(context);
return Sdd.UITypeEditorEditStyle.Modal;
}
public override object EditValue(Scm.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
Swfd.IWindowsFormsEditorService editorService;
if (context == null || context.Instance == null || provider == null)
return value;
editorService = (Swfd.IWindowsFormsEditorService)provider.GetService(typeof(Swfd.IWindowsFormsEditorService));
//CForm CollectionEditor = new CForm();
//--- Replaced the Collection from this post with mine which requires an argument that passes the collection
Ccne.CustomCollection editgcp = new Ccne.CustomCollection(); // Ccne.CustomCollection is my collection
editgcp = MYGCPS; // MYGCPS is the actual instance to be edited
Gcp_Editor.GcpEditorMain CollectionEditor = new Gcp_Editor.GcpEditorMain(editgcp); // call my editor
if (editorService.ShowDialog(CollectionEditor) == System.Windows.Forms.DialogResult.OK)
{
MYGCPS = CollectionEditor.ReturnValue1; // update current instance of the collection with the returned edited collection
THISPG.Refresh(); // calls a method which refreshes the property grid
return value; // the replaces the statment in the post >> CollectionEditor.Programmed;
}
//---
return value;
//return base.EditValue(context, provider, value);
}
}
//---------- The propertygrid entry
private Ccne.CustomCollection gCPs;
[Scm.Category("3 - Optional inputs to run gdal_translate")]
[PropertyOrder(133)]
[Scm.TypeConverter(typeof(Ccne.CustomCollectionConverter))]
[Scm.Editor(typeof(CustomCollectionModalEditor), typeof(Sdd.UITypeEditor))]
[Scm.Description("The Collection of the single or multiple Ground Control Points (Gcp)" +
" entered. \n Each gcp requires a Name, pixel, line, easting, " +
"northing, and optionally an elevation")]
[Scm.RefreshProperties(Scm.RefreshProperties.All)] // http://stackoverflow.com/questions/3120496/updating-a-propertygrid
[Scm.DisplayName("23 Collection of Gcp's")]
[Scm.ReadOnly(true)] // prevents values being changed without validation provided by form
public Ccne.CustomCollection GCPs
{
get { return gCPs; }
set { gCPs = value; }
}
//-------- important code from CollectionEditor i.e. > Gcp_Editor.GcpEditorMain(editgcp)
using Swf = System.Windows.Forms;
namespace Gcp_Editor
{
public partial class GcpEditorMain : Swf.Form
{
public Ccne.CustomCollection ReturnValue1 { get; set; }
...
public GcpEditorMain(Ccne.CustomCollection input1)
{
InitializeComponent();
formcollection = input1;
}
...
private void OkayBtn_Click(object sender, EventArgs e)
{
this.DialogResult = Swf.DialogResult.OK;
ReturnValue1 = formcollection;
return;
}

.NET PropertyInfo.SetValue seemingly ignoring my commands

As the topic suggests I have some problems with PropertyInfo.SetValue. To get to the point, here is my example - I have created my own class and the main thing about it is the presentation object:
using System;
using System.Reflection;
namespace TestingSetValue
{
public class Link
{
private object presentationObject = null;
private string captionInternal = string.Empty;
public Link (string caption)
{
captionInternal = caption;
}
public string CaptionInternal
{
get { return captionInternal; }
set { captionInternal = value; }
}
public bool Visible
{
get
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
return Convert.ToBoolean(pi.GetValue(PresentationObject, null));
}
}
return true;
}
set
{
if (PresentationObject != null)
{
PropertyInfo pi = PresentationObject.GetType().GetProperty("Visible");
if (pi != null)
{
pi.SetValue(PresentationObject, (bool)value, null);
}
}
}
}
public object PresentationObject
{
get { return presentationObject; }
set { presentationObject = value; }
}
}
}
Then, I do this:
private void btnShowLink_Click(object sender, EventArgs e)
{
Link link = new Link("Here I am!");
this.contextMenu.Items.Clear();
this.contextMenu.Items.Add(link.CaptionInternal);
link.PresentationObject = this.contextMenu.Items[0];
link.Visible = true;
lblCurrentVisibility.Text = link.Visible.ToString();
}
Now, I can imagine this doesn't look too logical/ economical, but it shows the essence of my real problem. Namely, why doesn't the visibility of presentation object (and the value of link.Visible) change, after I call:
link.Visible = true;
I simply do not know what else to do to make this work... Any help is deeply appreciated.
To make things even more interesting, the property Enabled behaves as expected of it...
PropertyInfo pi = PresentationObject.GetType().GetProperty("Enabled");
Could it be related to the fact that Visible is actually a property of ToolStripDropDownItem base base object, whereas Enabled is 'direct' property of ToolStripDropDownItem ?
It would have been easier to figure this out if you said upfront what class this is but now we know it is ToolStripDropDownItem which we can infer means WinForms.
What you are seeing is an oddity with the ToolStripItem's Visible property. It's setter & getter are not tied directly together. MSDN says
"The Available property is different from the Visible property in that
Available indicates whether the ToolStripItem is shown, while Visible
indicates whether the ToolStripItem and its parent are shown. Setting
either Available or Visible to true or false sets the other property
to true or false."
In other words, you want to use the Available property instead of the Visible property
Check http://msdn.microsoft.com/en-us/library/system.web.ui.control.visible.aspx. Maybe this is causing your problem.
There is very important piece of info:
If this property is false, the server control is not rendered. You should take this into account when organizing the layout of your page. If a container control is not rendered, any controls that it contains will not be rendered even if you set the Visible property of an individual control to true. In that case, the individual control returns false for the Visible property even if you have explicitly set it to true. (That is, if the Visible property of the parent control is set to false, the child control inherits that setting and the setting takes precedence over any local setting.)

.NET user settings event handlers

I am trying to use the built in .NET application settings. So for instance I have a User setting of year.
If the end user changes the setting in the program I need to respond by refreshing the data displayed.
I currently have code like below to do this:
Settings.Default.PropertyChanged += SettingsChanged;
//on year change clear out the grid and update the data
private void SettingsChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "year")
{
grdStudentCourseSearch.DataSource = null;
grdStudentCourseSearch.DataMember = null;
UpdateData();
}
}
As you can see their seems to only be an event handler for all the settings and I am having to use e.PropertyName to compare a string to see which property has changed. Is there a better way to do this? Potentially if I change property names later this could be forgotten.
I believe there isn't a better way (using the generated Settings class), due to the implementation of the Settings Class
Consider the generated class code for a simple string setting:
public string Test {
get {
return ((string)(this["Test"]));
}
set {
this["Test"] = value;
}
}
As you can see, it uses the indexer with a string value - you don't have any specialized TestChanged event or some such. The call to OnPropertyChanged is in the indexer setter:
set
{
SettingChangingEventArgs e = new SettingChangingEventArgs(propertyName, base.GetType().FullName, this.SettingsKey, value, false);
this.OnSettingChanging(this, e);
if (!e.Cancel)
{
base[propertyName] = value;
PropertyChangedEventArgs args2 = new PropertyChangedEventArgs(propertyName);
this.OnPropertyChanged(this, args2);
}
}
You could choose to implement settings like this:
class Settings
{
public event EventHandler YearChanged;
private int _year;
public int Year
{
get { return _year; }
set
{
if (_year != value)
{
_year = value;
OnYearChanged(EventArgs.Empty);
}
}
}
protected virtual void OnYearChanged(EventArgs e)
{
if (YearChanged != null)
YearChanged(this, e);
}
}
Then you could register on the YearChanged event.
No, this isn't a good idea. The code is much too brittle. Catch this earlier. At the user interface level for example, whatever code you have that sets the setting value. It can fire the event and you'll know exactly what is getting modified. Or make an intermediate helper class.
In earlier .net framework (3.5 i think) we can use nameof keyword to avoid magic strings so e.PropertyChange == nameof(Year). So you will be warned by compiler when the property identifier changed.
You could just create a variable and assign the current setting to it at runtime, then just update the one variable whenever it's changed, after comparing it to the previous.

Categories