I'm creating a properties grid using C#.NET for my AutoCAD plugin. This plugin checks each entity in an AutoCAD drawing for XRecord data and if it finds any, attempts to deserialize it into a class. All of that is working so now I want to show a stored object's properties in my form's properties grid. Here is the class who's information I want to show
public class SerializeTest
{
#region class variables
public int X { get; set; }
public int Y { get; set; }
#endregion
#region Constructors
public SerializeTest(int passedX, int passedY)
{
this.X = passedX;
this.Y = passedY;
}
private SerializeTest()
{
}
#endregion
}
And here's what it looks like in my form when I click the AutoCAD entity that is storing it as an XRecord
I want to add the object's class type (in this case SerializeTest) before the other properties. How would I customize the properties grid like that?
Specifically, I want it to look like this except with the class field at the top not the bottom
Related
I recently created a winforms application that followed no specific design pattern. This application has 4 different "views", each implemented using a TableLayoutPanel. One view is a "main" view that allows the user to select input files and the other 3 views contain DataGridViews that allow the user to work with the data loaded from the input file.
The problem lies in the fact that I have a single form with 4 different panels within it that are hidden and made visible when needed. But this has caused my form class to become much larger than I would like considering I have different events and methods that operate on the data for each panel all within the same class. So I did some research and came across Model-View-Presenter, but I've only came across examples that show applications with single-views.
My question is, if I use MVP and each view has its own interface and presenter, and the concrete implementation of the view is done using a Form, what is the best way to switch between views (for example, when clicking "next").
Should the concrete implementation of my view even be a Form? Am I missing something here? I'd like to follow the MVP pattern but I am open to suggestions if there is a better alternative.
First of all, you want to create a UserControl for each of the three DataGridView forms. As you are using MVP, each one should have an interface that the control inherits. For example:
public interface IDataGridView1
{
// Properties, Methods, etc...
}
public interface IDataGridView2
{
// Properties, Methods, etc...
}
public interface IDataGridView3
{
// Properties, Methods, etc...
}
Here is an example of the DataGridView1 UserControl, which inherits from its interface, and also from Control:
public class DataGridView1 : Control, IDataGridView1
{
TableLayoutPanel layoutPanel;
public DataGridView1()
{
layoutPanel = new TableLayoutPanel();
// Set up the layoutPanel.
// Rest of constructor, define your controls.
// Add your controls to layoutPanel.
// Add layoutPanel to this control.
Controls.Add(layoutPanel);
}
// Methods etc...
}
The other two DataGridViews will be similar but with their own functionality.
You could then create an interface for the MainView, which includes properties for the three DataGridViews it should contain, and methods to show one DataGridView whilst hiding the rest:
public interface IMainView
{
IDataGridView1 DataView1 { get; set; }
IDataGridView2 DataView2 { get; set; }
IDataGridView3 DataView3 { get; set; }
void ShowOnlyDataView1();
void ShowOnlyDataView2();
void ShowOnlyDataView3();
// Other methods, properties, etc...
}
The MainView class would inherit from Form and its own interface. Here I have shown the instantiated DataGridViews being passed in via the form's constructor:
public class MainView : Form, IMainView
{
public IDataGridView1 DataView1 { get; set; }
public IDataGridView2 DataView2 { get; set; }
public IDataGridView3 DataView3 { get; set; }
TableLayoutPanel layoutPanel;
public MainView(IDataGridView1 dataView1, IDataGridView2 dataView2,
IDataGridView3 dataView3)
{
this.DataView1 = dataView1;
this.DataView2 = dataView2;
this.DataView3 = dataView3;
layoutPanel = new TableLayoutPanel();
// Define your layout panel here.
// Add your controls to layoutPanel.
// Add layoutPanel to the MainView.
Controls.Add(layoutPanel);
// Rest of constructor...
}
// Hides other views and show DataView1.
public void ShowOnlyDataView1()
{
DataView2.Hide();
DataView3.Hide();
DataView1.Show();
}
// Hides other views and show DataView2.
public void ShowOnlyDataView2()
{
// Etc...
}
// Hides other views and show DataView3.
public void ShowOnlyDataView3()
{
// Etc...
}
// Other Methods etc...
}
Here is an example of the your Main method. You will want to instantiate each DataGridView and pass these into your MainView:
public static void Main(string[] args)
{
IDataModel dataModel = new DataModel();
IDataGridView1 dataView1 = new DataGridView1();
IDataGridView2 dataView2 = new DataGridView2();
IDataGridView3 dataView3 = new DataGridView3();
IMainView mainView = new MainView(dataView1, dataView2, dataView3);
DataGridPresenter1 dataPresenter1 = new DataGridPresenter1(dataView1, dataModel);
DataGridPresenter2 dataPresenter2 = new DataGridPresenter2(dataView2, dataModel);
DataGridPresenter3 dataPresenter3 = new DataGridPresenter3(dataView3, dataModel);
MainPresenter mainPresenter = new MainPresenter(mainView, dataModel);
}
Something to that effect.
So your three DataGridViews are displayed within your MainView, and all four views are accessed by their own Presenters.
I am trying to implement a Custom Composite WebControl with "nested" properties, i.e., encapsulate a group of properties into a class.
For example, in this composite control, I have placed a button. I would like to be able to encapsulate relevant properties for the button into a class (e.g., buttonText, buttonStyle, etc.). This would make defining properties in multi-button/controls composite-control easier and consistent and intuitive.
Note: I would like for the encapsulated properties to appear grouped in the Properties dialog in VisualStudio, in a manner very similar to Style/Font.
Sample:
public class fooButtonProperties
{
[Category("Appearance"), Description("URL for the Profile page")]
public string URL { get; set; }
[Category("Appearance"), Description("Text to display"), DefaultValue("Profile")]
public string ButtonText { get; set; }
/// <summary>
/// Position of the control on the page, default is Right-Aligned
/// </summary>
[Category("Appearance"), Description("Position in the Header"), DefaultValue(PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions.Right)]
///Here is the composite control
public PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions PositionInHeader { get; set; }
}
public class myCustomClass: System.Web.UI.WebControls.CompositeControl
{
protected System.Web.UI.HtmlControls.HtmlLink myButton;
[Category("Appearance")]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
private fooButtonProperties _myeButtonProp;
#region Constructor
public myCustomClass()
{
this._myeButtonProp = new fooButtonProperties ();
}
#endregion
}
Unfortunately, this approach dos not work. The new property myButtonProperties does not appear at all in the "Properies" dialog.
To create a nested property use the System.ComponentModel.DesignerSerializationVisibility attribute in your control like this:
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
The final property name will be "myButtonProperties-URL" (with a hyphen). You can also add this attribute to properties in your fooButtonProperties class for even more nesting.
Please note that you may have to close the aspx file and rebuild the solution to refresh the Properties window.
The Category attribute works in your control and in your nested class.
The Description attribute for the descriptions seems correct BUT it does not work which could be a bug in Visual Studio. I found this link:
https://www.beta.microsoft.com/VisualStudio/feedback/details/653335/webcontrol-property-descriptions-do-not-appear-in-property-window
Also I observed that no properties show descriptions.
Regards
Oli
I have a DB and I created classes to access/manage it using SQLMetal. Now, I use LINQ to SQL, and I want to display the result of queries in a data grid view. when I do that, the columns are named after my columns in the DB table, and all the properties are displayed. I know I could change this by using DisplayName and Browseable attributes, but because the classes are auto-generated I can't just add this attributes where there are needed. I came up with three workarounds:
creating an Adopter to adopt my classes. I'm still not sure how exactly you make an adopter for this case.
creating another program that will run after the generating of the code that will add the attributes. this seems like an hack, and I prefer to separate between the functionality and the GUI, so this method is on hold.
using the MetaDataType attribute. I couldn't get this to work, and it's requires, as far as I know, that the classes and the metadata class will be in the same DLL.
How do I do the customization? is there another way? what way should I take and how?
EDIT: forgotten to mention: I'm using winforms, but if it will simplify things, I will move to WPF.
You can set a Types Metadata Type at run time by registering it with the TypeDescriptor manually.
That goes something like this.
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
To show it all in context this will show a single column in the data grid with the header "Custom Bar".
public class Foo
{
public string Bar { get; set; }
public string DontShowMe { get; set; }
}
public class FooMetadata
{
[DisplayName("Custom Bar")]
public string Bar { get; set; }
[Browsable(false)]
public string DontShowMe { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var type = typeof(Foo);
var metadataType = typeof(FooMetadata);
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType), type);
this.dataGridView1.DataSource = new List<Foo> { new Foo { Bar = "Foobar" } };
}
}
The is also a TypeDescriptor.RemoveProviderTransparent if you want to switch the Metadata Type on the go but keep in mind that setting/unsetting it applies to the whole application domain and so threading needs to be taken into account.
By using the WPF DataGrid you can easily customize the auto generated columns by using the AutoGeneratingColumn event.
Why can't you use the data grid view's Columns collection to change DisplayNames and Visibles at runtime?
I am creating a graphing interface and would like to give the user the option to edit the graphs look, i.e. Series color, BackColor, Data point size, etc... The chart is being created using
System.Windows.Forms.DataVisualization.Charting
To allow the user to edit those options I've placed a PropertyGrid in the form. However there are some properties I do not want the user to have access to. I would like to be able to set up a chart in my form then create a propertygrid that is connected to that chart but have certain properties removed from the grid.
What I have so far tried is ...
public partial class Form1: Form
{
PropertyGrid propG1 = new PropertyGrid();
this.Controls.Add(propG1);
//... There is code here where my chart(chart1) is being populated with data
private void toolStripButton1_Click(object sender, EventArgs e)// The button is just to test
MyChart myC = new MyChart();
propG1.SelectedObject = myC;
}
So based on the recomendations I've received so far I've created a class called MyChart which contains the properties that I don't want to be displayed on my chart.
using System.ComponentModel
//...
public class MyChart : Chart
{
[Browsable(false)]
public new System.Drawing.Color Property
{
get{return BackColor;} // BackColor is just an example not necessarily a property I'd like to remove
set{base.BackColor = value;}
}
I haven't been able to remove the properties from my grid nor have I been able to connect myC with my chart1 so when a property is changed in the grid chart1 is affected. Thanks for the continuing help.
Instead of modifying the PropertyGrid component and its behavior you can modify the objects that you display with attributes. Something like this:
[Browsable(false)]
public object SomeProperty
{
}
Don't forget to add:
using System.ComponentModel;
And to override inherited properties and hide them from the propertyGrid you could do something like:
public class Chart : BaseChart
{
[Browsable(false)]
public new string BaseString // notice the new keyword!
{
get { return base.BaseString; } // notice the base keyword!
set { base.BaseString = value; }
}
// etc.
}
public class BaseChart
{
public string BaseString { get; set; }
}
Setting the Browsable attribute to false will keep SomeProperty from showing up in the PropertyGrid.
So, in a hypothetical chart class like the one below, you will see the chart instance, the SomeProperty1 property but not SomeProperty2.
public class Chart
{
public object Property1 { get; set; }
[Browsable(false)]
public object Property2 { get; set; }
// etc.
}
See Getting the most out of your property grid for more information. And here is a very, very good deep-dive into customizing the PropertyGrid control that will blow your mind. ;-)
And, even more fun with attributes and the PropertyGrid:
[DefaultPropertyAttribute("Property1")]
public class Chart
{
[CategoryAttribute("My Properties"),
DescriptionAttribute("My demo property int"),
DefaultValueAttribute(10)]
public int Property1 { get; set; }
[Browsable(false)]
public object Property2 { get; set; }
[CategoryAttribute("My Properties"),
DescriptionAttribute("My demo property string"),
DefaultValueAttribute("Hello World!")]
public string Property3 { get; set; }
// etc.
}
I have a master-detail relationship in some custom entities. Say I have the following structure:
class Master : INotifyPropertyChanged
{
public int Id { get; set; } // + property changed implementation
public string Name { get; set; } // + property changed implementation
public ObservableCollection<Detail> Details { get; }
}
class Detail : INotifyPropertyChanged
{
public int Id { get; private set; }
public string Description { get; set; }
public double Value { get; set; } // + property changed implementation
}
My goal is to have a ListView, using a GridView, showing a list of Master objects. When I select a specific Master, I'll have a separate ListView for the details, allowing editing. Basically, a fairly standard Master-Detail view.
However, I also want the GridView for the Master to show the Sum of all of that master's Detail elements, ie: Details.Select(d => d.Value).Sum();
This is fairly easy to display using a custom IValueConverter. I can convert from the details collection directly to a double displaying sum, and bind a TextBlock's Text to the Details OneWay, via the IValueConverter. This will work, and show the correct values when I open the window.
However, if I change one of the detail members, this will not update (even though detail implements INotifyPropertyChanged), since the collection itself is still the same (the ObservableCollection reference hasn't changed).
I want to have an aggregated value in a master list, showing the sum (or average/count/etc) within the detail list, and have this stay up to date when the user changes properties in details. How can I go about implementing this?
Edit:
Ideally, I would prefer if there is a means of accomplishing this that doesn't involve changing the Master class directly. The application in question is using the MVVM pattern, and I'd really prefer to not change my Model classes in order to implement a specific View. Is there a way to do this without introducing custom logic into the model?
I was considering possibilities with the UI where you'd make the binding explicit and perform binding/updates from a command... but it seems that the easiest way to do it would be to extend the ObservableCollection to add/remove listeners to each Detail instance as its added/removed, then just fire CollectionChanged when any of them change. Call it DeeplyObservableCollection<T>.
class Master : INotifyPropertyChanged
{
public int Id { get; set; } // + property changed implementation
public string Name { get; set; } // + property changed implementation
public double Sum {get {return Details.Sum(x=>x.Value);}}
public DeeplyObservableCollection<Detail> Details { get; }
// hooked up in the constructor
void OnDOCChanged(object sender, CollectionChangedEventArgs e)
{ OnPropertyChanged("Sum"); }
}
Worst case you'd have to wrap an ObservableCollection in another type if you can't properly override all the methods you need...