I have an application with several DataGridView controls. I would like to create a master control or component (I'm not sure which one to use) where I can define basic coloring and behavior. However, since all DataGridView controls have different columns in the application I want to define columns after placing a new instance on the form and not in the master form.
Can you suggest a way to do this? Thanks in advance.
Have something like your own MasterGridView where you define all common behavior, colour, etc.. & then inherit all respective datagridviews from this one.
public class MasterDataGridView: System.Windows.Forms.DataGridView
{
public MasterDataGridView()
{
BackColor = Color.Yellow;
// define other behaviours
}
}
public class OrdersDataGridView : MasterDataGridView
{
// data binding, column addition etc can be handle in respective grid views
}
public class ReportsDataGridView : MasterDataGridView
{
}
...
etc.
Related
I'm currently working on refactoring a lot of duplicated code in a couple of UserControls in Windows Forms project.
The architecture leaves much to be desired with a lot of logic implemented inside the UI layer. This will be fixed eventually, I'm now working on improving what I have on my hands now.
The problem is, that a lot of duplicated code relates directly to controls, for instance:
private void InitDestinationPathControls(string path)
{
if (someField)
{
tbOne.Enabled = false;
tbOne.Visible = false;
btnTwo.Enabled = false;
btnTwo.Visible = false;
tbOne.Text = string.Empty;
return;
}
// (...)
}
Don't get too attached to the cited code itself, it is just an example.
I'd like to move this code to a common base class, but it relies directly on specific fields (even though they are exactly the same in all controls too). Those fields, on the other hand, are generated by the designer, so I cannot extract them to the base class.
The only thing that comes to my mind is to pass those fields as parameters to a method in base class, but then if some method uses a lot of them, I'll end up with a monstrous interface part and that won't actually improve the readability too much.
How can I deal with such common parts of user controls in Windows Forms?
Apparently you have a combination of several controls that appears in several forms. In other words: you have for instance some buttons, comboboxes, etc, that you want to show on different forms, and you want them to have the same behaviour.
If not only the behaviour of these controls is the same on all forms, but also the layout, then consider to create a class derived from UserControl.
The UserControl hides from the outside world which controls are shown, how they are visualized and how they behave.
I assume that you already pulled the model out of the visualisation of the data.
If all instances of this user control should all call the same functions of possibly a different object of the same class, then give your special user control a property that represents this class, or at least an interface. During construction you can plug in the actual class that should handle the actions after operator input.
If, on the other hand, the layout differs on each form, but the collection of buttons, comboboxes, etc and their behaviour is similar on all forms that show this collection of controls and they have a lot of common behaviour, consider to create your own ControlCollection.
For instance, if on several forms you have a button to select a (text) file, labels with the name, size and creation date of the selected file, and an edit box that shows the content of the text file, but you want to layout them differently, consider something like this:
class FileDisplayControls : IDisposable
{
public Button ButtonSelectFile {get;} = new Button();
public Label labelFileName {get; } = new Label();
public Label labelFileSize {get; } = new Label();
public TextBox textFileContents {get; } = new FileContents();
private void ButtonSelectFile_Clicked(object sender, ...)
{
// TODO: open file dialog, display result in labels and text box
}
}
Constructor can set initial layout properties of the controls, and subscribe to events, such that the controls will react on user input.
The user of the class (= code, not operator) immediately has a collection of controls that have some standard behaviour, like react on button click. All he has to do is set the location of the items in his own form. If desired change other layout properties (colour, background) and put them on his own form.
If you want to prevent that others change other visual aspects of the controls than the position, don't publish the control themselves, only the position of the control:
public System.Drawing.Point LocationSelectFileButton
{
get => this.buttonSelectFile.Location;
set => this.buttonSelectFile.Location = value;
}
public System.Drawing.Point LocationFileContentTextBox
{
get => this.textBoxFileContent.Location;
set => this.textBoxFileContent.Location = value;
}
etc.
If needed, you can add events for users:
public event EventHandler SelectedFileChanged;
public string FileName => this.labelFileName.Text;
public string FileContents => this.textBoxFileContent.Text;
etc.
Conclusion
The solution that you choose depends on the similarity between the controls on the various forms:
if Behaviour and Layout are all the same: UserControl
If only position and a few properties different: special class with the properties that are different. This way you can force a more similar style: all "Select File" buttons look similar.
If only one or two behaviours are different: add Action<...> properties or events
If you want full control of the layout: expose the Controls.
The behaviour that is common for all you forms that show these controls (in my example: how to select a file and what to do when a file is selected) is inside the class.
repeated code can be extracted to method (possibly in base class, or as static method in helper class)
public void DisableControls(params Control[] controls)
{
foreach(var c in Controls)
{
c.Enabled = false;
c.Visible = false;
if (c is TextBox t)
{
t.Text = string.Empty;
}
}
}
private void InitDestinationPathControls(string path)
{
if (someField)
{
DisableControls(tbOne, btnTwo);
return;
}
// (...)
}
I have class derived from Form and it contains a TableLayoutPanel and in it one Label and one Panel. When I create instance of this Form, all properties and events of controls in design editor are read-only. Is there any way how to expose whole object for editing? I know that I can expose properties one by one, but that is not the best way in case when you want all of them.
Have a look here:
Avoid Visual Inheritance
The TableLayoutPanel control does not support visual inheritance in
the Windows Forms Designer. A TableLayoutPanel control in a
derived class appears as locked at design time.
You can use internal or make a getter method / property
public Label GetLabel() => return someLabel;
or
public Label MyLabel { get { return someLabel; } }
or
internal Label someLabel;
I'm new to C#, and I'm new to the idea of "partial" classes.
I wish to access the "grid" variable outside of this "MainWindow" class. How would I go about doing that?
Partial means that your class is split among different files, it has nothing to do with the exposure of variables to other classes.
Your grid is a local variable in your current method, so it's not accessible by others. If you want to make it accessible, define it as a property instead.
public DataGrid Grid { get; set; }
Even though it is technically possible, you should not make your data grid accessible outside the class. The grid is part of the view managed by your class, so making the grid accessible to other classes breaks encapsulation by making implementation details of your form visible.
I have another class, Server, and it receives data that I wish to add to grid.ItemSource.
Then your Server class should provide a data source to which your form should bind the grid. In other words, the access should go in the other direction.
You need to declare the variable as a public member of the class like this
public partial class MainWindow ...
{
public DataGrid grid;
public MainWindow()
{
...
}
public void DataGrid_Loaded(...)
{
...
grid = sender as DataGrid;
...
}
}
Now you can access to the variable in this way
var x = MainWindow.grid;
I have a usercontrol where I want a property that can list all the other instances of the same usercontrols in the Windows Form.
Eg. I have a simple usercontrol (sidebarbutton). I drag-drop 2 instances of it in a UserForm. Now I want a property (in the usercontrol itself) that can list both of them.
I have written this property. However, when used in Property Browser Window of Visual Studio, it allows me to add new instances of sidebarButton control.
private List<SidebarButton> _sidebarButtons;
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always), Category("Roshan")]
public List<SidebarButton> SidebarButtons
{
get { return this._sidebarButtons; }
set { this._sidebarButtons = value; }
}
How to add the 2 instances that I drag-droped in the form in this property.
I know the property needs to be modified but don't have a proper direction to take. Please HELP me.
One way of doing this could be that upon addition of the user control to the form, the user control notifies all other user controls of that type that it now exists. Your user control can implement an interface like so:
public interface INotifiable
{
public void AddToList(INotifiable newButton);
}
When the user control is added to the form you iterate through all the controls and check whether they implement this interface. If they do you call the AddToList method and pass the newly added control to it.
I'm developing a WPF wizard WiX custom managed bootstrapper application.
Some elements of the wizard are common (for example, the product information and logo at the top), and I don't want to have to redefine these. I also don't want to have to redevelop the whole thing from scratch for each install (yes, there are a number of installs I want to use this with and they have different wizards).
I have a model like this (simplified to avoid confusion)
public class WizardModel : INotifyPropertyChanged
{
private UserControl _currentPage;
public UserControl CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
if (PropertyChanged != null) PropertyChanged(this, "CurrentPage");
}
}
}
I would like to bind a control so that the control in CurrentPage is displayed when the property changes.
I did experiment with using ContentCrontol but I quickly dismissed that as not intended for this purpose, and I'm really not sure where to go from here.
I'm making a few guesses here because there's not a lot of detail in the original question, but I think I get the idea of what you are trying to accomplish.
Seems like you would want to have a base class with all of the common controls on it already. Say we call this class WizardUserControl. Anyway, let's say we define a Grid inside the WizardUserControl (let's call it CustomGridArea) and that grid will be the main area where we can drop all of our custom controls based on which page of the wizard we are processing. So maybe WizardUserControl may implement a method like:
public class WizardUserControl : UserControl
{
...
// All your standard wizard code stuff/behavior/business logic/etc...
...
...
public void InsertCustomizedControl(UserControl customizedControl)
{
CustomGridArea.Children.Clear();
CustomGridArea.Children.Add(customizedControl);
}
}