I am working on an application that has been edited by various programmers over the past few years and I have stumbled across a problem with using String Literals to access MenuItems.
For Example: in many places there is code like
mainMenu.MenuItems[1].MenuItems[0].Visible=true;
or
mainMenu.MenuItems["View"].MenuItems["FullScreen"].Visible=true;
how do I change the Strings used to identify the MenuItem and catch all of the places that it is being used for access? The menus and menuitems are declared as public and are used throughout this large application
What is the right way prevent the use of these magic indexes from being used. I forsee things being broken everytime a new item is added or the name is changed.
P.S. I have started using an enumerated dictionary approach in which every menuItem is paired with a key. but this still does not force other developers to use my implementation nor is it the most elegant solution to question 2
Give each menu item a name in the WinForms designer (I assume), and then refer to it by that name.
Then just use this in your code:
menuExit.Visible = false;
If the menu items are added programmatically, do this:
class MyForm : Form
{
private MenuItem menuExit;
...
myMenu.Items.Add(menuExit = new MenuItem(...));
...
}
and then still access it by the menuExit name. The key to avoiding magic numbers and strings is to just keep a direct reference to whatever it is you want to refer to. As a bonus, you can now rename this vairable safely using F2.
Romkyns answer is the correct one for this scenarion however if you do need to use string literals in your code I would alwasy keep them in public static classes such as:
public static class Constants
{
public static class Menu
{
public static readonly string FirstMenuName = "Menu 1";
...
}
public static class OtherCateogry
{
...
}
}
You can then access them by Constants.Menu.FirstMenuName.
As for definitively preventing other devs from using literals throughout code - you might have to make recourse to the Rod of Correction (sturdy metal ruler) ;).
Related
I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.
I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.
I want to dynamically add items to a class, but someone may have to help me with my syntax here.
I've got a ContainerTank class like this:
class ContainerTank {
double radius;
public ContainerTank(int diameter) {
radius = (double)diameter / 2;
}
public int Gauge {get;set;}
public double Volume { get { return (3.1415)*(radius*radius)*Gauge; } }
}
Each of our Sites can have anything from none to an absurd value of 10 tanks (water tanks, oil tanks, etc.).
I can easily create a Class that using List<ContainerTank> tankList to maintain the data in each tank per site, but I'd rather find a way to initialize my class by specifying how many tanks are on a site, then have each tank show up individually.
For example, if I create a new site with 2 tanks, I would have a map to Tank1 and Tank2 (or Tank0 and Tank1 if you want to use zero index).
Why?
For our developers using the code, seeing Tank1 and Tank2 pop up with Visual Studio's IntelliSense would help them when using our control rather than presenting them with a collection.
Mainly, I want to know if it can be done and how I'd implement it.
No, this isn't possible. All class members must be specified when the assembly is compiled.
If what you're interested in is purely ease of debugging, it's possible to write your own debug visualizer.
Could you tell me if it is possible to add an element to a Microsoft WinForms control?
For example: Suppose you have an application that has several users, each of which have their own "permissions," which are represented simply by the strings "1," "2," "3," etc. You also have several buttons on your application, which should be enabled/disabled according to the permission level of the current user.
Would it be possible to add a "String" to the "Button" control, which could indicate what permission level this button represents.
The reason this is helpful, is because I could loop through all of my buttons and disable them if the current user's permission level is not high enough.
I hope this makes sense.
Thanks.
There is a Tag property on WinForms controls that you can use to store a reference to related information. It is of type object, so it can store anything. (msdn reference)
myButton.Tag = "1";
If you want to store more than one thing, then create a class for it:
class UserTag
{
public string Permission {get;set;}
public string Name {get;set;}
}
....
myButton.Tag = new UserTag { Permission="1", Name="Alice" };
....
// Use like this: ((UserTag)myButton.Tag).Permission
Maintain and storing logics behind UI is not an appropriate solution, It makes things harder as your projects grows larger, Store your logics in data structures and make UI compatible and suitable by using the datas.
anyway the solution to your problem is both IExtenderProvider said by Hans and Matt's Answer.
----EDIT----
//this is just a simple sample! :D
Dictionary<string, int[]> CtrlType = new Dictionary<string, int[]>();
CtrlType.Add(button1.Name, new int[] { 2, 3 });
//add another controls status or attributes for user customizing
//.
//.
//.
//somewhere in your form UI Customization for users
button1.Enabled = CtrlType[button1.Name].Contains(UserID) ? true : false;
//handle another controls attributes
Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH
I'm looking for a way to accelerate a repeatable task when I write code. I have ReSharper and I'm thinking a customization could do what I need.
I have two objects of the same type. I want to copy all of the public properties of one object to the other object. I want the tool, ReSharper in this case, to do generate the code for me. I'll tell it the names of the first object and the second object. I want it to find all the public properties of the first object and copy the values to the second object.
Here's the type of code I'm looking to have generated with a tool like ReSharper:
foo.Name = moo.Name;
foo.Age = moo.Age;
foo.City = moo.City;
Automating this simple code that copies values from right to left would save a ton of time and I'm thinking that ReSharper can do it. However, I haven't seen anything pop-up in searches for it though.
I'm not looking for a CodeSmith code generation technique or T4 template because I only want it to generate these specific lines inside my class, not generate and entire class or a separate file.
Does anyone know a way to press a few keystrokes, enter the "foo" and "moo" object names above and have the tool generate these copy from right to left lines of code?
Update:
I've found some documentation on building extensions to ReSharper, and this can probably be achieved by that path, but it looks really involved.
http://www.jetbrains.net/confluence/display/ReSharper/PowerToys+Pack+3.0+User+Guide
This is beginning to look like a weekend challenge unless someone else has already written it.
It's really easy. ReSharper doesn't do it, but you can use a super duper REGEX!
In Visual Studio:
public string Email { get; set; }
public string CellPhone { get; set; }
public int NumChildren { get; set; }
public DateTime BirthDate { get; set; }
Select all your properties. Hit CTRL-D to copy down.
Now hit CTRL-H to replace. Make sure .* is selected for Regex matching.
Replace: public [\w?]* (\w*) .* (This Regex may need to be tweaked)
With: dest.$1 = source.$1;
Now you have some beautiful code you can put in a method of your choosing:
dest.Email = source.Email;
dest.CellPhone = source.CellPhone;
dest.NumChildren = source.NumChildren;
dest.BirthDate = source.BirthDate;
EDIT: New alternatives
You can use AutoMapper for dynamic runtime mapping.
Mapping Generator is really nice for static mapping. It can generate the code above and it works well with R#.
This is somewhat derivative from answer by #Jess (his regex didn't work for me on VS2013) but instead of using Visual Studio I am using regex101
Click link above and just paste your properties into Test string field and you will get them mapped.
Regex I used
public [A-Za-z\?]* ([A-Za-z0-9]*) .*
and replace
Dest.$1 = Source.$1
hope this saves you some time.
I don't believe Resharper can do this, but Open Source AutoMapper can. New to AutoMapper? Check out the Getting Started page.
I agree with #Ben Griswold.
In most situations, Automapper is the way to go.
But when you truly want to generate code that copies properties from one object to another, try this:
Create a brand new class and derive from the class from which you want to copy properties.
Right-click on this new derived class and click 'Refactor > Extract Interface'.
Check all properties that you wish to copy.
Choose 'Place beside' because this interface will be only temporary.
Click 'Next'.
Modify your derived class so that you are no longer inheriting from the base class and you are only implementing your new interface. Expect to see a red squiggle.
Place your cursor over the red squiggle and hit 'ALT-ENTER' to 'Implement Members'.
Click 'Finish'.
Delete that temporary interface and modify your class so that you are no longer implementing it.
Here's a simple class to clone an object. It's not exactly what you asked for but perhaps this will be useful for you:
//.Net 2.0
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace YourNameSpace {
public static class ObjectCloner {
public static T Clone<T>(T obj) {
using (MemoryStream buffer = new MemoryStream()) {
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(buffer, obj);
buffer.Position = 0;
T temp = (T)formatter.Deserialize(buffer);
return temp;
}
}
}
}
Based on #Matas answer I created a more robust version using regex101 that ignores generics, attributes and comments and normalizes spaces.
Regex: *((\/+.*\n*.*)|(\[.*\]\n*.*))*public [A-Za-z\_\?\<\>]* ([A-Za-z0-9\_]*).*(\n| )*
Replace: $4 = person.$4,\n
This is the kind of thing for which Cog shines. Basically, Cog is code generation tool. Code is generated via Python.
Simply copying values from one side to the other is pretty ugly.
You might find it better to create a method to include in your classes that uses reflection to copy public properties. You could save this method in resharper to regenerate into other classes you need this functionality in.