MVVM Additional Property for a Field - c#

Maybe you can help me at the following issue.
I have a Model like this:
public class Lookup
{
private string _ValueName;
[Required]
public string ValueName
{
get
{
return _ValueName;
}
set
{
_ValueName = value;
}
}
}
In the ViewModel a can access Lookup.ValueName to get the Value of the item. But how i can get the Name of the DataColumn? I want to implement a new interface which will give me the Name but maybe it is a lot easier? I do not want to write the Name hardcoded.
Target is to write: Lookup.ValueName.DataColumnName
Thanks for helping
Tom

Consider this option:
public class Lookup
{
[Required]
public string ValueName { get; set; }
public string ValueNameDataColumnName => nameof(ValueName);
}

Related

C# EF Core converting object to string / get; set

I do get a stack overflow when trying to convert an object to a string in C#. I'm making an API Call to an endpoint and pass the response to this class. As the ef core does not support the datatype “object” I'm trying to convert this datatype to string to store it in the database as a column.
I have a class with various attributes like
public DateTime? start_date_local { get; set; }
public string? timezone { get; set; }
public double? utc_offset { get; set; }
some of them are from type object:
[NotMapped]
public object? start_latlng { get; set; }
As this datatype is not supported, I'm not mapping this to the DB, but I'm trying to convert this into a string and store it within a second datatype which can be inserted into the DB.
public string start_latlng2
{
get { return start_latlng2; }
set { start_latlng2 = Convert.ToString(start_latlng); }
}
This does not seem to work as I always get an error like:
Stack overflow.
Repeat 19126 times:
--------------------------------
at SportAnalytics.DataModel.Activity.get_start_latlng2()
--------------------------------
at DynamicClass.lambda_method171(System.Runtime.CompilerServices.Closure, Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.EnsureOriginalValues()
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntrySubscriber.SnapshotAndSubscribe(Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry)
Am I doing something wrong? Is there a better way to achieve what I'm trying to do?
The StackoverflowException comes from the wrong definition of your property. You should write it with an explicit back field.
private string _start_latlng2;
public string start_latlng2
{
get { return _start_latlng2; }
set { _start_latlng2 = value; }
}
In your case you should use an explicit read-only property, that will be called when needed:
public string start_latlng2
{
get { return start_latlng == null ? null : Convert.ToString(start_latlng); }
}
You can try like this
public class Test{
public Test1 start_latlng {get;set;}
}
public class Test1{
public string Attr1 {get;set}
public string Attr2 {get;set}
}
#Oliver
sorry for asking once again a silly questions, but I just got started with C# a few weeks back.
I have now set something like this:
[NotMapped]
public object? start_latlng { get; set; } // was object
private string? _start_latlng2;
public string? start_latlng2
{
get { return start_latlng == null ? null : Convert.ToString(start_latlng); }
set { _start_latlng2 = value; }
}
If I run the debugger once again, I can see that the original object is an array like ["49,11", "49,12"], the start_latlng2 is also set like a string "["49,11", "49,12"]" which is fine and ok for me to be added to the database like this, but the column in sql keeps empty and I don´t know why because the property in the debugger is set correctly.
Can you once again maybe quickly explain how your suggestion
private string _start_latlng2;
public string start_latlng2
{
get { return _start_latlng2; }
set { _start_latlng2 = value; }
}
corresponds to transforming an object to an string? Your suggestion here just covers how you can access private fields with those getters and setters or? I really would like to understand how I can convert an object to string working with getters and setters. Maybe you can explain that once more, thank you very miuch Oliver!

Binding C# BindingList<T> to a DataGridView

Maybe the question is a little bit dumb, but I did not quite find solution anywhere else.
So I am using a BindingList of custom made class objects as a DataSource for DataGridView.
Everything works fine with properties, that are directly inherited from other classes, but if I have an object of other class in the main class, its properties wont show up in DataGridView.
Classes are:
enum Valsts
{
Latvija,
Igaunija,
Ķīna,
ASV
}
class Razotajs
{
public Valsts valsts { get; set; }
public string razotajaNosaukums { get; set; }
}
class Tehnika
{
public string krasa { get; set; }
public Razotajs razotajs = new Razotajs();
}
class Viedierice : Tehnika
{
public string operetajsistema { get; set; }
public double ekranaIzmers { get; set; }
public bool irHDMI { get; set; }
}
class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim = new SimKarte();
public override string ToString()
{
return String.Join(";", modelis.ToString(),svars.ToString(),sim.veids.ToString(),operetajsistema.ToString(),ekranaIzmers.ToString(),irHDMI.ToString(),krasa.ToString(),razotajs.razotajaNosaukums.ToString(),
sim.numurs.ToString(),razotajs.valsts.ToString());
}
}
class SimKarte
{
public string veids { get; set;}
public int numurs { get; set; }
}
For example- I can see columns "modelis" and "svars", but attributes like "veids" and "numurs" from class SimKarte are not included in the DataGridView.
Is there any solution for this?
I've tried to add { get; set; } after declaring a new instance of an object in the class, but it's not even a real thing. I really don't have any idea, what would help me to solve this.
Thank you all in advance! :)
Honestly, I think the simplest solution is the one JohnG proposed; add proxy properties to your main class that read/write the properties of the complex objects
A datagridview will show only the simple types it knows how to show, from the top level class. It will not dig into properties of properties (otherwise even adding a string column would cause the grid to fill up with a Length column an Isinterned column etc..)
partial class MobilaisTelefons : Viedierice
{
public string modelis { get; set; }
public double svars { get; set; }
public SimKarte sim { get; set; } = new SimKarte();
public override string ToString()
{
return String.Join(";",
modelis, svars, sim.veids, operetajsistema, ekranaIzmers, irHDMI, krasa, razotajs.razotajaNosaukums,
sim.numurs, razotajs.valsts);
}
}
partial class MobilaisTelefons {
public string SimVeids { get => sim.veids; set => sim.veids = value; }
public string SimNumers { get => sim.numers; set => sim.numers = value; }
public string RazotajsRazotajaNosaukums { get => razotajs.razotajaNosaukums; set => razotajs.razotajaNosaukums = value; }
public Valsts RazotajsValsts { get => razotajs.valsts; set => razotajs.valsts = value; }
}
Few tips:
I made the extension of the class partial so you can put it in another file. Hiding its members from intellisense would be hard work
the Enum column will probably show as an int. if you want it to be sensible, use a DataGridViewComboBox column bound to a list of all the enum values/names. On the column, set the DataMember to "RazotajsValsts", the DataSource to the list of enums, the DisplayMember to the property representing the enum name and the ValueMember to the property representing the enum value. See Enum.GetValues.
Enums should only have a plural name (if valsts is plural) if they are flags
classes should not have a plural name
public properties names should be in PascalCase not camelCase
I simplified your tostring: you don't need to call to string on everything; string join will do it. You especially don't need to call tostring on a string

