Well, my question is a bit silly, but I've tried lots of different thing without result.
I have a ComboBox in my main form and I want to point its data source to the public readonly List PriceChanges list declared in the Filters class. No problem with that but I want to list the Description field.
I tried to assign the "Description" string to the DisplayMember attribute without success. My ComboBox only lists: "BusinessLogic.PriceChange" for each entry, where BusinessLogic is the name of my Namespace and PriceChange the class.
I appreciate any help.
Regards
That's part of the code of my main form
public mainFrm()
{
InitializeComponent();
prodFilter = new Filters();
cbPriceChanges.DataSource = prodFilter.PriceChanges;
cbPriceChanges.DisplayMember = "Description"
}
That's is part of the code that declares the List object
public enum PriceChangeTypes
{
No_Change,
Increased,
Decreased,
All
}
public class PriceChange
{
public String Description;
public readonly PriceChangeTypes Type;
public delegate bool ComparisonFuntionDelegate(Decimal a);
public readonly ComparisonFuntionDelegate ComparisonFunction;
public PriceChange(String Description, PriceChangeTypes type , ComparisonFuntionDelegate CompFunc)
{
this.Description = Description;
Type = type;
ComparisonFunction = CompFunc;
}
}
public class Filters
{
public readonly List<PriceChange> PriceChanges = null;
public Filters()
{
PriceChanges = new List<PriceChange>();
PriceChanges.Add(new PriceChange("No Change", PriceChangeTypes.No_Change, PriceChange => PriceChange == 0));
PriceChanges.Add(new PriceChange("Increased", PriceChangeTypes.Increased, PriceChange => PriceChange > 0));
PriceChanges.Add(new PriceChange("Decreased", PriceChangeTypes.Decreased, PriceChange => PriceChange < 0));
PriceChanges.Add(new PriceChange("All", PriceChangeTypes.All, a => true));
}
}
Have you tried making "Description" a property? It will change a lot in case the list tries to get the field through reflection (as it most likely does).
public class PriceChange {
public string Description{
get;
set;
}
// ...
}
Try adding this to your class :
public override string ToString()
{
return Description;
}
Currently you're just getting the default value of ToString, which is the object namespace and class
Related
I have a stucture of classes and subclasses as follows:
public class Regions
{
public const string UNITEDSTATES = "United States";
public static string[] members = { UNITEDSTATES};
public static class UnitedStatesTypes
{
public const string STEEL = "steel";
public const string CONCRETE = "concrete";
public static string[] members = { STEEL, CONCRETE };
public static class SteelStandards
{
public const string A36 = "ASTM A36";
public static string[] members = { A36 };
public static class A36Grades
{
public const string GRADE_36 = "Grade 36";
public static string[] members = { GRADE_36 };
}
public static class ConcreteStandards
{
...
}
There are more values under each one of the classes, but this is just a small sample so you can get the idea of what it looks like. I am trying to create a UI to select each one of these. There are 4 dropdown menus, each menu is populated by the value of the higher menu. So if the standards dropdown is on SteelStandards, the next dropdown is populated with A36, if it was on ConcreteStandards the next would be populated with the data under ConcreteStandards. Is there a way that I can access a subclass using a string variable?
For example, the first dropdown will select United States. The next dropdown needs to piece together "UnitedStatesTypes" and then access Regions.UnitedStatesTypes.members. I have tried using braces
Regions["UnitedStatesTypes"].members
but this did not work. Is there a way to make this happen? Or is there a better way to organize my data?
You could do this with just dictionaries, albeit it gets a bit unwieldy as you go down the tree:
var regions = new Dictionary<string,Dictionary<string,Dictionary<string,Dictionary<string,List<string>>>>>();
// populate it. Yes I know how ugly that will look!
var usSteelStandards = regions["United States"]["Steel"]["Standards"];
A better way might be to refactor your code as a set of class instances, instead of trying to use static classes/members all the way. It is a typical tree structure
public class Node : IEnumerable<Node>
{
public Node(string text)
{
this.Text = text;
this.Children = new List<Node>();
}
public string Text {get; private set;}
public List<Node> Children { get; private set;}
public Node this[string childText]
{
get{ return this.Children.FirstOrDefault(x => x.Text == childText); }
}
public void Add(string text, params Node[] childNodes)
{
var node = new Node(text);
node.Children.AddRange(childNodes);
this.Children.Add(node);
}
public IEnumerator<Node> GetEnumerator()
{
return Children.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
This can then be setup and used much easier
var node = new Node("root")
{
{
"United States",
new Node("Steel")
{
{
"ASTM A36",
new Node("Grade 36")
}
},
new Node("Concrete")
{
}
}
};
Console.WriteLine(node["United States"].Children.Count);
Console.WriteLine(node["United States"]["Steel"]["ASTM A36"].Children[0].Text);
Live example: https://rextester.com/QVGN99585
Ive come across multiple questions and answers on here but none specific to my situation.
I have a class 'Entity' with multiple classes that extend off of it. I want the serialization to hit the list and understand and use the type of each item for the node name.
Now, I can use what is commented out (define each array item in the main class and define the name of such by using [XmlArrayItem("Subclass1", typeof(subclass1)] but I want to keep all definitions in their subclass and I will be having too many subclasses to define everything in the main entity class...Is there anyway to achieve this?
I have tried using [XmlType(TypeName="...")] for the subclasses and so on but that did not work.
[Serializable]
[XmlInclude(typeof(Subclass1))]
[XmlRoot("Entity")]
public class Entity{
[XmlArray("CausedBy")]
//[XmlArrayItem("Subclass1", typeof(subclass1))]
//[XmlArrayItem("Sublcass2", typeof(Subclass2))]
public List<Entity> CausedBy { get; set; }
}
[Serializable]
[XmlRoot("Subclass1")]
[XmlInclude(typeof(Subclass2))]
public class Subclass1:Entity{
//Code...
}
[Serializable]
[XmlRoot("Subclass2")]
public class Subclass2:Subclass1{
//Code...
}
Serializing the above code after creating an entity and adding a Subclass1 and Subclass2 to the list 'CausedBy' class results in the following:
<Entity>
<CausedBy>
<Entity ... xsi:type="SubClass1" />
<Entity ... xsi:type="SubClass2" />
</CausedBy>
<Entity>
I would like the output to show:
<Entity>
<CausedBy>
<SubClass1 .../>
<SubClass2 .../>
</CausedBy>
<Entity>
Since I totally failed to read the question to begin with, here's a new answer (it's a bit of a tl;dr, so you can always skip to the end and follow the link):
It isn't possible to get the built in serializer class to work because you don't wish to add the attributes that it needs to be able to operate. Your only option is to seralize the class yourself, however, this need not be as tedious as it sounds; I had a similar issue a few years ago with DataGridView in virtual mode and produced a generic virtualizer that could be used to virtualize the data for display; it used a custom attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class showColumnAttribute : System.Attribute
{
///<summary>Optional display format for column</summary>
public string Format;
///<summary>Optional Header string for column<para>Defaults to propety name</para></summary>
public string Title;
///<summary>Optional column edit flag - defaults to false</summary>
public bool ReadOnly;
///<summary>Optional column width</summary>
public int Width;
///<summary>
///Marks public properties that are to be displayed in columns
///</summary>
public showColumnAttribute()
{
Format = String.Empty;
Title = String.Empty;
ReadOnly = false;
Width = 0;
}
}
And a constructor:
///<summary>
///Extracts the properties of the supplied type that are to be displayed
///<para>The type must be a class or an InvalidOperationException will be thrown</para>
///</summary>
public Virtualiser(Type t)
{
if (!t.IsClass)
throw new InvalidOperationException("Supplied type is not a class");
List<VirtualColumnInfo> definedColumns = new List<VirtualColumnInfo>();
PropertyInfo[] ps = t.GetProperties();
MethodInfo mg, ms;
for (int i = 0; i < ps.Length; i++)
{
Object[] attr = ps[i].GetCustomAttributes(true);
if (attr.Length > 0)
{
foreach (var a in attr)
{
showColumnAttribute ca = a as showColumnAttribute;
if (ca != null)
{
mg = ps[i].GetGetMethod();
if (mg != null)
{
ms = ps[i].GetSetMethod();
definedColumns.Add
(
new VirtualColumnInfo
(
ps[i].Name, ca.Width, ca.ReadOnly, ca.Title == String.Empty ? ps[i].Name : ca.Title,
ca.Format, mg, ms
)
);
}
break;
}
}
}
}
if (definedColumns.Count > 0)
columns = definedColumns.ToArray();
}
This extracts the public properties of the class and supplies marked items to the DataGridView as columns together with a header, format, etc.
The effect of all of this (and the rest of the missing code) was that any type could be virtualized in a dataGridView simply by tagging public properties and calling the virtualizer once for a given type:
#region Virtualisation
static readonly Virtualiser Virtual = new Virtualiser(typeof(UserRecord));
[XmlIgnore] // just in case!
public static int ColumnCount { get { return Virtual.ColumnCount; } }
public static VirtualColumnInfo ColumnInfo(int column)
{
return Virtual.ColumnInfo(column);
}
public Object GetItem(int column)
{
return Virtual.GetItem(column, this);
}
/*
** The supplied item should be a string - it is up to this method to supply a valid value to the property
** setter (this is the simplest place to determine what this is and how it can be derived from a string).
*/
public void SetItem(int column, Object item)
{
String v = item as String;
int t = 0;
if (v == null)
return;
switch (Virtual.GetColumnPropertyName(column))
{
case "DisplayNumber":
if (!int.TryParse(v, out t))
t = 0;
item = t;
break;
}
try
{
Virtual.SetItem(column, this, item);
}
catch { }
}
#endregion
The number of columns, their properties and order can be specified automatically by creating a number of public properties derived from the class data:
#region Display columns
[showColumn(ReadOnly = true, Width = 100, Title = "Identification")]
public String DisplayIdent
{
get
{
return ident;
}
set
{
ident = value;
}
}
[showColumn(Width = 70, Title = "Number on Roll")]
public int DisplayNumber
{
get
{
return number;
}
set
{
number = value;
}
}
[showColumn(Width = -100, Title = "Name")]
public string DisplayName
{
get
{
return name == String.Empty ? "??" : name;
}
set
{
name = value;
}
}
#endregion
This would virtualize any class for dataGridView to display and edit data and I used it many times over the years and the extraction of properties to display is exactly what is required for XML serialization, indeed, it has a lot of the same characteristics.
I was going to adapt this method to do the same job for XML serialization but someone has already done it at https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453, I hope you can make use of this method to solve your problem.
This works for me:
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Entity entity = new Entity();
entity.CausedBy = new List<Entity>();
entity.CausedBy.Add(new Subclass1());
entity.CausedBy.Add(new Subclass2());
entity.CausedBy.Add(new Subclass2());
entity.CausedBy.Add(new Subclass1());
entity.CausedBy.Add(new Subclass1());
entity.Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test.txt"));
}
}
[Serializable]
[XmlRoot("Entity")]
public class Entity
{
[XmlArray("CausedBy")]
[XmlArrayItem("SubClass1", typeof(Subclass1))]
[XmlArrayItem("SubClass2", typeof(Subclass2))]
public List<Entity> CausedBy { get; set; }
}
[Serializable]
[XmlRoot("Subclass1")]
public class Subclass1 : Entity
{
[XmlIgnore]
String t = DateTime.Now.ToShortDateString();
public String SubClass1Item { get { return "Test1 " + t; } set { } }
}
[Serializable]
[XmlRoot("Subclass2")]
public class Subclass2 : Entity
{
[XmlIgnore]
String t = DateTime.Now.ToString();
public String SubClass2Item { get { return "Test2 " + t; } set { } }
}
It produces:
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CausedBy>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
<SubClass2>
<SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item>
</SubClass2>
<SubClass2>
<SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item>
</SubClass2>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
</CausedBy>
</Entity>
I'm trying to Copy and Paste part of my list. Unfortunately, when pasting from the clipboard, the condition finds the stored data, but when assigned to the variable it is still equal to null.
This is my code for List and List Item.
public class ListCanvasBlocks : List<MyBlock>
{
public List<MyBlock> MyCopySelectedObj()
{
var x = new List<MyBlock>();
x.AddRange(this.Where(z => z.IsSelected));
return x;
}
}
[Serializable]
public class MyBlock
{
public MyBlock(Rectangle rect, BlocksData.Shape shape,int id)
{
Rect = rect;
Shape = shape;
Text = BlocksData.Text(Shape);
ID = id;
}
public string Text;
public bool IsSelected { get; set; } = false;
public bool IsLocked = false;
public int ID{ get; set; }
public Point PointInput;
public Point PointOutput1, PointOutput2;
public Rectangle Rect;
public SolidBrush BackColor;
public Color FontColor;
public int FontSize;
public BlocksData.Shape Shape{get;set;}
}
and this is what i do when i press ctrl+c/v
public void Copy()
{
Clipboard.Clear();
Clipboard.SetData("ListCanvasBlocks", _canvObj.MyCopySelectedObj());
}
public void Paste()
{
if (Clipboard.ContainsData("ListCanvasBlocks"))
{var test = (ListCanvasBlocks)Clipboard.GetData("ListCanvasBlocks");}
}
Condition in Paste method return true but variable test is still null after assigment
i'm trying use this solution
and this and few others
BlockData.Shape is enum
You are trying to cut and paste a ListCanvasBlocks but the MyCopySelectedObj() method returns a List<MyBlock>, which is a supertype. The runtime can't automatically cast from a general to specific.
Try changing this:
public class ListCanvasBlocks : List<MyBlock>
{
public ListCanvasBlocks MyCopySelectedObj()
{
var x = new ListCanvasBlocks();
x.AddRange(this.Where(z => z.IsSelected));
return x;
}
}
At the moment when I tried to individually add variables to the clipboard, it turned out that the problem was serialization. The problem was solved in SolodBrush because its attribute is Color, and only variables without attributes can be serialized.
There i found solution when i found what is real problem
Looks like MyCopySelectedObj() returns a List of MyBlock which gets assigned to the name "ListCanvasBlocks" on the Copy(), but when you call Paste(), you cast the List in the Clipboard to Object (ListCanvasBlocks). It is inherited from List, but still not object type ListCanvasBlocks. Try casting directly to List to that var test when you call your Paste().
I'm trying to add strings to a List<string> so I can print them with a loop in a certain point of time, being more specific here is part of my code:
public class Foo{
public string propertyA;
public string propertyB;
public string propertyC;
public List<string> list;
Public Foo(){
list = new List<string>();
list.Add(propertyA);
list.Add(propertyB);
list.Add(propertyC);
}
}
In later code, after assigning propertyA and the other variables and trying to iterate over the List I get empty strings. I require the properties to be in the list. My questions is which would be the best way to achieve this?
Looks like you are getting empty strings because when you are adding to the list the values in your properties have not been set at the time that the Foo() constructor is called...
Try passing values and setting them in the Foo constructor as follows:
public class Foo{
public string propertyA;
public string propertyB;
public string propertyC;
public List<string> list;
Public Foo(string propA, string propB, string propC){
propertyA = propA;
propertyB = propB;
propertyC = propC;
list = new List<string>();
list.Add(propertyA);
list.Add(propertyB);
list.Add(propertyC);
}
}
Alternatively you could add the values to the list at a later time when the properties are actually set and not in the constructor e.g.
public string PropertyA
{
//set the person name
set { propertyA = value;
list.Add(value);
}
//get the person name
get { return propertyA; }
}
...
What you're seeing is expected behavior. Updating "propertyA", etc later on won't update the strings that have already been added to the collection.
You could consider using a Dictionary instead of your own class, and then adding and updating elements is easier: (and you don't have to keep updating your class with new property names)
var properties = new Dictionary<string, string>();
properties.Add("propertyA", "some value of property A");
properties["propertyA"] = "some new value";
And when you want to display the values later:
MessageBox.Show(string.Join(Environment.NewLine, properties));
Alternatively, if you want a class and the option of adding properties to it, then maybe extending the Dictionary class like this will at least make things easier to maintain, so you can add more properties that'll stay in sync with the underlying Dictionary, with a minimum of fuss.
public class PropertyCollection : Dictionary<string, string>
{
public string PropertyA
{
get { return GetValue(); }
set { StoreValue(value); }
}
public string PropertyB
{
get { return GetValue(); }
set { StoreValue(value); }
}
protected string GetValue([CallerMemberName] string propName = "")
{
if (ContainsKey(propName))
return this[propName];
return "";
}
protected void StoreValue(string propValue, [CallerMemberName] string propName = "")
{
if (ContainsKey(propName))
this[propName] = propValue;
else
Add(propName, propValue);
}
}
If you want to assign propertyA, B, C after an instance of Foo is created and enumerate them, you could try something like this:
public class Foo
{
public string propertyA { get { return list[0]; } set { list[0] = value; } }
public string propertyB { get { return list[1]; } set { list[1] = value; } }
public string propertyC { get { return list[2]; } set { list[2] = value; } }
public List<string> list = new List<string>() {"", "", ""};
}
For the reasons why the code behaves in a way you might not expect, see How are strings passed in .NET?
I have a property which allows a user to select multiple enum values, and at the moment this works fine for saving the information into the database and for using it. However it doesn't seem to correctly read the values out of the property back to the edit UI.
I presume there is some sort of type issue with the enum which causes the SelectMany values not to be set as you'd expect.
I have the following enum:
public enum Skills
{
People,
IT,
Management,
Sales,
}
And the following ISelectionFactory:
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestSite.Business.EditorDescriptors
{
using EPiServer.Shell.ObjectEditing;
public class EnumSelectionFactory<TEnum> : ISelectionFactory
{
public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
var values = Enum.GetValues(typeof(TEnum));
return (from object value in values select new SelectItem { Text = this.GetValueName(value), Value = value }).OrderBy(x => x.Text);
}
private string GetValueName(object value)
{
return value.ToString();
}
}
}
Then I have the property which I've added to the ContactPage model in the Alloy Demo.
[SelectMany(SelectionFactoryType = typeof(EnumSelectionFactory<Skills>))]
public virtual string EmployeeLevels { get; set; }
Does anyone know how to solve this?
Seems to be a bug. Please report to EPiServer.
Set the underlying value type ...
namespace TestSite.Business.EditorDescriptors
{
using EPiServer.Shell.ObjectEditing;
public class EnumSelectionFactory<TEnum, TUnderlying> : ISelectionFactory
{
public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
{
var values = Enum.GetValues(typeof(TEnum));
return (from TEnum value in values select new SelectItem { Text = this.GetValueName(value), Value = Convert.ChangeType(value, typeof(TUnderlying)) }).OrderBy(x => x.Text);
}
private string GetValueName(object value)
{
return Enum.GetName(typeof(TEnum), value);
}
}
}
...implemented by your model with a string type ...
[SelectMany(SelectionFactoryType = typeof(EnumSelectionFactory<Skills,string>))]
public virtual string EmployeeLevels { get; set; }