Custom Label does not show the Text string - c#

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?

Related

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

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); }
}

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;
}

xamarin mvvm update from different class

I read several similar issues with MVVM, but just cannot find the right fix to this problem. I am sure it's really simple, but I cannot work out how to update an UI from two different classes.
I have one ModelView (Homepage.xaml) and MVVM binded label, which works fine from the Homepage.cs, but how can I update this label from from a different class (anotheractivty.cs). Is there a way to reference the string from from the second class file or do I need to somehow call and pass the string between the anotheractivty to the homepage class?
Many thanks
Please note the code is simplified:
Homepage.xaml
<Label
Text={"Binding Updatetext}"
/>
Homepage.xaml.cs
String updatetext = "";
public Homepage()
{
BindingContent = this;
}
Public string Updatetext
{
get=> updatetext
set
{
if (value == updatetext)
return;
updatetext = value;
OnPropertyChange(nameof(Updatetext));
}
}
Public updatetest()
{
Updatetext = "new text";
}
anotheractivty.cs
How to link this to the Homepage.cs?
Public updatetest()
{
var page = new Homepage;
Homepage.Updatetext = "new text";
}

Forcing all WPF bound controls to update with GetBindingExpression().UpdateTarget()

I have a WPF application that includes ~50 controls that are bound to properties on my business object which implements INotifyPropertyChanged. Here's a quick snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// properties begin here
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
// constructor, etc. not shown
}
I also have several validation rules that are used to validate the user input in these controls. I'm using command binding to prevent my user from saving the data if there are any validation errors. My application also includes a "Reset default values" button which, obviously, will reset the default value for all of the properties on my business object. This all works exactly as I'd like it to with one exception. If my user enters invalid data into one or more controls and then clicks the "Reset default values" button, the controls that contain invalid data don't always update as I'd expect. This happens because of the following code in my property setters:
if (_name == value)
{
return;
}
This code exists to prevent unnecessary property changed notifications from occurring when the value entered by my user in the bound UI control is the same value that the property is already set to. As an example, I have an IntegerUpDown control in my UI (this control is part of the Extended WPF Toolkit from Xceed). The default value of the property that my control is bound to is 10. My user deletes the value from the control and my validation rule is triggered which results in a validation error and the UI is updated appropriately with an error adorner, etc. The value of the property that this control is mapped to hasn't been changed so it's still set to 10. Now my user clicks the "Reset default values" button which will result in the default value (10) for the property being reset. However, the value for the property is already set to 10 so the short circuit logic in my setter will return instead of setting the property value.
So now, after my user clicks "Reset default values", I am also forcing an update on my binding target like this:
this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();
This solves my problem but only for this particular control. Is there any easy way to do this for all of my bound controls without having to specify each one? Thanks.
OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));
This is intended to imply that ALL properties on that object have changed.
Could you do one of the following?
1) Reset the DataContext - Either recreate it, or re-set the property
var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;
2) Loop through all properties programmatically via reflection and manually call OnPropertyChanged with the relevant property names.
var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}
You've mentioned validation and reset values, and of course the obvious one is to persist it.
Why don't you implement IEditableObject Interface on your entity that has three signature methods. BeginEdit(), CancelEdit() and EndEdit()
That way you can easily roll back your entity to the whatever you want, or validate it and lastly persist it. A good example is found here
Sample code
public class Customer : IEditableObject
{
struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}
private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;
// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " + this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}
void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " + this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}
void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}
public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}
public string ID
{
get
{
return this.custData.id;
}
}
public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
this.OnCustomerChanged();
}
}
public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
this.OnCustomerChanged();
}
}
internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}
private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}
public override string ToString()
{
StringWriter sb = new StringWriter();
sb.Write(this.FirstName);
sb.Write(" ");
sb.Write(this.LastName);
return sb.ToString();
}
}
Wouldn't it be easier to just always call OnPropertyChanged regardless of whether its the same? How much of a performance boost does that give you?

how to implement autoimplemented properties in C# for setting a value

I am trying to use autoimplemented properties.
It shows me error
public OpenMode OpenFor
{
get;//must declare a body because it is not marked as abstract, partial or external. Why so
set
{
if (value == OpenMode.Add)
{
btnAddGuest.Text = "Save";
btnUpdatePreference.Visible = false;
dgvGuestInfo.ClearSelection();
}
else if (value == OpenMode.Update)
{
btnAddGuest.Text = "Update";
btnUpdatePreference.Visible = true;
}
}
}
You must implement both a getter and setter if you implement one of them. You can auto implement only both:
public OpenMode OpenFor
{
get;
set;
}
You may consider to use a backing field:
private OpenMode openFor;
public OpenMode OpenFor
{
get
{
return openFor;
}
set
{
openFor = value;
//...
}
}
autoimplementation works only for the simple usecase that set and get have no custom body
To use just get; you need to use just set; as well. In this case you'd have an implicit variable. When you declare a body for set, that doesn't work. Ask yourself the question; how can you get anything you can never set?
private OpenMode _openFor;
public OpenMode OpenFor
{
get{return _openFor;}
set{
_openFor = value;
SetOpenFor(value);
}
}
private void SetOpenFor(OpenMode mode)
{
if (mode== OpenMode.Add)
{
btnAddGuest.Text = "Save";
btnUpdatePreference.Visible = false;
dgvGuestInfo.ClearSelection();
}
else if (mode == OpenMode.Update)
{
btnAddGuest.Text = "Update";
btnUpdatePreference.Visible = true;
}
}
Also note that the auto-implemented properties also listen to access modifiers:
public string Foo { get; private set; }
Though you still need to define both.
In your example, it looks like you don't need the get. You aren't storing the value into a local field either, so it looks like your property should be a method.
Alternatively, your get could infer the value from the state of the buttons you are modifying in the set - but this is starting to get silly.

Categories