I have created a user control (CheckedDirTree) that exposes a CheckedFolder property which in turn returns an IEnumerable of the FullPath property of Nodes checked in the control. Something like this:
public IEnumerable<string> CheckedFolders
{
get
{
foreach (TreeNode node in treeView1.Nodes[0].DescendantNodes())
{
if(node.Checked && !node.FullPath.Equals(_directoryRoot))
yield return node.FullPath;
}
}
}
This is being fed to another class's (SymbolsShareDto) Folders property after this cntrol is shown in a grid and the user has checked some folders:
using (var dirControl = new CheckedDirForm(symbolsShare))
{
if (dirControl.ShowDialog() == DialogResult.OK)
{
var symbolsShareObj = bindingSourceShare.Current as SymbolShareModel;
if (symbolsShareObj != null) symbolsShareObj.Folders = dirControl.CheckedFolders;
}
}
[DataContract]
public class SymbolShareDTO
{
public SymbolShareDTO(){}
[DataMember]
public string Share { get; set; }
[DataMember]
public string BackupTo { get; set; }
[DataMember]
public IEnumerable<string> Folders { get; set; }
public override string ToString()
{
return string.Format("Share: {0}{1}BackupTo: {2}{3}Folders: {4}", Share, Environment.NewLine, BackupTo,
Environment.NewLine, Folders.Count());
}
}
However when I serialize the SymbolsShareDto, I'm getting an error saying
CheckedDirTree+<get_CheckedFolders>d__6' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Any thoughts ? Do I need to return a new List from CheckedFolders property instead.
thanks
Sunit
Do CheckedFolders.ToArray() and serialize that.
use
[DataContract] for your class (msdn)
and [DataMember] for each property (msdn)
You're probably trying to send instances of SymbolsShareDto through WCF.
All you have to do is to mark it with DataContractAttribute and all the property with DataMember :
[DataContract]
public class SymbolsShareDto
{
[DataMember]
public int Data1 { get; set;};
[DataMember]
public int Data2 { get; set;}
}
I hope this help.
Related
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
I'm trying to deserialize json to RequestWithDefault object
JSON:
{
"fields":["f1","f2"]
}
My simple class diagram:
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[DataMember]
public override List<string> Fields {get; set; } = new List<string> {"test"}
}
After deserializing json to RequestWithDefault object Fields property contains ["test", "f1", "f1"]. I want to be sure that this default values are applied only in case when Fields were not specified in request, or was specified as null. How I can do this? I tried with [OnDeserializing] attribute but without success. Result is the same
According to this:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts
Looks like during deserialization DataContractSerializer calling Add method from collection. That's why I have also default value and rest of items are added. When I will replace List<string> to string[] everything works fine.
It seems WCF serialization never use setter to set the value of the DataMember with type of collection, but use Add instead. Because of this, the only way to check whether the fields has any value is to check after it has been deserialized (not while deserializing).
[DataContext]
public abstract class BaseRequest
{
[DataMember]
public virtual List<string> Fields { get; set; }
}
[DataContext]
public class RequestWithDefault : BaseRequest
{
[System.Runtime.Serialization.OnDeserialized]
void OnDeserialized(System.Runtime.Serialization.StreamingContext c)
{
if (Fields == null
|| Fields.Count < 1)
{
Fields = new List<string> { "test" };
}
}
}
Is there an easy way to copy everything from a strongly typed object into a dynamic one? The target has to be a DynamicObject as determined by a 3rd party library I'm using. Everything from TypedModel needs to go into MyDynamicObject at runtime.
public class MyDynamicObject : DynamicThirdPartyObject
{ }
public class TypedModel
{
public string text { get; set; }
public int number { get; set; }
public List<SomeOtherModel> someList { get; set; }
}
Existing solutions I found on SO all match up properties between typed classes.
EDIT
Found a simple solution based on FastMember:
public void CopyProperties(object source, DynamicObject target)
{
var wrapped = ObjectAccessor.Create(target);
foreach (var prop in source.GetType().GetProperties())
{
wrapped[prop.Name] = prop.GetValue(source);
}
}
I propoes to use reflection.
suppose you make following declaration:
public class MyDynamicObject : DynamicThirdPartyObject
{ }
public class TypedModel
{
public string text { get; set; }
public int number { get; set; }
public List<SomeOtherModel> ListOtherModel { get; set; }
}
Lets say you want to get properties of instance:
typedModel.GetType().GetProperties();
Another possible situation is if you want to copy type:
typeof(TypedModel).GetProperties();
TypedModel typeModel = new TypedModel {number = 1, text = "text1",
ListOhterModel = new List()
};
foreach(var prop in typeModel.GetType().GetProperties())
{
Console.WriteLine("{0}={1}", prop.Name, prop.GetValue(typeModel, null));
}
And if you need to go through hierarchy, maybe you need to use recursion, in order to go through nested types, I mean you can use reflection for copying all members of SomeOtherModel.
<root xmlns:test="url" test:attr1="10" test:attr2="someValue>
<elementwrapper>
<secondElementwrapper>
<element>someValue</element>
<differentelement>anotherValue</differentelement>
</secondElementwrapper>
</elementwrapper>
</root>
The following classes are made:
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper{ get; set; }
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper{ get; set; }
}
public class Values
{
[XmlElement("element")]
public string Element{ get; set; }
[XmlElement("differentelement")]
public string Differentelement{ get; set; }
}
}
And here is where i serialize and deserialize the xml:
var reader = XmlReader.Create(url);
var xmlRecord = new XmlEntities.Root();
try
{
var serializer = new XmlSerializer(typeof(XmlEntities.Root));
xmlRecord = (XmlEntities.Root)serializer.Deserialize(reader);
reader.Close();
}
catch (Exception e) { }
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element
How do i get the test:attr1 value?*
If i remove the class Elementwrapper and Root, xmlRecord returns null.
[XmlAttribute] inside the Root class to get attr1 value doesnt work for me.
Thank you!
EDIT:
The element was a copy/paste wrong doing. Fixed that. I also added :test infront of the attr1, forgot that.
EDIT:
Adding the following as sgk mentioned, inside the root class, allowed me to access the attribute
[XmlAttribute("attr1", Namespace = "url")]
public string attr { get; set; }
EDIT: And is there a way to map the classes differently? So i can access xmlRecord.Element directly?
EDIT: #TonyStark seems like what i want needs to be approached a different way, but then again this already works i just need to access the element trough the nodes (xmlRecord.elementwrapper.secondelementwrapper.element) for those that are wondering.
To access the attribute of root i simply use : xmlRecord.attr after i added the xmlattribute that its written above.
I want to access xmlRecord.Element instead of xmlRecord.Elementwrapper.Values.Element - Based on the XML structure you have, to Deserialize and access the <element> value and <differentelement> value - you need to go through root-->elementwrapper-->secondElementwrapper.
How do i get the attr1 value? - add [XmlAttribute("attr1")] inside class Root
Then, [XmlElement("<elementwrapper>")] should be [XmlElement("elementwrapper")] otherwise when you Deserialize you will always get null as there is no matching element.
See below
public class XmlEntities
{
[XmlRoot("root")]
public class Root
{
[XmlElement("elementwrapper")]
public Elementwrapper Elementwrapper { get; set; }
[XmlAttribute("attr1", Namespace="url")]
public string attr1;
}
public class Elementwrapper
{
[XmlElement("secondElementwrapper")]
public SecondElementwrapper SecondElementwrapper { get; set; }
}
public class SecondElementwrapper
{
[XmlElement("element")]
public string Element { get; set; }
[XmlElement("differentelement")]
public string Differentelement { get; set; }
}
}
i've got 3 classes (all deriving from the same base class) and i have to dynamicly fill a ListBox with the Property-Names.
I've tried like this
class Test : TestBase {
[NameAttribute("Name of the Person")]
public string PersonName { get; set; }
private DateTime Birthday { get; set; }
[NameAttribute("Birthday of the Person")]
public string PersonBDay {
get {
return this.bDay.ToShortDateString();
}
}
}
...
[AttributeUsage(AttributeTargets.Property)]
public class NameAttribute : Attribute {
public string Name { get; private set; }
public NameAttribute(string name) {
this.Name = name;
}
}
Is there a possibility to look in my object for all properties which has the attribute NameAttribute and get the string form the Name property of NameAttribute?
You can inspect each property from Type.GetProperties and then filter the ones that have the required attribute with the MemberInfo.GetCustomAttributes method.
With a little bit of LINQ, this would look like:
var propNameTuples = from property in typeof(Test).GetProperties()
let nameAttribute = (NameAttribute)property.GetCustomAttributes
(typeof(NameAttribute), false).SingleOrDefault()
where nameAttribute != null
select new { Property = property, nameAttribute.Name };
foreach (var propNameTuple in propNameTuples)
{
Console.WriteLine("Property: {0} Name: {1}",
propNameTuple.Property.Name, propNameTuple.Name);
}
By the way, I also recommend declaring the attribute to be single-use only with AllowMultiple = false in the AttributeUsage decoration.