How to maximize the efficiency of the FlowLayoutPanel? - c#

The basic goal is to create a Business Card for each contact and put that card in a FlowLayoutPanel. The card itself is comprised of about 10 labels and 3 comboboboxes.
It doesn't take too many business cards to cause initial population of the panel to take a long time. Maybe I can find a way to deal with this
However, a primary concern is crashing. It only takes about 200 or so cards to crash (run out of handles).
I agree there are paging methods that could be implemented since a user never will need to see more than will fit on 1 or 2 screens at a time, but writing paging routines might be pretty tough.
Any suggestions on how to maximize efficiency of this planned FlowLayoutPanel?
P.S. The main issue is running out of handles (too many controls in the panel). Must resolve this before worrying about speed.

Okay, how about:
Create a class to store the information necessary to recreate a business card (10 labels and 3 comboboboxes) as public, gettable/settable properties, and with an empty default public constructor. Then, serialize each business card as an xml (or binary) file using XmlSerializer into a single folder. Then, you could use something like string[] businessCards = Directory.GetFiles(Path.GetFullPath("mysettings\\businesscards")); to iterate through your long 'cached' list of business cards. Have class that manages adding/removing items from your FlowLayoutPanel by calling a function like: SerializableBusinessCardClass GetNextCard() {}. This would be fairly simple to implement. You could also serialize a List<SerializableBusinessCardClass> with a length of about 5 (or however many you wanted to load in at once) to a single XML file for maximum efficiency, or if you have a truly ridiculous amount of business cards (such that explorer lags when browsing the folder). While a binary serialization would be faster, the XML approach has the added benefit that your clients can specify new business cards they want you to display by creating the XML file yourself.
Here, I will give you a concrete example of how you would build and serialize such a data structure:
public class SerializableBusinessCard
{
public SerializableBusinessCard() { }
public string Name { get; set; }
public string Company { get; set; }
public List<string> Labels { get; set; }
public List<ComboItem> ComboBoxes { get; set; }
}
public class ComboItem
{
public ComboItem() { }
public string Name { get; set; }
public string Text { get; set; }
public int SelectedIndex { get; set; }
public Point Location { get; set; }
public Size size { get; set; }
public List<string> Collection{ get; set; }
}
Usage:
public void stackoverflow_BusinessCard_FlowLayoutPanel()
{
List<string> labels = new List<string>();
labels.Add("Title");
labels.Add("Description");
labels.Add("Phone");
labels.Add("Email");
labels.Add("Address");
labels.Add("label6");
labels.Add("labelN");
ComboItem cItem = new ComboItem();
cItem.Collection = new List<string>();
cItem.Collection.Add("Option1");
cItem.Collection.Add("Option2");
cItem.Name = "comboName";
cItem.SelectedIndex = 0;
cItem.Text = cItem.Collection[cItem.SelectedIndex];
cItem.Location = new Point(50, 265);
cItem.size = new Size(100,21);
List<ComboItem> comboItems = new List<ComboItem>();
comboItems.Add(cItem);
SerializableBusinessCard bCard1 = new SerializableBusinessCard();
bCard1.Name = "CompanyXYZ_BlueTheme";
bCard1.Company = "CompanyXYZ";
bCard1.Labels = labels;
bCard1.ComboBoxes = comboItems;
SerializeObjectXML("BusinessCard_392.xml",bCard1);
SerializableBusinessCard loaded = DeserializeBusinessCardXML("BusinessCard_392.xml");
}
Here is the serialization function:
public void SerializeObjectXML(string filename,object obj)
{
using(StreamWriter streamWriter = new StreamWriter(filename))
{
XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
xmlSerializer.Serialize(streamWriter,obj);
}
}
Result:
<?xml version="1.0" encoding="utf-8"?>
<SerializableBusinessCard xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>CompanyXYZ_BlueTheme</Name>
<Company>CompanyXYZ</Company>
<Labels>
<string>Title</string>
<string>Description</string>
<string>Phone</string>
<string>Email</string>
<string>Address</string>
<string>label6</string>
<string>labelN</string>
</Labels>
<ComboBoxes>
<ComboItem>
<Name>comboName</Name>
<Text>Option1</Text>
<SelectedIndex>0</SelectedIndex>
<Location>
<X>50</X>
<Y>265</Y>
</Location>
<size>
<Width>100</Width>
<Height>21</Height>
</size>
<Collection>
<string>Option1</string>
<string>Option2</string>
</Collection>
</ComboItem>
</ComboBoxes>
</SerializableBusinessCard>
And the deserializer:
public static SerializableBusinessCard DeserializeBusinessCardXML(string filename)
{
SerializableBusinessCard result = new SerializableBusinessCard();
using(StreamReader streamReader = new StreamReader(filename))
{
XmlSerializer xmlReader = new XmlSerializer(typeof(SerializableBusinessCard));
result = (SerializableBusinessCard) xmlReader.Deserialize(streamReader);
}
return result;
}
Hope this helps.

