inherited ListViewItem in WinForms, C#, .Net2.0 - c#

I got a problem with ListView in System.Windows.Forms, that i can't handle it on myself, begging for help or a hint where do I do wrong?
Description:
- I have a class - name it cListViewItem ('c' from custom), that inherits from standard ListViewItem, but stores my own data handling class. Now, after adding cListViewItem to a ListView class using ListView.items.Add( ) i don't seem to have any control over the item's name.
- fragments of my source (lil changed for the purpose of this post)
using System.Windows.Forms;
cListViewItem:ListViewItem
{
// gives a basic idea
public cListViewItem( myclass DataStorage )
{
// constructor only stores DataStorage to a local variable for further use;
this._storage = DataStorage;
}
protected myclass _storage;
// and there goes the fun:
// I thought that ListView class uses .Text property when drawing items, but that is not truth
// my idea was to 'cheat' a little with:
new public String Text
{
get
{
// overriding a getter should be enough i thought, but i was wrong
return( some string value from DataStorage class passed via constructor );
// setter is not rly needed here, because data comes from this._storage class;
// in later stages i've even added this line, to be informed whenever it's called ofc before return( ); otherwise VisualStudio would not compile
MessageBox.Show( "Calling item's .Text property" );
// guess what? this message box NEVER shows up;
}
}
}
I see that's important to use .Text setter, but constructor is the last moment i can do it, right after creation cListViewItem is being added to ListView Items property and displayed, so there's no place to call .Text = "" again.
My piece of code only works when I set all things in cListViewItem 's constructor like:
public cListViewItem( myclass DataStorage )
{
this._storage = DataStorage;
this.Text = DataStorage.String1;
// and if I add subitems here, I will see em when the ListView.View property be changed to View.Details
}
So am I blind or what? when I use cListViewItem.Text = "string" I will see 'string'
in the ListView but when I just override .Text getter i can't see the items :(
ListView class gives the flexibility of showing items the way I need. I wanted to create a class that will bind my custom data storage class with a ListView class. In the next stage of my application I want to bind a form for selected item in a ListView, that will allow me change item's (my custom class) values. That's why i wanted to make each ListViewItems item remembering corresponding custom data storage class.
Names shown in ListView will never be uniqe, so multiple same names all allowed, but items will differ by a id value (database-wise);
I just can't figure out why using ListViewItem.Text setter does the job, altho ListView class does not use ListViewItem.Text getter for displaying items (my MessageBox never pops up)??
Pls help.

The main problem here is that you are hiding the property with the new keyword. The original property is not virtual ("overwritable") so it is NOT overwritten but shadowed.
Read here for more information.

If I understand correctly, then the following points might be helpful.
For storing custom data you don't actually need to derive from the ListViewItem class, instead you can use an instance of a ListViewItem and set the Tag property to any object, this can be your DataStorage class. If you do this, then after you've constructed the ListViewItem set the Text of it
DataStorage storage = GetDataStorage();
ListViewItem item = new ListViewItem(storage.Name);
item.Tag = storage;
If you are going to inherit the ListViewItem then set the value in the constructor
public cListViewItem( myclass DataStorage )
{
// constructor only stores DataStorage to a local variable for further use;
this._storage = DataStorage;
this.Text = this._storage.Name;
}
Property and Method hiding gets a little confusing for myself at least, I can't quite remember the rules, but ultimately the call to Text that is done automatically is not going to be calling your version...

Related

Setting the properties of form values from another class

I have been working on an assignment with forms for a while now and everything works, however my professor want us to devide everything in seperate classes.
So what I have now is:
MainForm.cs
MainForm.Designer.cs
MainForm.resx
program.cs
In the MainForm.cs is where i have all my code and where i call the buttons, labels, textboxes etc from.
What I want to do, is to have a strucutre with other classes such as
MainForm.cs
MainForm.Designer.cs
MainForm.resx
program.cs
class1.cs
class2.cs
I tried doing this, but from my class1 i couldn't call the Design(name) of the Form since it dont exist in the context. I have been searching a lot but haven't found anything that matched my problem or how to solve it.
How can I solve this problem?
For any property to be accessible from another class (another form for instance as Form itself is a class), the property must be public, so for example lets say you have a textbox named txtSomething and you need to access its text, you may create a public property that lets you get and set its Text property:
public string SomeProperty { get { return txtSomething.Text;} set {txtSomething.Text = value;}}
You can of course change MainForm.Designer.cs and make all controls (for example textboxes) public where they are defined, but it is not a good choice at all. because you should always give public access only where it is needed. for example if you need a controls text, let just its text property be accessible (the code above).
Even, if the second form has just to get the textbox value and does need to set it, you may give a readonly access. so the code above would be:
public string SomeProperty { get { return txtSomething.Text;} }
Then assuming that the instance of FrmMain is frmMain you can access the Text property of that textbox like:
string propertyValue = frmMain.SomeProperty;

WinForms DataGridView and PropertyGrid behavior [duplicate]