Combining Attributes in C#

I am using Attributes in a serializable properties object that is used to read/write xml config file for my app and display using Windows.Forms.PropertyGrid. I use c# Attributes to achieve this and would like to be able to combine values of attributes so that my [Description] includes [DefaultSettingValueAttribute].
For example, here is how one property is defined:
`[Category("General")]
[Description("Default Filename")]
[global::System.Configuration.DefaultSettingValueAttribute("sample.txt")]
public string DefaultFileName { get; set; } = "sample.txt";`
What I would like to be able to do is something on the lines of:
`[Category("General")]
[global::System.Configuration.DefaultSettingValueAttribute("sample.txt")]
[Description("Default Filename: " +
global::System.Configuration.DefaultSettingValueAttribute]
public string DefaultFileName { get; set; } = "sample.txt";`
Any suggestions on how I could achieve this?
At the very minimum, if two strings have different meaning then I wouldn't combine them. If you combine them then what you have is one big string, and whatever part of your code looks at that string has to know how to parse it back into its original pieces.
You can just as easily create an attribute that contains two strings. For example,
public class SomeAttribute : Attribute
{
public SomeAttribute() { }
public SomeAttribute(string category, string description)
{
Category = category;
Description = description;
}
public string Category { get; set; }
public string Description { get; set; }
}
No need to combine them.
But another question is, are the values related somehow so that they actually belong in the same attribute? I don't know how your attribute is used. But unless they're intrinsically related, it may be better to even keep them as separate attributes. If there's a logical distinction between two strings, use two strings, and the same for attributes.
Or perhaps what you might want is for one attribute to inherit from the other:
public class BaseAttribute : Attribute
{
public BaseAttribute() { }
public BaseAttribute(string defaultValue = null)
{
DefaultValue = defaultValue;
}
public string DefaultValue { get; set; }
}
public class SomeAttribute : BaseAttribute
{
public SomeAttribute() { }
public SomeAttribute(string category, string description, string defaultValue = null)
:base(defaultValue)
{
Category = category;
Description = description;
}
public string Category { get; set; }
public string Description { get; set; }
}
Now the second attribute contains its own properties plus that of the base attribute.

