Trivial data binding examples are just that, trivial. I want to do something a little more complicated and am wondering if there's an easy, built in way to handle it.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<DataStruct> list = new List<DataStruct>()
{
new DataStruct(){Name = "Name 1", Value = "Value 1", ComplexValue = new ComplexValue(){Part1 = "1:P1", Part2 = "1:P2"}},
new DataStruct(){Name = "Name 2", Value = "Value 2", ComplexValue = new ComplexValue(){Part1 = "2:P1", Part2 = "2:P2"}}
};
listBox1.DataSource = list;
listBox1.DisplayMember = "ComplexValue.Part1";
}
}
public class DataStruct
{
public string Name { get; set; }
public string Value { get; set; }
public ComplexValue ComplexValue { get; set; }
}
public class ComplexValue
{
public string Part1 { get; set; }
public string Part2 { get; set; }
}
Is there an easy way to get the value of the Part1 property to be set as the display member for a list of DataStruct items? Above I tried something that I thought made sense, but it just defaults back to the ToString() on DataStruct. I can work around it if necessary, I was just wondering if there was something built into the data binding that would handle more complex data binding like above.
Edit: Using WinForms
perhaps not a built-in way, but you could always define
DataStruct {
public ComplexValuePart1 {
get { return ComplexValue.Part1; }
}
}
and set your DisplayMember to that
Personally I'd go for a simple solution like Jimmy's - however, if you want to do this (with regular win-form bindings), you'd need to use a custom type descriptor to flatten Part1 / Part2 as virtual properties of DataStruct. You can do this via ICustomTypeDescriptor or TypeDescriptionProvider. The latter is more involved, but cleaner from a "separation of concerns" viewpoint.
If you really want I can prepare an example (or there are many I've done in the past floating around) - but simple is beautiful: if you only have a few properties, a simple facade (i.e. pass-thru properties on DataStruct) would be preferable.
Related
I have a class like this
public class basic
{
public bool Success { get; set; } = false;
public string Message { get; set; } = string.Empty;
}
public class ServiceResponse<T>:basic
{
public T? Data { get; set; }
}
public class ServiceResponse2<T> : basic
{
public T?[] Data { get; set; }
}
And I invoke it in my controller like this
ServiceResponse2<string> response = new ServiceResponse2<string>();
response.Success = true;
response.Message = "success";
response.Data[0] = filename;
response.Data[1] = outname;
when I do, I get runtime error in my lastline as: Object reference not set to an instance of an object. I hovered on top of Data variables and the values were null. Can I know what I'm missing here? Apologies if its a dumb doubt
The problem is that Data is an array, and this is never initialized to an object. You need to initialize it, for example:
response.Data = new string[]{filename, outname};
Or
public T?[] Data { get; set; } = new T[2];
However, I would be careful with using an array like this. What does Data means? How should it be used? Why can I change it however and whenever I want? How is it related to the other properties? Does a specific index have some special meaning? Does it promise to hold some specific number of items?
If this is intended to be used for requests to some type of service it is normal to use some form of serialization to convert objects to data. And this is normally done fairly close to the communication layer, so that most of the code can handle typed objects, and only a small part need to handle bits and bytes.
I was unable to define what to name this problem, thus tried google and here, but cant find.
Is there any way to set array(or dictionary) members value on property level, and set attribute onto them? like this:
public class XYZ{
private string[] x= new string[]{"smth", "smth"};
x[0] { get; set; }
[Receipt(order=2, name="warranty")]
x[1] { get; set; }
....
}
So, mainly I have to questions to be frank:
1) Is that possible to set attributes onto array members?
2) Is that possible (forget attribute) to assign value to array member in property level (not within method), like this?:
public class XYZ{
private string[] x= new string[2]();
x[0] = "smth";
}
The answer is definitely no. Attributes can only be added onto compile-time features such as classes and methods. Not onto runtime features such as object data.
Also your getter/setter syntax is very wrong, it can only be like this:
public int Month { get; set; }
So an access modifier (public; optional), followed by a Type (int), followed by the name (Month), and then the get and/or set specification.
You can't do it exactly the way you want, but you can get close:
public class XYZ
{
private string[] x= new string[]{"smth", "smth"};
[Receipt(order=1, name="warranty")]
public string Receipt1 {get { return x[0];} set{x[0] = value;} }
[Receipt(order=2, name="warranty")]
public string Receipt2 {get { return x[1];} set{x[1] = value;} }
//...
}
Of course, the number of entries here are fixed, and you have to give these properties a real, meaningful name. You can't have a dynamic number of entries that changes while the program runs.
The one other thing you can look at is an indexer property. With an indexer, you can only set your attribute once on the whole property, rather than individual elements, but it will allow the number of items to change at run time.
The short answer is no. Attributes are for metadata, and this looks like actual data. Probably you should have your array be an array of some class that contains the value (i.e. smth) and all the data you wanted to have in your ReceiptAttribute.
So something like:
public class Receipt
{
public string Value {get; set;}
public int Order {get; set;}
public string Name {get; set;}
}
And then:
public class XYZ
{
private Receipt[] x= new Receipt[]
{
new Receipt() { Value = "smth", Order = 1, Name = "warranty" },
new Receipt() { Value = "smth", Order = 2, Name = "warranty" },
};
}
And, obviously, when you want the value you'd do something like:
x[0].Value
For what i have been reading, with another Class I would be able to add the indexing to the property. But i am not managing to achieve the get/set of the "Option[x]" property of my custom "Poll" class.
public class Poll
{
//Constructor
public Poll() { }
//Properties
public string Title
{
get { return title; }
set { title = value; }
}
public Options Option { get; set; }
private string title;
}
public class Options
{
string[] option = { };
public string this[int i]
{
get { return option[i]; }
set { option[i] = value; }
}
}
When i try to add the first option to the poll, it says the object ("Options") has not being instantiated. And it does make sense. But I couldn't figure out where in Poll i would instantiate it.
So, can anyone explain me what am i doing wrong? Am i following the right direction? Or point to me further reading. For the solutions I have seen, this one seemed the most logical to me, but was only a small raw example, low on details.
I didn't want to follow the dictionary (Implementing indexing "operator" in on a class in C#) way, or "Option" property returning a List of strings.
Change:
public Poll() { }
To:
public Poll() { Option = new Options(); }
Also pay attention to "Wai Ha Lee" pointed out: "the indexer will always throw an IndexOutOfRangeException because the option array is always an empty array."
What he means is that you have to replace:
string[] option = { };
With:
string[] option = new string[X]; //X is Array size
Some sample code below. The interesting/problem case is the Data property in
Mad. This code blows up (null value in the enumerable). Also, it works if i don't use the static attributes but instead the runtime type model, where i put in member.SupportNull = true for the fields (which is the behaviour i want), so what am i missing in the attributes / settings? Google search seems to indicate this is an open issue with probuf-net? That the same functionality is not available via attributes?
As as aside, if someone could suggest a way - i really love the runtime type model, i want to use that everywhere with a nice compiled model... but with it i lose the object versioning that protocol buffers solves! (via explicit tags). Is there any good way to maintain object version compatibility (simply adding fields) without doing all the static notation with fixed tags?
Basically the key thing with the runtime model is the assignment of tag indices and i can't think of a way of handling versions without explicitly specifying the tag indices via attributes...
[ProtoContract]
[ProtoInclude(1, typeof(ing))]
public class Eff
{
[ProtoMember(2)]
public string gg { get; set; }
}
[ProtoContract]
public class ing : Eff
{
[ProtoMember(1)]
public int zz { get; set; }
}
[ProtoContract]
public class Mad
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public IEnumerable<ing> Data { get; set; }
[ProtoMember(3)]
public ing Single { get; set; }
}
private static void Main(string[] args)
{
var obj = new Mad
{
Name = "test"
,Data = new[] { new ing {gg = "ooga", zz = -101},null,new ing()}
,Single = new ing {gg = "abc", zz = -999}
};
var m = new MemoryStream();
Serializer.Serialize(m, obj);
m.Seek(0, SeekOrigin.Begin);
var copy = Serializer.Deserialize<Mad>(m);
}
Short answer, it seems unavailable via attributes.
Workaround i'm doing for now - for every single type of interest(including the whole inheritance hierarchy) - add it to the type model yourself (with default handling so that it processes attributes), then call .GetFields() and set .SupportNull = true for each field (or only the relevant one)
I've got a question about getting the values from a constructor in a generic way.
namespace myTestNamespace
{
Public Class myTestClass()
{
Public myTestClass(int myInt,bool myBool, double myDouble)
{
//do / set something
}
Public myTestClass(int myInt,bool myBool)
{
//do / set something
}
}
}
Using (what you need);
Using myTestNamespace;
namespace MyIWannaLookForTheParametersName
{
Public Class MyLookUpClass()
{
Public void DoSomething()
{
List<object> myList = new List<object>();
myTestClass _ myTestClass = new myTestClass(1,true,2.5);
object mySaveObject = myTestClass;
mylist.Add(mySaveObject);
//how do I get the info from the right constructor
//(I used the one with 3 parameters_
//what was the value of myInt, myBool and myDouble
//how can I make it generic enough, so it will work with other classes with
// different constructors ass well?
}
}
}
Questions about intent aside, there's no generic way for you to do this. Information about what methods have been called and what values were supplied is not saved automatically. You are, of course, perfectly able to keep track of these things yourself, but you would have to write each class to do this explicitly.
Doing this in a generic way is asking for trouble. What if I did this?
public class Foo
{
public string Name { get; set; }
}
public class Bar
{
public Bar(Foo foo)
{
// ...
}
}
Then suppose I called it in this way:
Foo f = new Foo();
f.Name = "Jim";
Bar b = new Bar(f);
f.Name = "Bob";
Now, if such a generic system existed, what would be the value of foo for the Bar constructor? Either it reports "Bob" (which is what the value for Name is on the instance of Foo that was supplied), or it reports "Jim", meaning that the runtime or library would essentially have to be smart enough to make a deep copy of the object so that the state is not changed.
The bottom line is this: if you need access to the parameters passed to the constructor (or any other function), you'll have to store them somewhere explicitly.
You can't get thevalues from the constructor. You need to first place them in a property or a field within your class. The example you provided is a poor use of generics. You wouldbe better off placing the constructor values into properties and creating an interface with those properties.
I got what I needed with this method:
private static ParameterSettings[] GetListOfParametersFromIndicator(object indicatorClass, int loopId, myEnums.ParaOrResult paraOrResult)
{
return (from prop in indicatorClass.GetType().GetProperties()
let loopID = loopId
let Indicator = indicatorClass.GetType().Name
let value = (object)prop.GetValue(indicatorClass, null)
where prop.Name.Contains("_Constr_")
select new ParameterSettings { ParaOrResult=paraOrResult, LoopID= loopId, Indicator= Indicator, ParaName= prop.Name, Value= value }).ToArray();
}
where ParameterSettings is:
public struct ParameterSettings
{
public myEnums.ParaOrResult ParaOrResult { get; set; }
public int LoopID { get; set; }
public string Indicator { get; set; }
public string ParaName { get; set; }
public object Value { get; set; }
}
This info is ok for me. Thanks for the replies.
Regards,
Matthijs