Let's say I have a property which I want shown in a DataGridView, but not when the same object is shown in a PropertyGrid. I know I can use [Browsable(false)], but that hides it in both views. I can also do a gridView.Columns["blah"].Visible = false;, but this is the opposite of what I want, as it hides in the DataGridView but not in PropertyGrid. Is there some way to do the reverse? (Short of creating a whole new DataTable just to hold the same data minus one field, and rebinding everything to that instead - that's really a kludge way to do things.) Alternatively, I could live with a solution which adds a column to the DataGridView that is not present on the actual class.
it is possible to solve this issue by using the BrowsableAttributes property of a PropertyGrid.
First, create a new attribute like this:
public class PropertyGridBrowsableAttribute : Attribute
{
private bool browsable;
public PropertyGridBrowsableAttribute(bool browsable){
this.browsable = browsable;
}
}
Then add this attribute to all those properties which you want to be shown in your PropertyGrid:
[DisplayName("First Name"), Category("Names"), PropertyGridBrowsable(true)]
public string FirstName {
get { return ... }
set { ... }
}
Then set the BrowsableAttributes property like this:
myPropertyGrid.BrowsableAttributes = new AttributeCollection(
new Attribute[] { new PropertyGridBrowsableAttribute(true) });
This will only show the attributed properties in your property grid and the DataGridView can still access all properties with only a little bit more coding effort.
I would still go with Tergiver and call this behaviour a bug, since the documentation of the Browsable attribute clearly states its use for property windows only.
(Credit goes to user "maro" at http://www.mycsharp.de/wbb2/thread.php?postid=234565)

Understanding how a property of type List<> works

I got to think I don't understand how it works.
My specific question is: Why am I allowed to set the value of a list property element when I have no setter and no backing list variable?
Let me explain. Let's say I have a CustomerTable class with:
public List<string> Name
{
get
{
var names = new List<string>();
foreach (CustomerRow row in Rows)
{
name.Add(row.Name);
}
return names;
}
}
The idea is to have a read-only property show the contents of a column without duplicating data in my class, since I already have a list of rows.
Anyway, my surprise comes when pieces of code like the following one are accepted by Visual Studio without claiming any kind of error (and it even allows me to compile without errors):
Name[0] = "John";
I can't understand why this is legal. My property has no set { }, and it doesn't even have a backing list to modify. What is this piece of code supposed to do?
Shouldn't it work like a method? Is there really a stored list other than the one I generate each time someone "gets" it?
(I can give more details on demand and will also be grateful for any other remarks)
You are not setting the property, rather you are getting the property (which is a list) and then operating on it (in your example, changing its first member). If you were to try:
Name = new List<string>();
You would get the compilation error you were expecting to get. Note that since you are creating a new list every time, your Rows property remains read-only (assuming it is not exposed somewhere else). If you want to make it clear that changes to your returned collection are meaningless, you can change the type of the Name property to IEnumerable<string>:
public IEnumerable<string> Name
{
get
{
return Rows.Select(row => row.name); //LINQ is more elegant here
}
}
In your example you don't set Name (which is read-only, indeed), but you set the first list element contained in Name, which is Name[0], and there's no reason why you could not do that since List<string> is an object type which allows to set elements.

UserControl cannot be displayed in Designer - null object reference

I have a created a UserControl with a combobox in it. This combobox is populated from a xml, when this is not present, it is loaded from resource file.
It works fine in the program, but it can't be displayed in designer - it says: "Object reference not set to an instance of an object."
In the class responsible for loading the list from xml the null reference check is skipped for reasons beyond my understanding...
public SortedDictionary<string, string> Countries
{
get
{
if (object.ReferenceEquals(countries, null))
{
GetCountryList();
}
return countries;
}
}
Populating of the comboBox goes like this:
comboBoxCountry.DataSource = new BindingSource(Program.language.Countries, null);
Program.language is initialized in Program, but it does not help for the Designer.
The question is, how (when, at what event) should I populate the ComboBox (=load list from xml) to be able to display my control in designer.
If possible, you want to check for this.DesignMode and then simply not load the ComboBox at design-time.
Does GetCountryList() set a member variable? If so, move that call to a method. Property get accessors and the ToString() method are assumed pure: the program state before and after must be identical. Violating this assumption can cause all sorts of problems, especially designer/debugger/runtime inconsistency. Various rants have taken place, but the best thing to do is understand the assumption, follow it, and let it work to your advantage as you debug.

DataGridView - setting object's displayed value in a column

I bind some collection to a DataGridView. Collection contains KeyValuePair objects where key is a string and value is an object of my class Field. DataGridView displays two columns - one containing the key and the other one containing the value. The value (Field object) is displayed with its ToString() method. But I would like it to be displayed using its Name property. The problem is the column contains no DisplayMember property.
How can i do it?
Edit: I know I could override ToString() to return the name of the object but I don't want to do that.
DataGridView (in common with most direct list-based bindings) can only bind to immediate properties of the row item. You could perhaps create a facade object for this? i.e. a class that accepts the instance and returns the name as a direct property:
public string Name {
get {return innerObject.Name;}
set {innerObject.Name = value;}
}
// snipped: other properties - Key etc
Alternatively, you could project into a new object? For example, data-bindings work (read-only, at least) with anonymous types pretty well:
grid.DataSource = originalData.Select(x=>
new {x.Key, Name = x.Field.Name}).ToList();
Finally, you can hack around in ComponentModel to flatten the model at runtime, but it really isn't worth it just for this.
You could put the DataGridView into virtual mode (view.VirtualMode = true), and handle the CellValueNeeded (and possibly the CellValuePushed) events to access the "Name" property. This would avoid creating lots of wrapper objects, but does make the code somewhat less elegant.

Categories

Resources