How can I return the value of a field in C# if I want to execute a function on that field?

I have a model that looks like this:
[DisplayName("Subject")]
public string SubjectID { get; set; }
public string SubjectText ??
What I want to do is to have the value of SubjectText be equal to the following when a user requests it:
SubjectText = SubjectReference.GetSubjectText(Model.PageMeta.SubjectID)
I also don't want anyone to be able to set this value. How can I do this?
Use a property:
public string SubjectText {
get {
return SubjectReference.GetSubjectText(Model.PageMeta.SubjectID);
}
}
You can use this:
public string SubjectText {
get { return SubjectReference.GetSubjectText(Model.PageMeta.SubjectID); }
}
edit: ninja'd
Are you looking for something like this?
public string SubjectID { get; set; }
public string SubjectText
{
get
{
return "anything you want here";
}
}
You can use a property getter that references the other property, SubjectID, like this:
public string SubjectText { get { SubjectReference.GetSubjectText(SubjectID) } }
This will be a read-only property because it doesn't have a setter.

Bind List of Classes into DataGridView

I need to bind a List<MyClass> myList to a DataGridView. And get in the results table with two columns ID and Name.
Code snippets:
private List<MyClass> myList = new List<MyClass>(){...};
public void BindClass()
{
dataGridView.DataSource = myList;
}
public MyClass
{
public MyDataClass Data{ get; set; }
}
public MyDataClass
{
public string ID { get; set; }
public string Name { get; set; }
}
Is it possible?
How about binding to an anonymous type:
public void BindClass()
{
dataGridView1.DataSource = myList.Select(myClass => new {myClass.Data.ID, myClass.Data.Name}).ToList();
}
Will you be updating the data in the datagridview ?
To do that without changing the model is exceptionally tricky (but possible), requiring ICustomTypeDescriptor or TypeDescriptionProvider, and a custom PropertyDescriptor. To be honest: not worth it.
Just add pass-thru properties:
public MyClass
{
public MyDataClass Data{get; set;}
[DisplayName("ID")]
public string DataID {
get {return Data.ID;}
set {Data.ID = value;}
}
[DisplayName("Name")]
public string DataName {
get {return Data.Name;}
set {Data.Name = value;}
}
}
It's easy with LINQ as you can see in This answer
Here's a simple implementation of something I needed to attach to datagridview.
DataGridView1.DataSource = _
(From i In ItemList Select i.ListID, i.FullName, i.PurchaseDesc, i.EditSequence).ToList
No, you can't do this out of the box. You will have to write a custom binding source (most likely with specialized logic for your specific purpose) to allow 'drilling' deeper than just 1 level of properties.

Categories