C# Is there a way to use a variable AS a property? - c#

I want to use a variable, here i'm using TextName as the variable.
then use it AS a property. Of course i cannot because it's a string, but how do i get a variable/string to be treated as the text in the string it's holding? In google sheets this is very similuar to something called an INDIRECT, to be able to be the data you are referring to.
// Code I like to write:
TextName = "richTextBox";
TextName.Text = "text for richTextBox";
I want that code to be treated as
richTextBox.Text = "text for richTextBox";
but with richTextBox.Text in full or part to be a variable, so that I can put this entire thing in a method, and only change the variable, and not have an entire method all over the code over and over.
I'm using .NET6.0 if it matters.

I suppose you are going to do this in a Form. In this case, you can add this code into your form:
public string this[string name]
{
get
{
return this.Controls.ContainsKey(name) ?
this.Controls[name].Text :
null;
}
set
{
if (this.Controls.ContainsKey(name))
{
var control = this.Controls[name];
control.Text = value;
}
}
}
And get/set the text of any Control in this way:
this["richTextBox"] = "text for richTextBox";
var text = this["richTextBox"];
this is your form.
If you are going to use in multiple forms, you can create an extension methods:
public static class FormExtends
{
public static string GetText(this Form form, string name, string defaultText = null)
{
return form.Controls.ContainsKey(name) ?
form.Controls[name].Text :
(defaultText ?? string.Empty);
}
public static void SetText(this Form form, string name, string text)
{
if (form.Controls.ContainsKey(name))
{
var control = form.Controls[name];
control.Text = text;
}
}
}
And simplify the code in each form:
public string this[string name]
{
get { return this.GetText(name); }
set { this.SetText(name, value); }
}

Related

Optimize the Algotithm

