Update issue with data-bound Listbox and PropertyGrid - c#

This is part of my AOI class (nothing special about it):
class AOI : INotifyPropertyChanged
{
private Guid _id;
private string _name;
private string _comment;
public Guid Id
{
get { return _id; }
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public string Comment
{
get { return _comment; }
set
{
_comment = value;
OnPropertyChanged("Comment");
}
}
public override string ToString()
{
if (_name.Length > 0)
{
return _name;
}
else
{
return _id.ToString();
}
}
}
I keep them in a BindingList<AOI> which is bound to a ListBox. In the SelectedValueChanged event of the ListBox I assign the selected object to a PropertyGrid, so that the user can modify the AOI.
This works fine except for the Name field (which is displayed in the ListBox (see ToString() above)).
When I edit the name field using the PropertyGrid, the ListBox is updated correctly. But in the PropertyGrid, the Name field (just the value) is cleared as soon as I press enter. The correct (modified) value appears when I set the cursor to another field in the PropertyGrid.
What is the easiest workaround to handle this correctly?

This is a problem of the PropertyGrid.
This can also be reproduced with the designer within the Visual Studio. Simply select a control and change its minimum size to a larger value than the current one. If you take a look into the grid, the value in the size property won't be updated till you select it within the grid.
If some rows won't update correctly normally one of these two options will help:
Reattach the object to the PropertyGrid by calling propertyGrid.SelectedObject = myObject
Force the grid to redraw itself by calling propertyGrid.Invalidate()

Related

GroupBox Custom Control DataBinding

Good Day,
I am trying to make a custom control where a property can be bound to a DataSource. I have been searching all day and managed to figure out how to set the custom control DataSource to the desired DataSource on my form. I also saw how to set the values of a ComboBox. But I will be using a GroupBox.
For example, in a ComboBox control we have the properties - DataSource, DisplayMember and Value Member. And the value of the column can be found with "NormalComboBox.SelectedValue".
How will I be able to bind my custom "ValueMember" property to a column in my binding source in order to get the value of the column in the current row?
public class ValueGroupBox : GroupBox
{
private object _dataSource;
// ** This is where I am having issues - Need to return value
private string _valueMember;
private string _selectedValue;
public string ValueMember { get { return _valueMember; } set { _valueMember = value; }
public string SelectedValue { get { return _selectedValue; } set { _selectedValue = value; }
// ** End of having trouble
[Category("Custom Data")]
[Description("Indicates the source of data for the GroupBox control.")]
[TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
[DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public object DataSource
{
get
{
return this._dataSource;
}
set
{
if (this._dataSource != value)
{
this._dataSource = value;
}
}
}
}
Any help would be much appreciated.

Custom UserControl - Design-Time support for control repainting

I have created my custom UserControl with some custom properties for it. For example:
[Description("Example Description"),Category("CustomSettings"),DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get; set;
}
Everything works fine. I can change custom property in code and in design-time.
What I'm looking for (and cannot find anything) now is: How could I repaint (reacreate) my UserControl in design-time when my custom property change in design-time.
Let's say when DatabaseName will be changed to localhost UserControl will add and display some Label on my UserControl. It's important to work in Design-Time.
Nothing special there. You just have to set the text to Label inside the property setter. That should update the UI.
private string databaseAddress;
[Description("Example Description"), Category("CustomSettings"), DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get { return databaseAddress; }
set
{
databaseAddress = value;
yourLabel.Text = value;//Set value to Label or whatever
}
}
Try this
private string _databaseAddress = "localHost";
[Description("Example Description"), Category("CustomSettings"), DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get
{
return _databaseAddress;
}
set
{
if(!string.IsNullOrEmpty(value))
{
_databaseAddress = value;
lblAddress.Text = value;
lblAddress.Invalidate();
}
}
}
Both previous answers are correct.
I just want to add that the user control can even react on resizing during design time, using the Layout event.

How to make PropertyGrid Collection Items semi editable?

I have a class called BasePath, which contains Drive, Path and MetaName properties.
Furthermore I have created a BasePathCollection to show them in the PropertyGrid (expandable).
For adding a path you have to click the "three dots button" where you get another PropertyGrid where you see all items of the "BasePathCollection" on the left and the items properties on the right.
When BasePath properties are [ReadOnly(true)] I can edit the path by clicking to another "three dots button":
But now I need to make the the MetaName property editable and boom, suddenly I can't edit the path. The "three dots button" is no longer accessible:
What do I need to change for getting the "three dots button" back with the ability to still edit the MetaName property?
Edit:
Declaration of Properties:
How is your MetaName property declared? Show us some code please. – Simon Mourier 1 hour ago
private string m_metaName;
[Browsable(true)]
[ReadOnly(true)]
public string Drive { get { return m_drive; } set { m_drive = value; } }
[Browsable(true)]
[ReadOnly(true)]
public string Path { get { Clear(); return m_path; } set { m_path = value; Clear(); } }
[Browsable(true)]
[ReadOnly(true)]
public string MetaName { get { return m_metaName; } set { m_metaName = value; } }
Yes in a lack of knowledge I tried to set the following attributes for the BasePath-Class to make it editable.
[TypeConverter(typeof(BasePathConverter))]
[Editor(typeof(FolderNameEditorWithRootFolder), typeof(System.Drawing.Design.UITypeEditor))]
public class BasePath : IComparable<BasePath>{ ... }

Data Binding to an object in C#

Objective-c/cocoa offers a form of binding where a control's properties (ie text in a textbox) can be bound to the property of an object. I am trying to duplicate this functionality in C# w/ .Net 3.5.
I have created the following very simple class in the file MyClass.cs:
class MyClass
{
private string myName;
public string MyName
{
get
{
return myName;
}
set
{
myName = value;
}
}
public MyClass()
{
myName = "Allen";
}
}
I also created a simple form with 1 textbox and 1 button. I init'd one instance of Myclass inside the form code and built the project. Using the DataSource Wizard in Vs2008, I selected to create a data source based on object, and selected the MyClass assembly. This created a datasource entity. I changed the databinding of the textbox to this datasource; however, the expected result (that the textbox's contents would be "allen") was not achieved. Further, putting text into the textbox is not updating the name property of the object.
I know i'm missing something fundamental here. At some point i should have to tie my instance of the MyClass class that i initialized inside the form code to the textbox, but that hasn't occurred. Everything i've looked at online seems to gloss over using DataBinding with an object (or i'm missing the mark entirely), so any help is great appreciated.
Edit:
Utilizing what I learned by the answers, I looked at the code generated by Visual Studio, it had the following:
this.myClassBindingSource.DataSource = typeof(BindingTest.MyClass);
if I comment that out and substitute:
this.myClassBindingSource.DataSource = new MyClass();
I get the expected behavior. Why is the default code generated by VS like it is? Assuming this is more correct than the method that works, how should I modify my code to work within the bounds of what VS generated?
You must assign the textbox's data source to be your new datasource. But additionally, you must assign the datasource's datasource to be an instance of your class.
MyDataSource.DataSource = new MyClass();
TextBox1.DataSource = MyDataSource;
That should work for your first pass. As others have mentioned, you may need to implement additional interfaces on your class (INotifyPropertyChanged etc), if you are going to be modifying the class properties via any background processes.
If you are only updating the properties via the form, then you do not need this step.
You should implement the INotifyPropertyChanged interface to your MyClass type:
public class MyClass : INotifyPropertyChanged
{
private string _myName;
public string MyName
{
get { return _myName; }
set
{
if( _myName != value )
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if( PropertyChanged != null )
PropertyChanged( this , new PropertyChangedEventArgs(propertyName) );
}
}
This interface is required for the DataBinding infrastructure if you want to support simple databinding.
The INotifyPropertyChanged interface is used to notify a 'binding' that a property has changed, so the DataBinding infrastructure can act accordingly to it.
Then, you can databind the MyName property to the Text Property of the textbox.
I get an error message in the DataBinding.Add("TEXT", myObject, myObjectProperty) method
This is probably because you're missing the explicit {get;set;} on the property declaration!
BAD:
public string FirstName; //<-- you will not be able to bind to this property!
GOOD:
public string FirstName { get; set; }
Looks like you probably need a Bindable attribute on your MyName property (and follow Frederik's suggestion as well):
[System.ComponentModel.Bindable(true)]
public string MyName
{
get { return _myName; }
set
{
if( _myName != value )
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
Via: http://support.microsoft.com/kb/327413
I don't have any code in front of me, but I think the data source is kind of like a collection. You have to add an instance of MyClass to the data source, and that's what the form fields will bind to. There's also methods for navigating through the data source to multiple instances of MyClass, but it doesn't sound like you need that. Check the docs for DataSource.
I don't think you need to implement any fancy interfaces. I seem to remember there's a method on the data source that lets you refresh or rebind the current item after you change some values.
using System.Collections.Generic;
public class SiteDataItem
{
private string _text;
private string _url;
private int _id;
private int _parentId;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
}
}
public string Url
{
get
{
return _url;
}
set
{
_url = value;
}
}
public int ID
{
get
{
return _id;
}
set
{
_id = value;
}
}
public int ParentID
{
get
{
return _parentId;
}
set
{
_parentId = value;
}
}
public SiteDataItem(int id, int parentId, string text, string url)
{
_id = id;
_parentId = parentId;
_text = text;
_url = url;
}
public static List<SiteDataItem> GetSiteData()
{
List<SiteDataItem> siteData = new List<SiteDataItem>();
siteData.Add(new SiteDataItem(1, 0, "All Sites", ""));
siteData.Add(new SiteDataItem(2, 1, "Search Engines", ""));
siteData.Add(new SiteDataItem(3, 1, "News Sites", ""));
siteData.Add(new SiteDataItem(4, 2, "Yahoo", "http://www.yahoo.com"));
siteData.Add(new SiteDataItem(5, 2, "MSN", "http://www.msn.com"));
siteData.Add(new SiteDataItem(6, 2, "Google", "http://www.google.com"));
siteData.Add(new SiteDataItem(7, 3, "CNN", "http://www.cnn.com"));
siteData.Add(new SiteDataItem(8, 3, "BBC", "http://www.bbc.co.uk"));
siteData.Add(new SiteDataItem(9, 3, "FOX", "http://www.foxnews.com"));
return siteData;
}
}
More detail you can read tutorial dapfor. com

Howto determine order of displayed columns in a datagridview binded to a datasource

Is there a way to determine the order of the columns displayed in
a datagridview when binding it to a datasource whitch contains an
underlying IList ?
I thought there was a specific property attribute for this purpose
but can't recall what it actually was.
eg:
public void BindToGrid(IList<CustomClass> list)
{
_bindingSource.DataSource = list;
dataGridView1.DataSource = _bindingSource.DataSource;
}
Type binded should be something like this
class CustomClass
{
bool _selected = false;
//[DisplayOrder(0)]
public bool Selected
{
get { return _selected; }
set { _selected = value; }
}
string _name;
//[DisplayOrder(2)]
public string Name
{
get { return _name; }
set { _name = value; }
}
string _value;
//[DisplayOrder(1)]
public string Value
{
get { return _value; }
set { _value = value; }
}
}
Edit:
I would like to add that I rather not want to add the columns manually to columns list in the designer. I'd like to keep this as dynamic as possible.
In the DataGridView specify an actual list of columns instead of allowing it to auto-databind. You can do this in Design View in Visual Studio by selecting the control and adding the columns. Make sure you specify in each column which property it should bind to. Then you can rearrange the columns any way you want as well as do other customizations.
I think that the DisplayOrder attribute is relatively new and probably not supported in the DataGridView control.
The display order of the columns in the DataGridView is determined by the DisplayIndex properties of the DataGridViewColumn-s. You would have to set these properties on the columns of the grid, in order to change their order.
I also agree with Eilon's answer: you can create the list of the columns yourself, instead of auto-databinding, and that way you can determine the order in which they will be displayed.
The column ordering does not always work. You'll need to turn off AutoColumnCreate to fix inconsistencies:
http://www.internetworkconsulting.net/content/datadridview-displayorder-not-working
I am not sure whether this is a functionality that .Net Offers, but if you just change the order of your properties in the class, the grid renders the columns in the same order.
The below two classes will render in the order they are typed in the class. Strange!!
class CustomClass
{
public bool Selected {get;set;}
public string Name{get;set;}
}
class CustomClass
{
public string Name{get;set;}
public bool Selected {get;set;}
}

Categories