Related

Deserialization of serialized data fails

I am trying to deserialize an XML document that I am also serializing at another time. I am using it to store a configuration file.
This is my Code:
namespace OrderTracker
{
[Serializable]
public class AutofillValues
{
private string fileName = Directory.GetCurrentDirectory() + "\\bin\\settings.db";
public ComboBox.ObjectCollection Vendors { get; set; }
public ComboBox.ObjectCollection Products { get; set; }
public ComboBox.ObjectCollection Companies { get; set; }
public void save(AutofillValues afv)
{
if (!File.Exists(fileName))
{
FileStream fs = File.Create(fileName);
fs.Close();
}
XmlSerializer x = new XmlSerializer(typeof(AutofillValues));
TextWriter writer = new StreamWriter(fileName);
x.Serialize(writer, afv);
writer.Close();
}
public AutofillValues load()
{
XmlSerializer x = new XmlSerializer(typeof(AutofillValues));
TextReader file = new StreamReader(fileName);
AutofillValues av = (AutofillValues)x.Deserialize(file);
file.Close();
return av;
}
}
}
The error message that I am getting when trying to deserialize the file is this;
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Xml.dll
Additional information: There is an error in XML document (2, 2).*
This is the XML document:
<?xml version="1.0" encoding="utf-8"?>
<AutofillValues xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Vendors>
<anyType xsi:type="xsd:string">Test Vendor</anyType>
</Vendors>
<Products>
<anyType xsi:type="xsd:string">Test Product</anyType>
</Products>
<Companies>
<anyType xsi:type="xsd:string">Test Company</anyType>
</Companies>
</AutofillValues>
How can I deserialize the XML file and get back the serialized data?
I just changed this part and it worked for me.
You can not deserialize the XML back, because the class ComboBox.ObjectCollection does not have a standard (parameterless) constructor. This is a limitation of the XmlSerializer class, as stated in this SO post.
There is however another problem with your current code - even if the deserialization somehow works, than you still need to assign the collection to a ComboBox control, which the deserializer still can't do.
Instead of using the ComboBox.ObjectCollection class to store the items, I would suggest using either an array or a list of objects (as #kenlacoste suggested). Such collections can be easily inserted into the ComboBox using
the comboBox.Items.AddRange(arrayOfObjects) method.
Another refactoring would be to extract the serialization logic of the data class. Currently it is confusing to save and load the data, because I presume you want to save/fill the caller object:
save: object.save(object); - you can use the this keyword in the save method
load: object = object.load(); - same here, there is no need to return the value, use the this keyword to fill the existing properties
The changed code:
public class AutofillValues
{
private string fileName = #"d:\settings.db";
public object[] Vendors { get; set; }
public object[] Products { get; set; }
public object[] Companies { get; set; }
public void save()
{
XmlSerializer x = new XmlSerializer(typeof(AutofillValues));
// with using there is no need to close the writer explicitely
// second parameter - file is created if it does not exist
using (var writer = new StreamWriter(fileName, false))
{
x.Serialize(writer, this);
}
}
public void load()
{
XmlSerializer x = new XmlSerializer(typeof(AutofillValues));
AutofillValues av = (AutofillValues)x.Deserialize(new StreamReader(fileName));
this.Companies = av.Companies;
this.Vendors = av.Vendors;
this.Products = av.Products;
}
}
IMO the modified code is easier to read and understand:
var afv = new AutofillValues();
afv.load();
//use avf.Products
// or afv.save();
I would also suggest to extract the data that needs to be saved in an extra class, for example:
[Serializable]
public class AutofillValuesData
{
public Object[] Vendors { get; set; }
public Object[] Products { get; set; }
public Object[] Companies { get; set; }
}
In the class AutofillValues remove the three properties and leave just one:
public AutofillValuesData Data { get; set; }
Then the logic can be modified to fill the ComboBox controls from the filled data object. This way your data will not be hardwired to the UI and this would make the code more maintainable. You can use a helper like AutoMapper to remove the repetitive code (like mappig objA.Vendors to objB.Vendors).

decoupled components passing result c#

I have a program that receives files from clients and do some operations on files and save them on disk or won’t save them.
For decoupling of jobs, I created an interface named IFileEditor. Every component that do something on file, should implement this interface:
public interface IFileEditor
{
string Name { get; set; }
byte[] Content { get; set; }
string EditedName { get; set; }
byte[] EditedConent { get; set; }
string ComponentName { get; }
XmlDocument Config { get; set; }
XmlDocument Result { get; set; }
void EditFile(byte[] content);
}
Main method in this interface is EditFile that receives file contents and do operations, and maybe in last save the result on disk.
Sample class that I wrote is that create a thumbnail from image that implements this interface:
public class ThumbnailCreator : IFileEditor
{
public string Name { get; set; }
public byte[] Content { get; set; }
public sting EditedName { get; set; }
public byte[] EditedConent { get; set; }
public XmlDocument Config { get; set; }
public XmlDocument Result { get; set; }
public void EditFile(byte[] content)
{
//change the file content and save the thumbnail content in disk
}
}
I may have lots of components like ThumbnailCreator, for example zip content or anything else that do operation on content.
In main program I load every component by reflection. Implementation of loading them is not important, just know that copying ddl of component beside .exe of main program and if dll implements IFileEditor, I add that to list.
Main question is that, main application just receives files and pass them to components, and components do the jobs. If I want to pass the result of one component to another, what should I do?
Remember that components doesn't know each other and main program should not interfere in passing results.
I searched, and I think chain-of-responsibility design pattern will solve my question. I don’t know is that correct? If correct how to implement this?
For example one component creates the thumbnail and pass the result to compress the thumbnail.
I wrote this part like this, that every developer can create a component and main program could be extendable.
Thanks for reading this large post. ;)
Yes, the chain-of-responsibility pattern is what you could use to solve this design problem. You basically need to make sure that each processor knows the next processor in the chain and calls it and have some sort of runner that configures the processor chain once, starts the processing operation and collects the end result. There are many use cases for such a pattern, System.Net.Http.DelegatingHandler (http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler(v=vs.110).aspx) works like this. Java ServletFilters are conceptually the same thing as well.
You could also just keep your processors in a collection, iterate that collection and apply each processor to your input by calling a specific method, i.e. EditFile in your example.
Update -- here's a naive implementaiton to illustrate what I mean (taken from LINQPad):
void Main()
{
// Variant 1 - Chaining
var editorChain = new UpperCaseFileEditor(new LowerCaseFileEditor());
var data1 = new char[] { 'a', 'B', 'c' };
editorChain.Edit(data1);
data1.Dump(); // produces ['a','b','c']
// Variant 2 - Iteration
var editors = new List<IFileEditor> { new LowerCaseFileEditor(), new UpperCaseFileEditor() };
var data2 = new char[] { 'a', 'B', 'c' };
foreach (var e in editors) {
e.Edit(data2);
}
data2.Dump(); // produces ['A','B','C']
}
// Define other methods and classes here
public interface IFileEditor {
IFileEditor Next { get; set; }
void Edit(char[] data);
}
public class UpperCaseFileEditor : IFileEditor {
public IFileEditor Next { get; set; }
public UpperCaseFileEditor() : this(null) {}
public UpperCaseFileEditor(IFileEditor next) {
Next = next;
}
public void Edit(char[] data) {
for (int i = 0; i < data.Length; ++i) {
data[i] = Char.ToUpper(data[i]);
}
if (Next != null)
Next.Edit(data);
}
}
public class LowerCaseFileEditor : IFileEditor {
public IFileEditor Next { get; set; }
public LowerCaseFileEditor() : this(null) {}
public LowerCaseFileEditor(IFileEditor next) {
Next = next;
}
public void Edit(char[] data) {
for (int i = 0; i < data.Length; ++i) {
data[i] = Char.ToLower(data[i]);
}
if (Next != null)
Next.Edit(data);
}
}
Please take into consideration that this is a just an illustration and I won't claim that this will scale to a production/real-world use case :-). Depending on what you really do, you might need to work on performance improvements, it might be quite handy to work with streams instead of byte/char arrays for example.