So, i have a method
public void AddToSearch(List<FullName> fullNames)
{
foreach (var fullName in fullNames)
{
if (fullName.Surname != null)
_sb.Append(fullName.Surname.Trim() + " ");
if (fullName.Name != null)
_sb.Append(fullName.Name.Trim() + " ");
if (fullName.Patronymic != null)
_sb.Append(fullName.Patronymic.Trim());
fullNamesList.Add(_sb.ToString().TrimEnd());
_sb.Clear();
}
it takes a list of FullName and by using StringBuilder instance converts each element into a string(which format is "$Surname $Name $Patronymic"). At the end i put the result into my list. The Question is - how can i optimize all of that "Trim" stuff. It bothers me that i use it in multiple occassions and i am pretty sure it effects the time.
how can i optimize all of that "Trim" stuff
Very simple, simply don't call Trim() on those strings. What spaces are you worried about? Who's entering those values in your business objects? Because short of solar flares randomly flipping bits enough to append spaces to your strings, you're in full control from beginning to end, so simply don't add the spaces.
You also don't need the two string builders, just insert in your main one. There's no need for yet another Trim() here either, because simply decrementing the Length property of your string builder is a constant operation (it literally decrements one integer with guaranteed no extra allocations).
the strings normalization process should be done in the data layer (in application or database) for stored strings. While dynamic strings such as user input, needs to be normalized as soon as you get them to prepare them for the next task.
For your current code, you can modify the FullName class, adjust the setters to trim the value before it's been stored, and override the ToString to return the full name.
Example :
public class FullName
{
public string Name
{
get => Name;
set => Name = value?.Trim();
}
public string Surname
{
get => Surname;
set => Surname = value?.Trim();
}
public string Patronymic
{
get => Patronymic;
set => Patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Then, you can do this :
fullNamesList.AddRange(fullNames.Select(x=> x.ToString()));
UPDATE :
Thanks to #olivier-jacot-descombes, the above code is missing the use of backing fields, which will avoid causing overflow exception by the properties infinite recursions. The following adjustments will do the trick.
public class FullName
{
private string _name;
private string _surname;
private string _patronymic;
public string Name
{
get => _name;
set => _name = value?.Trim();
}
public string Surname
{
get => _surname;
set => _surname = value?.Trim();
}
public string Patronymic
{
get => _patronymic;
set => _patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Try and extension something like this.
public static class Helper
{
public static StringBuilder AppendValue(this StringBuilder builder,string value)
{
if(!string.IsNullOrEmpty(value))
{
builder.Append(value.Trim());
return builder;
}
}
}
call as follows:
sb.AppendValue(fullName.Name);
sb.AppendValue(fullName.Surname);
...
You will get the StringBuilder back with the value if it is not empty otherwise nothing will be added to it.

Sharing values between different Classes in WPF

I have a WPF Project where I want to save DataRows from a DataGrid into an "options" class and retrieve those variables in another window.
Thats how I save my Variable from my DataGrid into my "options" Class (mainWindow.xaml.cs):
options.title = Convert.ToString((showEntries.SelectedItem as DataRowView).Row["title"]);
This Variable im saving via a getter and setter (options.cs):
public string Title
{
get { return title; }
set { title = value; }
}
And now I want to retrieve the saved variable in another window(updateDatabse.xaml):
private void getUpdateEntries()
{
Options returnValues = new Options();
titleBox.Text = returnValues.Title;
}
My Question is: Why is my textbox "titleBox" empty when running my code.
If the logic of your task does not provide for the creation of several instances of classes (and as far as I understand your explanations, this is so), then you can use the Singlton implementation.
Example:
public class Options
{
private string title;
public string Title
{
get { return title; }
set { title = value; }
}
private Options() { }
public static Options Instance { get; } = new Options();
}
Options.Instance.Title = Convert.ToString((showEntries.SelectedItem as DataRowView).Row["title"]);
private void getUpdateEntries()
{
titleBox.Text = Options.Instance.Title;
}
You mixed things up.
private void getUpdateEntries()
{
Options returnValues = new Options();
returnValues.title = Convert.ToString((showEntries.SelectedItem as DataRowView).Row["title"]);
titleBox.Text = returnValues.Title;
}

Get set properties

So im trying to use the get / set properties on C# but I cant get my code to work ( it crashes my console app )
This is my textHandler.cs file as you can see the public static void method WriteInfo is using get / set properties but it crashes my app..
class TextHandler
{
public static void WriteInfo(String text)
{
var consoleText = new Text();
consoleText.text = text;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(consoleText);
Console.ForegroundColor = ConsoleColor.White;
}
public static void WriteError(String text)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text);
Console.ForegroundColor = ConsoleColor.White;
}
public static void WriteSuccess(String text)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(text);
Console.ForegroundColor = ConsoleColor.White;
}
public static void WriteText(String text, ConsoleColor color)
{
}
}
public class Text
{
public String text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
}
Here I call that method
TextHandler.WriteInfo("New client with version : " + message + " | current version : " + version);
If I remove that line the app doesnt crash anymore, dont know what Im doing wrong since I dont get any error.
Also if this is a bad method to do this please tell me I would like to improve
Thanks
The code that creates infinit recursion is:
public String text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
In set you assign this.text = value to itself, creating infinit recursion, so StackOverflow soon or later.
Seems that you do no need a field, so change your code to:
public String Text {get;set} //PROPERTIES ARE UPPERCASE BY MS STANDART
You need to separate the "backing" field from the public property:
public class Text
{
private string text;
public String TheText
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
}
In the above example, TheText is the a "badly named" public property and text is the backing field. At the moment, your code is addressing the same field for both, causing recursion. Usually the convention would be to have a capital property Text and a lowercase backing field text.
However, in your code you have named the class Text so it is confusing to address text.Text.
There is no need to create the "Text" class. Just pass the string to Console.WriteLine. Also, you didn't specify the nature of the application. This will work fine in a console app, but may not work for a web application or other app that is not bound to the SdtOut
So,
You are setting the into the property that is calling again the set method until you get a StackOverflow exception.
To avoid this try this
public class Text
{
string _text = null;
public String text
{
get
{
return this.text;
}
set
{
_text = value;
}
}
}
Or empty get set methods
public class Text
{
public string text { get; set; }
}

Custom Label does not show the Text string

I needed to make my own label to hold some value, that is diferent from the value displayed to user
public class LabelBean : Label {
private string value;
public LabelBean(string text = "", string value = ""): base() {
base.Text = text;
this.value = value;
}
public string Value {
get { return value; }
set { this.value = value; }
}
}
but now id in the form constructor I replace the control with my class
this.lbAttributeType = new LabelBean();
and later after the form is created, but before it is shown I set the text through setter
(this.lbAttributeType as LabelBean).Value = value;
this.lbAttributeType.Text = Transform(value);
but in the form I have always "label1" text... what is wrong with it?
thanks
UPDATE
I added the solution here to find it easier:
public class MyLabel : Label {
public MyLabel()
: base() {
}
public string Value {
set {
this.Text = value;
}
}
}
and the form with Widnows.Forms.Label label1 control
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.Controls.Remove(this.label1);
this.label1 = new MyLabel();
this.Controls.Add(this.label1);
(this.label1 as MyLabel).Value = "oh";
}
}
the bug was in the Controls.Remove and Controls.Add,
thanks all for their time :)
My guess is because, since you're doing the work in the constructor, the InitializeComponent code, automatically generated by the designer, is overwriting the control instance, as it's most likely called after your initialisation.
If the class is part of the project, you will find it on the toolbox; meaning you can simply drag and drop your new control on the form in place of the existing one - this is what you should do.
This ensures that the designer-generated property is of your LabelBean type, and not simply Label.
Also - you should consider changing your Value setter as demonstrated by WoLfulus (+1 there)
Update
In response to the comment you put on WoLfulus' answer - here's a couple of alternatives:
1) If the form is the 'clever' bit here - consider just writing a helper method in it, and setting the value of the label through it, leveraging the Tag property:
public void SetLabelBean(Label target, string value)
{
Label.Tag = value;
Label.Text = Transform(value);
}
public string GetLabelBean(Label target)
{
return target.Tag as string;
}
2) Continue using your sub-classed LabelBean type (adding it via the designer as I've already mentioned) - but use an abstraction to give it access to the form's Transform method:
public interface ITransformProvider
{
string Transform(string);
}
Make your form class implement this interface, with the Transform method you elude to.
Now, in your LabelBean class:
public ITransformProvider Transformer
{
get{
//searches up the control hierarchy to find the first ITransformProvider.
//should be the form, but also allows you to use your own container controls
//to change within the form. The algorithm could be improved by caching the
//result, invalidating it if the control is moved to another container of course.
var parent = Parent;
ITransformProvider provider = parent as ITransformProvider;
while(provider == null){
parent = parent.Parent;
provider = parent as ITransformProvider;
}
return provider;
}
}
And then, finally, using WoLfulus' code, but slightly changed, you can do this:
public string Value
{
get
{
return value;
}
set
{
this.value = value;
var transformer = Transformer;
if(transformer != null) this.Text = transformer.Transform(value);
}
}
That, I think, addresses your issues with that answer.
Try this:
Create a new delegate outside the label class:
public delegate string LabelFormatDelegate( string val );
Add this to your label class:
public LabelFormatDelegate ValueFormatter = null;
public string Value
{
get
{
return value;
}
set
{
this.value = value;
if (this.ValueFormatter != null)
{
this.Text = this.ValueFormatter(value); // change the label here
}
else
{
this.Text = value;
}
}
}
Place a new common label to your form (lets name it "label1")
Goto to Form1.Designer.cs and search for "label1" declaration.
Rename the "Label" type to your own label type (Ex: "MyLabel")
Change the initialization code of label on InitializeComponent function on designer code to match the new type "MyLabel"
Example:
this.label1 = new Label();
Change to:
this.label1 = new MyLabel();
In the Form_Load event, specify the format function:
this.label1.ValueFormatter = new LabelFormatDelegate(this.Transform);
Notes: You'll need to remove the "Text" setter call too from here:
(this.lbAttributeType as LabelBean).Value = value;
// this.lbAttributeType.Text = Transform(value);
This will keep your value/text in sync but remember not to set "Text" property by hand.
I agree with WoLfulus and Andreas Zoltan and would add a symmetrical functionality to Text if there exists a unambiguous reverse transformation:
public string Value
{
get { return value; }
set
{
if (this.value != value) {
this.value = value;
this.Text = Transform(value);
}
}
}
public override string Text
{
get { return base.Text; }
set
{
if (base.Text != value) {
base.Text = value;
this.value = TransformBack(value);
}
}
}
Note the if checks in order to avoid an endless recursion.
EDIT:
Assigning your label to lbAttributeType is not enough. You must remove the old label from the Controls collection before the assignment and re-add it after the assignment.
this.Controls.Remove(lbAttributeType); // Remove old label
this.lbAttributeType = new LabelBean();
this.Controls.Add(lbAttributeType); // Add new label
Your form was still displaying the old label! Why did I not see it earlier?

difficulty inserting a name to an inserted object of a checkedlistbox

I am abit new in C# and i am trying to insert an object to a CheckedListBox,
so this inserted item will have a title inside the checked list (my object contains a string field inside it which I want to be displayed in the CheckedListBox).
for example this is my class:
public class InfoLayer
{
private string LayerName;
private List<GeoInfo> GeoInfos;
public InfoLayer()
{
LayerName = "New Empty Layer";
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName)
{
this.LayerName = LayerName;
GeoInfos = new List<GeoInfo>();
}
public InfoLayer(string LayerName,List<GeoInfo> GeoInfosToClone):this(LayerName)
{
foreach (GeoInfo item in GeoInfosToClone)
{
GeoInfos.Add((GeoInfo)((ICloneable)item).Clone());
}
}
public GeoInfo SearchElement(long id)
{
foreach (GeoInfo info in GeoInfos) // foreach loop running on the list
{
if (info.INFOID == id)
return info; // return the item if we found it
}
return null;
}
public GeoInfo SearchElement(string name)
{
foreach (GeoInfo info in GeoInfos)
{
if (info.INFONAME.CompareTo(name)==0)
return info;
}
return null;
}
public override string ToString()
{
string toReturn = "";
for (int i = 0; i < GeoInfos.Count; i++) // for loop running on the list
{
toReturn += String.Format("{0}\n",GeoInfos[i].ToString()); // piping another geoinfo
}
return toReturn;
}
public string LAYERNAME{get{return LayerName;}}
my class also contains a tostring overrider inside her (not what i want to display)
thanks in advance for your help.
Override ToString() in your class, the class that the object is an instance of.
Edit:
You don't want to display the contents of ToString(). You want to display the LayerName, don't you? Perhaps you should display the values with Databinding instead. Then you can set DisplayMember to your new LAYERNAME property.
I believe this is what you are trying to achieve:
checkedListBox1.Items.Add(yourObject.stringField);
((MyObjectType)checkedListBox1.Items(index)).Name = "whatever"
You will have to know the index of the object you want to change.
You'll just have to override the ToString method in your class so that it returns this Name property value.
public overrides string ToString() {
return Name;
}
It will then display its name when added to your CheckedListbox.

Categories