I have a windows form that I am making in C# using Visual Studio 2013. The form contains a ListView that has 2 columns. The ListView itself is created inside of the InitializeComponent() which is auto-generated by VS. I actually add the column headers outside of InitializeComponent(). The problem is that I want the [Design] view in VS to show the column headers that I declare but I don't want to clutter up the InitializeComponent() method in addition to it also saying that I should not modify the contents of the code within the method.
Basically what I did was create a method that adds the two column headers to the ListView and formats them:
private void initRecipListView()
{
this.recipList.Columns.Add("Recipient", -2, System.Windows.Forms.HorizontalAlignment.Left);
this.recipList.Columns.Add("Number of Reports", -2, System.Windows.Forms.HorizontalAlignment.Left);
}
I want to call this method so that those two columns are added and visible during runtime AS WELL AS when I view the form inside of the VS designer window. I have tried putting that method inside of the constructor for the form itself which works during runtime but doesn't in the designer window.
Any idea where I need to put this method for it to be called and used when I am viewing the form in the designer window?
You can derive your own class from ListView and implement a scheme like this. Basic ingredients you need is presetting the number of columns in the constructor and exposing properties to allow you to set the column header text. Override OnClientSizeChanged() to keep the columns centered and deal with the vertical scroll bar appearing.
Add a new class and paste the code shown below. Compile. Drop the new control from the top of the toolbar. You can set the Column1Name and Column2Name properties in the designer or in your code. You'll get the WYSIWYG view in the designer.
using System;
using System.ComponentModel;
using System.Windows.Forms;
class MyListView : ListView {
public MyListView() {
this.Columns.Add("Unnamed1");
this.Columns.Add("Unnamed2");
this.View = View.Details;
}
public string Column1Name {
get { return this.Columns[0].Text; }
set { this.Columns[0].Text = value; }
}
public string Column2Name {
get { return this.Columns[1].Text; }
set { this.Columns[1].Text = value; }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public new ListView.ColumnHeaderCollection Columns {
get { return base.Columns; }
}
protected override void OnClientSizeChanged(EventArgs e) {
base.Columns[0].Width = this.ClientSize.Width / 2;
base.Columns[1].Width = this.ClientSize.Width - base.Columns[0].Width;
base.OnClientSizeChanged(e);
}
}
Related
I'm trying to create a custom BindingNavigator control in with some extra ToolStripButton (Edit and ExportToExcel).
The ToolStripButton is added to the BindingNavigator, but I cannot select this new ToolStripButton, e.g., to add code in its Click event handler. Actually, the ToolStripButtons appear locked.
Here is my code and an image that should describe the problem:
using System.Windows.Forms;
public class BindingNavigator : System.Windows.Forms.BindingNavigator
{
public ToolStripButton btnEdit;
public ToolStripButton btnExcelExport;
public BindingNavigator()
{
this.LayoutCompleted += BindingNavigator_LayoutCompleted;
}
public void BindingNavigator_LayoutCompleted(object sender, System.EventArgs e)
{
if (this.Items.Contains(btnEdit))
return;
if (this.Items.Count >= 11)
{
btnEdit = new ToolStripButton();
btnEdit.Image = global::BaseControls.Properties.Resources.Edit___16x16;
btnEdit.Name = "btnEdit";
this.Items.Insert(10, btnEdit);
this.Items.Add(new ToolStripSeparator());
btnExcelExport = new ToolStripButton();
btnExcelExport.Image = global::BaseControls.Properties.Resources.Excel___16x16;
btnExcelExport.Name = "btnExcelExport";
this.Items.Insert(13, btnExcelExport);
}
}
}
The BindingNavigator class has a dedicated Designer, BindingNavigatorDesigner, derived from ToolStripDesigner.
The Designer calls the public virtual AddStandardItems() method, which is then called by Form Designer when a BindingNavigator is added to a Form.
To make your Buttons functional, override this method in a custom class and add new buttons here.
Now your ToolStripButtons are serialized in the Form Designer and, if you double-click one of your custom buttons, a Click handler is added to the Form.
Notes you can find in the .Net Source Code:
Override this method in derived classes to define additional or
alternative standard items. To ensure optimal design-time support
for your derived class, make sure each item has a meaningful value in
its Name property. At design time, this will be used to generate a
unique name for the corresponding member variable. The item's Name
property will then be updated to match the name given to the member
variable.
As you can see, now the buttons are fully functional
I suggest you modify your custom BindingNavigator like this:
▶ Don't name your Custom Control BindingNavigator
public class BindingNavigatorEx : BindingNavigator
{
private ToolStripItem btnEdit;
private ToolStripItem btnExcelExport;
public BindingNavigatorEx() { }
public override void AddStandardItems()
{
base.AddStandardItems();
Items.Add(new ToolStripSeparator());
btnEdit = new ToolStripButton() {
Image = Properties.Resources.Edit___16x16,
Name = "bindingNavigatorButtonEdit",
DisplayStyle = ToolStripItemDisplayStyle.Image
};
Items.Add(btnEdit);
btnExcelExport = new ToolStripButton() {
Image = Properties.Resources.Excel___16x16,
Name = "bindingNavigatorButtonExcelExport",
DisplayStyle = ToolStripItemDisplayStyle.Image
};
Items.Add(btnExcelExport);
}
}
I have here some custom user control with DataGridView in it, now when i Implement my user control to some form I want to be able to access DataGridView properties within designer, is this possible?
This is my user control
public partial class MyUserControlTest01 : UserControl
{
// my way to accsses DataGridView
//
public DataGridView Dtv_userControl
{
get { return myUserControl_datagridView; }
set { myUserControl_datagridView = value; }
}
public MyUserControlTest01()
{
InitializeComponent();
}
So when i implement this user control to some Form, I can access DataGridView properties from code, but i want to do it from Designer.
Hope my question is clear,
any suggestion is helpful.
Thank you for your time.
Just add these annotations over your DataGridView-Property
[Browsable(true)]
[Category("*Any category you want*")]
public DataGridView Dtv_userControl
{
get { return myUserControl_datagridView; }
set { myUserControl_datagridView = value; }
}
I am trying to bind a list in my namespace to BindingSource through designer. I know how to bind a list in code behind but I would also like to know if it is possible to do the same in designer.
Using the "Data Source Configuration Wizard", I have selected "Object" but it shows only namespace and classes.
I choose a class with List and clicked Finish.
This is the code generated in designer after choosing the class name
this.bindingSource1.DataSource = typeof(Template.Form3);
It looks like windows forms does not have support for binding a list in designer. I am not sure this is the right method or windows forms does not support it. If windows forms have no support for binding to an object, can anyone explain the reason?
Edit:
I tried the suggestion in answer to choose a data member, but it does not bind the binding source with actual data in List. Now designer code looks like
this.bindingSource1.DataMember = "data";
this.bindingSource1.DataSource = typeof(Template.Form3);
The trick is to rebuild your solution, then any public class will be visible in the dropdown list to choose the datasource type from.
Then from the designer, click the binding source (bottom of screen) => Properties => DataMember => Select Property in your class to bind to (A List or Collection)
Edit:
Binding through the designer allows generation of columns at Design time, but it seems that you need to set the BindingSource's Data at runtime.
Since the bind object can't be static memeber but instance member.
private void bindingForm_Load(object sender, EventArgs e)
{
myDataSourceBindingSource.DataSource = (new myDataSource()).MyDataSourceList;
}
Designer:
//
// colADataGridViewTextBoxColumn
//
this.colADataGridViewTextBoxColumn.DataPropertyName = "ColA";
this.colADataGridViewTextBoxColumn.HeaderText = "ColA";
this.colADataGridViewTextBoxColumn.Name = "colADataGridViewTextBoxColumn";
//
// myDataSourceBindingSource
//
this.myDataSourceBindingSource.DataMember = "MyDataSourceList";
this.myDataSourceBindingSource.DataSource = typeof(myNameSpace.myDataSource);
Class:
public class myDataSource
{
public BindingList<myData> MyDataSourceList
{
get
{
var list = new List<myData>()
{
new myData() { ColA = "A" },
new myData() { ColA = "B" }
};
return new BindingList<myData>(list);
}
}
}
public class myData
{
public string ColA { set; get; }
}
I don't know if this makes sense for you, but this is how it works in Windows Forms.
The data binding works as it I intend, kind of... The real issue I'm running into now is what I believe to be 2 different instances of my User Control, but only the original, debug list I implemented is showing.
In short, I am building 2 lists that are technically bound to the data grid, the default debugging list I created in the default constructor and then the real list I created to bind to the data grid.
Every time I click on the user control with the data grid, the default constructor adds another line to my debugging list and displays it on the screen.
Every time I click the button that builds a list of selected options on a separate user control I can see my the options add on to the list of options I had been creating and technically set it to the data context of the data grid, the same way the default debug list does, except when I click back over to the data grid user control, the default constructor runs again, ads another line to my debug list, and displays the debug list that is being built.
Here's a copy of the class with a couple lines I added to help debug the problem.
public partial class QuotePreview : UserControl
{
private SelectionList _selectionList;
private SelectionList temp;
public QuotePreview()
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
}
private void QuotePreview_Loaded(object sender, RoutedEventArgs e)
{
//Adds item to Debugging list
_selectionList.SelectedOptions.Add(
new Selection
{
ModelNumber = "this",
Description = "really",
Price = "sucks"
});
}
public QuotePreview(SelectionList selectedOptions)
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
temp = selectedOptions;
//The list I am actually trying to display
_selectionList.AddRange(selectedOptions);
QuotePreview_Loaded();
}
private void QuotePreview_Loaded()
{
foreach (var options in temp.SelectedOptions)
{
_selectionList.SelectedOptions.Add(options);
}
QuotePreviewDataGrid.ItemsSource = _selectionList.SelectedOptions;
}
}
The implementation of the default constructor, is called every time the user control / tab, is clicked on. When that happens, _selectionList is set to the data context of the user control, followed by the Loaded Event which adds a line to my data grid.
In another user control where I select the options I want to add to my data grid user control, I click a button that creates a list of the options I want to be added and calls the custom constructor I wrote. Once the constructor finishes, it calls a custom Loaded Event method that I created for shits and giggles, that adds the selected options to my _selectionList.
Now once I click on the data grid user control again, it goes through the whole default process, and adds another default line.
If I go back a tab and say I want these options again and go back to the data grid, it again goes through the default process and adds another default line.
Whats most intriguing though is that I can see both of the selectionLists build since I dont clear the in between processes. I see a list build of the options i want to display and a list build of the default options build...
Oh, also, SelectionList does implement ObservableCollection
i don't follow exactly what you are asking but loaded event will fire whenever load is required and in your case you are switching between the views , TabControl will not render its content until it is required !
bool _isDefaultItemAdded = false
private void QuotePreview_Loaded(object sender, RoutedEventArgs e)
{
if(!_isDefaultItemAdded)
{
//Adds item to Debugging list
_selectionList.SelectedOptions.Add(
new Selection
{
ModelNumber = "this",
Description = "really",
Price = "sucks"
});
_isDefaultItemAdded = true;
}
}
I finally came up with a solution to the problem.
public static class QuotePreview
{
public static ObservableCollection<PurchasableItem> LineItems { get; private set; }
static QuotePreview()
{
LineItems = new ObservableCollection<PurchasableItem>();
}
public static void Add(List<PurchasableItems> selections)
{
foreach (var selection in selections)
{
LineItems.Add(selection);
}
}
public static void Clear()
{
LineItems.Clear();
}
}
public class QuoteTab : TabItem
{
public ObservableCollection<PurchasableItem> PreviewItems { get; private set; }
public QuoteTab()
{
Initialize()
PreviewItems = QuotePreview.LineItems;
DataGrid.ItemSource = PreviewItems
}
}
I don't want the button I add to have dotted borders when clicked, so I found out that I can disable that by turning off the focus cues. I don't want to have to change settings like this for each individual button I add. Is there any way to set property defaults in Visual Studio?
What you need to do is create a new Control based on Button and use it throughout your application.
public class MyButton : System.Windows.Forms.Button
{
protected override bool ShowFocusCues
{
get { return false; }
}
}