I am currently trying to combine two collections into one for binding to a combobox. I first started out with two static collections built within a class:
public partial class MainPage : UserControl
{
//create static observable collection
private ObservableCollection<string> items;
public ObservableCollection<string> Items
{
get
{
return this.items;
}
set
{
if (this.items != value)
{
this.items = value;
}
}
}
protected ObservableCollection<string> StaticItems
{
get
{
return new ObservableCollection<string>() { "Select User", "Select All" };
}
}
//create dynamic observable collection
public MainPage()
{
InitializeComponent();
this.items = this.StaticItems;
this.comboBox1.ItemsSource = this.Items;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
foreach (var item in GetDynamicItems())
{
this.Items.Add(item);
}
}
private List<string> GetDynamicItems()
{
return new List<string>() { "User1", "User2", "User3" };
}
The above works as desired.
What I would like to do now is to initate a query to a service and have the results of that service appended to the collection instead of User1, USer2,USer3
I create a query to the service as:
private void FillOfficerList()
{
QueryClient qc = new QueryClient("BasicHttpBinding_IQuery");
qc.GetOfficerNamesCompleted += new EventHandler<GetOfficerNamesCompletedEventArgs>(qc_GetOfficerNamesCompleted);
qc.GetOfficerNamesAsync();
}
public void qc_GetOfficerNamesCompleted(object sender, GetOfficerNamesCompletedEventArgs e)
{
// Now how do I add e.Results to above collection?
}
The query works I am just stuck on how to take the results ( e.Results) and bind/concat them to the Items collection. Any pointers or tips would be appreciated.
Note: This is for silverlight so using a composite collections approach does not seem to be an option as the class is not supported.
Thanks in advance
I just read your comment. Since you have the ObservableCollection with 3 strings and 1 int. Try doing this.
Lets assume you are having a Class say myClass which has the 3 strings and 1 int.
public class myClass()
{
string str1 {get; set;}
string str2 {get; set;}
string str3 {get; set;}
int int1 {get; set;}
}
Create an ObservableCollection in the client side with the same datatype.
ObservableCollection<myClass> collection = new ObservableCollection<myClass>();
public void qc_GetOfficerNamesCompleted(object sender, GetOfficerNamesCompletedEventArgs e)
{
// Now try adding this code
for(int i=0; i<e.Result.Count;i++)
{
// I do this because, I don't want the Client class to be unaware of the class myClass
collection.Add(new myClass()
{
str1 = e.Result[i].str1,
str2 = e.Result[i].str2,
str3 = e.Result[i].str3,
int1 = e.Result[i].int1
});
}
for(int i=0; i<collection.Count;i++)
{
Items.Add(collection[i].str1); // Add the string you want. I ve used str1 here.
}
}
Hope this helps.
Maybe I'm missing something, but as long as your service reference is using ObservableCollection as its collection type shouldn't you just be able to iterate over the results and Add() each item onto this.Items, just like you did with the dynamic items?
public void qc_GetOfficerNamesCompleted(object sender, GetOfficerNamesCompletedEventArgs e)
{
// Now how do I add e.Results to above collection?
foreach(var item in e.Results)
{
this.Items.Add(item);
}
}
I'm guessing I'm missing something. Can't you just do this?
public void qc_GetOfficerNamesCompleted(object sender, GetOfficerNamesCompletedEventArgs e)
{
foreach (var result in e.Results)
{
Items.Add(result);
}
}
If the service that is returning the result is of type ObservableCollection or if you are getting the result from the service as an Observable Collection(Say your service returns a List<> and if your collection type is ObservableCollection<>). You can append the items to the existing ObservableCollection. To confirm whether the return type of "e" is ObservableCollection:
Right Click the ServiceReference and Click Configure Service Reference. If the Collection type is List<>. You cannot add it to the ObservableCollection. So change it to ObservableCollection and if you wish the return type of the service also to ObservableCollection.
public void qc_GetOfficerNamesCompleted(object sender, GetOfficerNamesCompletedEventArgs e)
{
// Now try adding this code
for(int i=0; i<e.Result.Count;i++)
{
Items.Add(e.Result[i]); //Add individual item in the returning ObservableCollection to the items Collection
}
}
Hope this helps.
Thank you to everyone who helped out. Below is the final solution in working format. I had a couple of issues in the original code. Thanks to Aswin Ramakrishnan for indirectly pointing me to my collection types. I defaulted to usins obserColl when I should have referenced the original types from the WCF endpoint. This is where I was getting one error. The new code looks like this:
private ObservableCollection<MeDepartment> deptitems;
public ObservableCollection<MeDepartment> DeptItems
{
get
{
return this.deptitems;
}
set
{
if (this.deptitems != value)
{
this.deptitems = value;
}
}
}
protected ObservableCollection<MeDepartment> deptStaticItems
{
get
{
return new ObservableCollection<MeDepartment>()
{
new MeDepartment{Name = "Department"},
new MeDepartment{Name = "Select All"}
};
}
}
Next I needed to creat an onload event and query my WCF services for the department names
private void meFilter_Loaded(object sender, RoutedEventArgs e)
{
QueryClient qc = new QueryClient("BasicHttpBinding_IQuery");
qc.GetDepartmentsCompleted += new EventHandler<GetDepartmentsCompletedEventArgs>(qc_GetDepartmentsCompleted);
qc.GetDepartmentsAsync();
}
public void qc_GetDepartmentsCompleted(object sender, GetDepartmentsCompletedEventArgs e)
{
DeptItems = new ObservableCollection<MeDepartment>(deptStaticItems.Concat<MeDepartment>(e.Result));
DeptComboBox.ItemsSource = this.DeptItems;
DeptComboBox.SelectedIndex = 0;
}
Using the correct collection type (MeDepartment) allowed me to then properly concatonate the two collections together. (be sure to use the system.linq reference)
The final line was to repoint the combo box items source to the new collection.
Hope this helps others for future reference.
thanks again to all that contributed.
This is an old thread, but I just had this same issue and this is the solution I came up with.
interface IMyInterface{ string TheString{get;set}}
MyClass1 : IMyInterface {...}
MyClass2 : IMyInterface {...}
public ObservableCollection<IMyInterface> {get;set;}
You can then add both types to the collection without getting an error.
MyCollection.Add(new MyClass1());
MyCollection.Add(new MyClass2());
This is an example of combining two collection and sorting them:
this._sortedItems = new ObservableCollection<ILookupListItemEntity>(
LookupListItemEntities.Cast<ILookupListItemEntity>().Union(this.CustomLookupListItemEntities).OrderBy(a => a.Value).ToList());
Greg
Related
I'm having some trouble figuring out why the .selectedItem or .SelectedValue properties for my databound combobox are always returning a null value. I'm new to programming, so I'm sure I'm making a simple mistake, but I was unable to find a solution online that worked.
Here's my code setting up a constructor:
class EngStnadardMOList
{
public int ID { get; set; }
public string Model { get; set; }
public EngStnadardMOList(int i, string m)
{
ID = i;
Model = m;
}
}
Then I populate on load:
public FrmEngStandardMO()
{
InitializeComponent();
fillList();
}
Here's the fillList() method:
private void fillList()
{
List<EngStnadardMOList> _list = new List<EngStnadardMOList>() {
new EngStnadardMOList(1,"PE1008B1R405-A-VA"),
new EngStnadardMOList(2,"PE1008B1R405-A-VB"),
new EngStnadardMOList(3,"PE103D1R420-A-VB"),
new EngStnadardMOList(4,"PE103D1R420-A-VC"),
new EngStnadardMOList(5,"PE105F1R420-A-VB"),
new EngStnadardMOList(6,"PE105F1R420-A-VC"),
new EngStnadardMOList(7,"PM108F2R420-A-VC"),
new EngStnadardMOList(8,"PM112F3R4100-A-VC"),
new EngStnadardMOList(9,"PM212F6R4200-A-VC")
};
cmbStdModel.DisplayMember = "Model";
cmbStdModel.ValueMember = "ID";
cmbStdModel.DataSource = _list;
}
This does populate the combobox when you run the form, however, using the code below, I always get a null value when trying to get the selected item.
private void btnGenerateFiles_Click(object sender, EventArgs e)
{
if (cmbStdModel.SelectedValue != null)
{
MessageBox.Show(cmbStdModel.Text);
}
else {
MessageBox.Show("Null Model.");
}
}
}
What am I doing wrong?
Thanks in advance!
I've figured it out! I don't know why it works, but I found if I call the fillList(); method at the beginning of btnGenerateFiles_Click block, then the code doesn't return null.
I'm not sure why that is the case, so if anyone can clarify for me, I would appreciate it.
I'm trying to get a new form (FormAlbum) to open when I click buttonOpenAlbum and have an item selected in the AlbumListBox.
If I just have this in buttonOpenAlbum_Click:
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
The new from opens without error. However, as soon as I mention "AlbumListBox.SelectedItem" (as in the code belowin Form FormMain), I get a "StackOverflowException was unhandled" at:
public ListBox AlbumListBox
{
get
{ // <-This bracket here is where the error highlights
I don't understand why I'm getting this error, only that it must have something to do with AlbumListBox. What am I doing wrong? Any help is appreciated, thank you.
Form FormMain:
public FormMain()
{
InitializeComponent();
}
private void buttonAddAlbum_Click(object sender, EventArgs e)
{
FormAlbumAC addAlbumForm = new FormAlbumAC(this);
addAlbumForm.ShowDialog();
}
private void buttonOpenAlbum_Click(object sender, EventArgs e)
{
if (AlbumListBox.SelectedItem != null)
{
MessageBox.Show(AlbumListBox.SelectedItem.ToString());
FormAlbum MusicForm = new FormAlbum(this);
MusicForm.ShowDialog();
}
else
{
MessageBox.Show("You need to select an album from the list to open.");
}
}
public static class PublicVars
{
public static List<Album> AlbumList { get; set; }
static PublicVars()
{
AlbumList = new List<Album>(MAX_ALBUMS);
}
}
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
Look at your property implementation:
public ListBox AlbumListBox
{
get
{
return AlbumListBox;
}
}
It's just calling itself, recursively. It may be easier to see that if we convert it to a method:
public ListBox GetAlbumListBox()
{
return GetAlbumListBox();
}
That's why you've got an overflow. It's not clear what you meant it to do... where did you expect the value to come from? You probably need a variable to back the property. What did you expect to set the value returned?
I'd also strongly discourage the design of the PublicVars class. Aside from the naming, you're basically using global variables - not a good idea. Work out which classes need access to the data, and how to get that data to them appropriately.
Long story short, I needed a set of objects with dictionary-like functionality that can be serialized in order to save user data. The original dictionary was a Dictionary class that held an array of Item objects and the amounts of each object 'held' by the user. After finding some recommendations on the internet I tried implmenting my own dictionary-like class from KeyedCollection, but can't seem to add objects to it. Am I adding the objects wrong or is something wrong with my collection?
The 'SerialDictionary' class:
public class SerialDictionary : KeyedCollection<Item, int>
{
protected override int GetKeyForItem(Item target)
{
return target.Key;
}
}
public class Item
{
private int index;
private string attribute;
public Item(int i, string a)
{
index = i;
attribute = a;
}
public int Key
{
get { return index; }
set { index = value; }
}
public string Attribute
{
get { return attribute; }
set { attribute = value; }
}
}
The Main form (that is trying to add the object)
public partial class Form1 : Form
{
SerialDictionary ItemList;
Item orb;
public Form1()
{
InitializeComponent();
ItemList = new SerialDictionary();
orb = new Item(0001, "It wants your lunch!");
orb.Key = 001;
}
private void button1_Click(object sender, EventArgs e)
{
ItemList.Add(orb);
}
}
The error I am receiving when trying to add an object:
The best overloaded method match for 'System.Collections.ObjectModel.Collection.Add(int)' has some invalid arguments
If I throw an int in there it compiles, but I'm trying to get a collection of the Item objects in there...
You have it backwards, it should be:
public class SerialDictionary : KeyedCollection<int, Item>
The key type comes first in the signature, then the item type.
I'm attempting to allow my users to drag and drop certain rows of data from one custom list control to another, where the second list control is in another instance of the same application.
DoDragDrop(parameterTypedListView.SelectedObjects, DragDropEffects.Copy);
where parameterTypedListView.SelectedObjects is a generic IList where T is a custom class containing only valuetypes as fields/properties.
In the OnDragDrop event I try to extract this data but only get a System.__ComObject ... object which seems to inherit from System.MarshalByRefObject.
In short: How do I extract the data in an object oriented format I can actually use?
Edit: Setting my custom class as serializable has no discernible effect whatsoever. I can enumerate the __ComObject:
foreach (var dataObject in (IEnumerable) e.Data.GetData("System.Collections.ArrayList"))
{
// this actually enumerates the correct number of times, i.e. as many times as there are items in the list.
}
but every dataObject is, in itself, a System.__ComObject that I cannot cast to anything useful.
I was able to replicate your initial problem, but as soon as I added the [Serializable] attribute to the class in the array list, I was able to see the objects as their correct type.
Here is some example code, showing a small working example.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Form1_DragDrop);
this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Form1_DragEnter);
}
[Serializable]
class DragClass
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
}
private void label1_MouseDown(object sender, MouseEventArgs e)
{
System.Collections.ArrayList aDragClasses = new System.Collections.ArrayList();
aDragClasses.Add(new DragClass() { Prop1 = "Test1", Prop2 = 2 });
aDragClasses.Add(new DragClass() { Prop1 = "Test2", Prop2 = 3 });
aDragClasses.Add(new DragClass() { Prop1 = "Test3", Prop2 = 4 });
DoDragDrop(aDragClasses, DragDropEffects.Copy);
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
foreach (var aData in (System.Collections.IEnumerable)e.Data.GetData(typeof(System.Collections.ArrayList)))
{
System.Diagnostics.Debug.WriteLine(((DragClass)aData).Prop1);
}
}
}
I think that the problem is that you are using the list directly to pass the data. I tried it several different ways to get it to fail and figured out a few ways that it doesn't work.
If you don't have the [Serializable] attribute on your custom classes it will not work correctly because this is how the classes are marshaled between the processes. Also, if I use a List directly to pass the data I get a null reference exception.
If you use a simple transport class to pass the data (and all the types are serializable) then everything worked fine for me.
[Serializable]
class Test
{
public string Name { get; set; }
public string Description { get; set; }
}
[Serializable]
class Transport
{
public Transport()
{
this.Items = new List<Test>();
}
public IList<Test> Items { get; private set; }
}
Then I can do this no problem and it works across instances...
private void Form1_DragDrop(object sender, DragEventArgs e)
{
foreach (var item in ((Transport)e.Data.GetData(typeof(Transport))).Items)
{
System.Diagnostics.Debug.WriteLine(item.Name + " " + item.Description);
}
}
i created Web user control. it's property is included ListDictionary; But i can not generate
in web uset control. if i try wuc in asp.net page, it gives initialize error:
"Object reference not set to an instance of an object."
WEB USER CONTROL:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
FillList1();
}
}
public String OrderClause
{
get { return Session["selecteditem"].ToString(); }
set { Session["selecteditem"] = value; }
}
private ListDictionary items;
public ListDictionary Items
{
get { return items; }
set { items = value; }
}
void FillList1()
{
foreach (string ky in Items.Keys)
{
ListBox1.Items.Add(new ListItem(ky, Items[ky].ToString()));
}
}
BUT!!
if i add my web page this web user control. And write below codes:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
WebUserControl2_1.Items.Add("Yusuf", "1");
WebUserControl2_1.Items.Add("Gulden", "2");
}
}
protected void btn_test_Click(object sender, EventArgs e)
{
lbl_test.Text = WebUserControl2_1.OrderClause.Trim();
}
Object reference not set to an instance of an object.
Error point: WebUserControl2_1.Items.Add("Yusuf", "1");
i need listdictionary new method in web user control but how?
You are referencing the Items property without first instantiating the field. So asking for the item will cause the NullReferenceException. I like the lazy load approach, so when you need it, if it is null, then it will instantiate it.
Inside the getter you could simply put
if(items == null)items = new ListDictionary();
return items;
But then you need to think about state management. You have a few options including ViewState and also ControlState.
Andrew
You're never actually instantiating an instance of the ListDictionary in the items variable
In this bit of code:
private ListDictionary items;
public ListDictionary Items
{
get { return items; }
set { items = value; }
}
Change it to something like this:
private ListDictionary items;
public ListDictionary Items
{
get { if (items == null) {items = new ListDictionary}; return items; }
set { items = value; }
}
You are making a couple o mistakes. In first place, all your properties should have a way to store thier values between postback. an easy way to handle this is through viewstate.
Here is an article that gives more deatailed information about this topic
http://www.codeguru.com/csharp/.net/net_asp/controls/article.php/c11971/
In second place if it is a collection it is more likekely you will only need a getter since you will be manipullating collection instead of replacing the entire collectio.
so Your code should look like this:
public ListDictionary Items
{
get
{
object o = ViewState["Items"];
if (o == null)
{
ViewState["Items"] = new ListDictionary;
o = ViewState["Items"];
}
return (ListDictionary) o;
}
}
Hope this helps,