XmlSerializer exception with large amount of objects

I am storing DLL information into the following class:
[XmlRoot("Modules")]
[XmlInclude(typeof(Module))]
public class Modules : List<Module> {}
[XmlType("Module")]
public class Module
{
[XmlIgnore()]
public bool Selected { get; set; }
[XmlElement()]
public string Name { get; set; }
[XmlElement()]
public string Version { get; set; }
[XmlElement()]
public byte[] Binary;
}
When I serialize a handful of Module classes in the list using the following code, it is fine.
XmlSerializer serializer = new XmlSerializer(typeof(Modules));
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, ugModules.DataSource);
}
However, my problem comes when within the list, I have 113 items - now obviously that is quite a lot of data with the binary.
I am getting a System.OutOfMemory exception, I seem to remember having to increase a threshold of some sort but I can't find the correct property or method for what exactly I have to change.
I am serializing this to XML to pass the XML into SQL and then store the information within a table, just for a bit of scope on what exactly I am doing.
How do I get around the exception? Is there a better way to do this?

How to pass multidimensional list or tuple to C# Web Service

I'm working on a web service that needs to accept a collection with three values of different types. The values are
SkuNumber (integer),
FirstName (string),
LastName (string)
I want the web service to accept a list of 100 instances of these values but am not sure how to go about it. Do I use a multidimensional list or array? Maybe a tuple? Or can I just create a simple class structure and accept a list of that class?
This is all simple enough in a normal application, I'm just not sure how the app calling the web service would pass the data with any of the given options.
Can someone give me some pointers?
If a shared assembly is not feasible, you can always go with good ol' XML. It may not be the optimal solution and I'm sure plenty of users here will balk at the idea, but it is easy to support and relatively quick to implement, so it really depends on your individual situation and the skill level of the developers responsible for supporting the application.
The benefit to using XML here, is that the calling application can be written in almost any language on almost any platform, as long as it adheres to the XML structure.
The XML string should be easy enough to generate in the calling application, but the biggest downside here is that if you have a ton of data, the processing may take longer than desired -- on both ends of the service.
Here is a working sample if you want to give it a try:
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[WebMethod]
public void HelloWorld(string xmlString)
{
//make all the node names + attribute names lowercase, to account for erroneous xml formatting -- leave the values alone though
xmlString = Regex.Replace(xmlString, #"<[^<>]+>", m => m.Value.ToLower(),RegexOptions.Multiline | RegexOptions.Singleline);
var xmlDoc = LoadXmlDocument(xmlString);
var listOfStuff = new List<Whatever>();
var rootNode = xmlDoc.DocumentElement;
foreach(XmlNode xmlNode in rootNode)
{
var whatever = new Whatever
{
FirstName = xmlNode["first_name"].InnerText,
LastName = xmlNode["last_name"].InnerText,
SkuNumber = Convert.ToInt32(xmlNode["sku_number"].InnerText)
};
listOfStuff.Add(whatever);
}
}
public static XmlDocument LoadXmlDocument(string xmlString)
{
//some extra stuff to account for URLEncoded strings, if necessary
if (xmlString.IndexOf("%3e%") > -1)
xmlString = HttpUtility.UrlDecode(xmlString);
xmlString = xmlString.Replace((char)34, '\'').Replace("&", "&").Replace("\\", "");
var xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = false;
xmlDocument.LoadXml(xmlString);
return xmlDocument;
}
Your XML would look like this:
<stuff_to_track>
<whatever>
<sku_number>1</sku_number>
<first_name>jimi</first_name>
<last_name>hendrix</last_name>
</whatever>
<whatever>
<sku_number>2</sku_number>
<first_name>miles</first_name>
<last_name>davis</last_name>
</whatever>
<whatever>
<sku_number>3</sku_number>
<first_name>david</first_name>
<last_name>sanborn</last_name>
</whatever>
<whatever>
<sku_number>4</sku_number>
<first_name>john</first_name>
<last_name>coltrane</last_name>
</whatever>
</stuff_to_track>
I also recommend validating the incoming XML, for both schema and data.
Create a class and accept a list of that class. Be sure to mark it as [Serializable].
[Serializable]
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Best practice would be to define the class in an assembly that can be accessed by both the service and the project that calls it.
The trouble with a tuple or a multi-dimensional array is that the data you send doesn't have an inherent identity: you could stick any old thing in there. If you have a class, you are indicating that you are sending an Order or an Inquiry or a Coupon or whatever it is you are tracking. There's a level of meaning that goes along with it.
Just send what you want:
public class Whatever
{
public int SkuNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
[WebMethod]
public void TakeList(List<Whatever> theList)
{
foreach (var w in theList)
{
}
}

DataContractSerializer and its issues - search for better serializer

We've already established previously that DCS serializes/deserializes objects alphabetically. However, after further investigation I've discoverred this is not entirely true.
If we have a structure like this:
[DataContract]
public class Content
{
[DataMember]
public string Title;
[DataMember]
public string Slug;
[DataMember]
public string Description;
}
[DataContract]
public class TextContent : Content
{
[DataMember]
public string Text;
}
and have an object of type TextContent to serialize, DCS will do this:
<Content i:type="TextContent" ...>
<Description>desc</Description>
<Slug>some-slug</Slug>
<Title>content title</Title>
<Text>some content</Text>
</Content>
So as you can see, the property from the inheriting class is attached to the end of the serialized XML fragment even though it should be before Title. DCS doesn't go over the combined properties and reorder them.
I've noticed this when I was manually adding Text element in front of Title element and deserialization just didn't want to work. That's why I performed a serialization of a new object and figured this out.
My questions are:
This can't be common knowledge?? Anyone else noticed this?
Anyone knows of a better serializer (all I ever find if I search for it is the old XmlSerializer and DCS) because this issue with DCS's ordering is extremely annoying? I know we can use the Order attribute but that only enables us to align with one external XML source. What if we have three, four or more third party XML providers which all generate perfectly valid XML but our app is nitpicking about elements order (because of DCS)?
The base types are always first in the order. You can define the order of the serialized properties of a object with respect of the Order property of the DataMember attribute (see http://msdn.microsoft.com/en-us/library/ms729813.aspx)
There's the NetDataContractSerializer but the only difference between it and the DCS is that it enables type sharing between the client and the server but you lose the forward compatibility because both sides have to serialize/deserialize into the same type..
There's also the C# wrapper for protocol buffer on codeplex:
http://code.google.com/p/protobuf-net/
I haven't tried it out myself, but it's supposed to be much faster and lightweight. As to your actual questions:
doubt it, I certainly never
noticed this :-P
can you give an example where the ordering of elements actually mattered? I haven't come across this myself (I guess that's why most of us haven't noticed this behavior..), but with the proto-buf serializer this will undoubtly be a problem..
If you need to be able to serialize to match an external schema, then you obviously shouldn't be using the DataContractSerializer. That's not what it's for.
You can either use the XmlSerializer, which is intended to give you more control over the serialized XML, or implement IXmlSerializable, and gain complete control over the XML, or you can write your own custom serialization using LINQ to XML. This will let you do exactly what you mention - serialize the same data in different ways. Example:
Data
internal class Person
{
internal string Name { get; set; }
internal string Telephone { get; set; }
internal Address HomeAddress { get; set; }
internal Address WorkAddress { get; set; }
}
internal class Address
{
internal string Line1 { get; set; }
internal string Line2 { get; set; }
internal string City { get; set; }
internal string State { get; set; }
internal string PostalCode { get; set; }
}
Test Program
private static void Main()
{
var person = new Person
{
Name = "John Saunders",
Telephone = "something",
HomeAddress = new Address
{
Line1 = "Line 1",
Line2 = "Line 2",
City = "SomeCity",
State = "SS",
PostalCode = "99999-9999",
},
WorkAddress = new Address
{
Line1 = "Line 1a",
Line2 = "Line 2a",
City = "SomeCitay",
State = "Sa",
PostalCode = "99999-999a",
},
};
XDocument personWithElements = SerializeAsElements(person);
personWithElements.Save("PersonWithElements.xml");
XDocument personWithAttributes = SerializeAsAttributes(person);
personWithAttributes.Save("PersonWithAttributes.xml");
}
Serialization as Elements:
private static XDocument SerializeAsElements(Person person)
{
return new XDocument(
new XElement("Person",
new XElement("Name", person.Name),
new XElement("Telephone", person.Telephone),
SerializeAddressAsElements(person.HomeAddress, "HomeAddress"),
SerializeAddressAsElements(person.WorkAddress, "WorkAddress"))
);
}
private static XElement SerializeAddressAsElements(Address address, string elementName)
{
return new XElement(elementName,
new XElement("Line1", address.Line1),
new XElement("Line2", address.Line2),
new XElement("City", address.City),
new XElement("State", address.State),
new XElement("PostalCode", address.PostalCode)
);
}
Serialization as Attributes:
private static XDocument SerializeAsAttributes(Person person)
{
return new XDocument(
new XElement("Person",
new XAttribute("Name", person.Name),
new XAttribute("Telephone", person.Telephone),
SerializeAddressAsAttributes(person.HomeAddress, "HomeAddress"),
SerializeAddressAsAttributes(person.WorkAddress, "WorkAddress"))
);
}
private static XElement SerializeAddressAsAttributes(Address address, string elementName)
{
return new XElement(elementName,
new XAttribute("Line1", address.Line1),
new XAttribute("Line2", address.Line2),
new XAttribute("City", address.City),
new XAttribute("State", address.State),
new XAttribute("PostalCode", address.PostalCode)
);
}
PersonWithElements:
<?xml version="1.0" encoding="utf-8"?>
<Person>
<Name>John Saunders</Name>
<Telephone>somethine</Telephone>
<HomeAddress>
<Line1>Line 1</Line1>
<Line2>Line 2</Line2>
<City>SomeCity</City>
<State>SS</State>
<PostalCode>99999-9999</PostalCode>
</HomeAddress>
<WorkAddress>
<Line1>Line 1a</Line1>
<Line2>Line 2a</Line2>
<City>SomeCitay</City>
<State>Sa</State>
<PostalCode>99999-999a</PostalCode>
</WorkAddress>
</Person>
PersonWithAttributes:
<?xml version="1.0" encoding="utf-8"?>
<Person Name="John Saunders" Telephone="somethine">
<HomeAddress Line1="Line 1" Line2="Line 2" City="SomeCity" State="SS" PostalCode="99999-9999" />
<WorkAddress Line1="Line 1a" Line2="Line 2a" City="SomeCitay" State="Sa" PostalCode="99999-999a" />
</Person>

Categories

Resources