I am building some applications that use OPC to control some industrial automation. I have decided it might be a good idea to create some custom controls for standard things I will use such as buttons and text boxes. One of my main reasons was that I felt in an ideal pattern the end object i.e. the button is the one who holds information about the OPC item it refers to otherwise in a typical application my Form class gets polluted by tons of variables that are in my mind scoped more global then they should. I started then by using the Tag property of the button but this requires some overhead code that is the same for each instance. I felt like the right thing to do is subclass controls that I would like to use and provide properties to configure each one.
With that context in mind my real question is this. To make it as portable as possible I decided there should a property to define the OPC Group. I declared a property like this
public class OPCButton : Button
{
[Category("OPC")]
public OPCConnectedGroup
{
get { return _OPCGroup; }
set { _OPCGroup = value; }
}
}
This shows up int the property list when I add the control but I am unable to bind this property even though my Form1 contains
public OPCConnectedGroup Connection1 = new OPCConnectedGroup();
I have resolved that the way to solve this is to probably create an additional control like OPCGrp that can be added to a Form then the Controls can reference this. To test I added a property of type Button and sure enough when I added it and browsed to the property it gave me options for all the buttons on the Form. I have no huge problems with this approach I just want to make sure that Im following a prototypical pattern because I will be responsible for maintaining the control library but not always implementing and Im trying to get it down to a 1-2 step process to implement a control.
Thanks
Matt
When defining OPCConnectedGroup you can inherit Component class.
This way if you have a property of type OPCConnectedGroup in your OPCButton, then at design time, you can put instances of OPCConnectedGroup on the form, and then if you choose your OPCButton at designer, that property of type OPCConnectedGroup will show as a drop down list that you can select one of instances that you put on the form for it.
Example:
If I have such MyButton and MyClass:
public class MyButton : Button
{
public MyClass MyClassInstance { get; set; }
}
public class MyClass : Component
{
public string SomeProperty {get;set;}
}
Then you can put some (or one) instance of MyClass on the component tray of the form:
And then if you select MyButton on your form, you can choose one of MyClass instances from in property grid:
Related
I have code that dynamically loads (and destroys) user controls of different types onto a form as they are needed. The user control uses properties from its parent to load certain values into controls such as textboxes and check boxes. The issue is that in the line:
activeControl = new userControl1(params...);
my constructor is obviously going to try loading fields that don't exist yet (they belong to the parent form) since it hasn't been added to the parent form until after userControl1 is created.
I tried solving this using the ParentChanged event of the user control so it waits until its parent is set before loading fields. The problem here is when I destroy userControl1 and create a userControl2, the parent changes and it tries to load the fields from the null ParentForm (the exact problem I had originally.
Is there a preferred way to tackle this? Clearly, I could just add something like
if(this.ParentForm == null) { return; }
to the ParentChanged event or something along those lines. But this feels neither elegant nor safe. Am I overlooking some event or preferred way to accomplish this?
While it is not necessarily bad to check for null references and divert the code accordingly (e.g. not try to reference a missing object), the better approach here is to decouple your UserControl classes from their parents.
That is, these UserControl classes should not have any awareness of their parent form types at all. To do otherwise is to negate one of the major benefits of OOP: code reuse. With your current design, even if you get it to work without crashing, you can't ever use these UserControl objects except in the context of this specific parent form type.
You may think today that that's okay, that you'll never want to use them in any other context. But the future is hard to predict. You may decide, for example, that you want a variation on the parent form that should still use these same UserControl objects.
What you should be doing is exposing the UserControl state via public properties, and then allow the parent form to initialize those as necessary.
Here's a very simple example (I'm omitting the Designer-created codeā¦I assume that you can infer from the field names what I mean):
partial class UserControl1 : UserControl
{
/* ... */
public bool IsMyFlagChecked
{
get { return checkBox1.Checked; }
set { checkBox1.Checked = value; }
}
/* ... */
}
partial class ParentForm : Form
{
/* ... */
private void SomeMethodThatAddsUserControl1()
{
UserControl1 uc1 = new UserControl1;
uc1.IsMyFlagChecked =
this.SomeParentFormPropertyUserControl1UsedToReferenceDirectly;
// other initialization for uc1...
Controls.Add(uc1);
}
/* ... */
}
(Note that without a good, minimal, complete code example that illustrates your question, I can't provide a more detailed code example than the above. If this doesn't seem to address your question, please edit it so that it provides more detail, including a better code example).
I'm making such a tabbed "Product Version Control", using an UserControl which encloses fields, checkboxes, text areas, grids, and so on. At runtime, when I add a new tab (like we have on browsers), I instantiate a new UserControl (with all components inside). The issue is: I can get all internal component values and fill an object to persist, but not the other way. I can take all values from Object and set them to EditValue (or Text/Value properties) properties of internal components, but they are not showing up to the user.
Consider:
All my internal components have public set/get methods like shown below
public class TabVersion : UserControl
{
...
public DevExpress.XtraEditors.SpinEdit seRevision
{
get
{
return _seRevision;
}
set
{
_seRevision = value;
}
}
...
}
Inside TabVersion class, I have a "fill" method which receives an "Version" object and set all internal components "EditValue" properties like shown below:
public class TabVersion : UserControl
{
...
public void FillTab(Sheet sheet)
{
...
this.seRevision.EditValue = sheet.NumRevision;
...
}
...
}
At runtime, all EditValue component properties are filled but not showing to the user. Do I have to set "Enabled" or "Visible" properties for each component inside my usercontrol?
Any Ideas?
Thank you in advance.
Well... After googling everywhere with no goal, I wrote a "Fill" method that fills all DevExpress controls inside my user control, and called it inside the "Shown" event at the parent form. I think that's a bug from DevExpress, because all Windows default components works all right when I give them their respective values. That's it.
In my asp.net 4.0 application I have a standard set of tabs controls across the application that has one tab for Search, one for View and one for Reporting. I am in the process of updating those tabs and I was hoping to be able to wrap their functionality up into a single user control since they all function in pretty much similar ways. The issue I am having is how to create a property called SeacrhTab/ViewTab/ReportTab that allows me to assign a different user control to it depending on the area of the application (e.g. CompanySearchControl, ContactSearchControl, etc.) Anyone ever tried to accomplish something similar? How should I approach this?
You should just be able to create a regular property in the partial class definition in your code behind page. Make it public and of the type of generic class that includes all tab controls and you should be set.
I have not made other controls properties of a user control but I have certainly used all sort of other custom classes as properties of user controls, so I don't see why it wouldn't work.
Assuming CompanySearchControl and ContactSearchControl both inherit from GenericSearchTabControlClass, for instance:
public GenericSearchTabControlClass SearchTab {get;set;}
public GenericViewTabControlClass ViewTab {get;set;}
public GenericReportTabControlClass ReportTab {get;set;}
Create an enum for the specific layout, view or tab definition ie: SearchTab, ViewTab etc. specify that as a Property of your UserControl, then use that propery to create, or hide and show a specific PlaceHolder or Panel depending on the required layout, or functionality.
public enum ControlLayoutEnum
{
SearchTab = 0,
ViewTab = 1,
ReportTab = 2
}
public class TabViewControl: System.Web.UI.UserControl
{
public ControlLayoutEnum ControlLayout { get; set; }
protected override OnInit(object sender)
{
// Create controls required or Hide/Show PlaceHolder or Panel etc
switch (this.ControlLayout)
{
case ControlLayoutEnum.SearchTab: // Create Search Layout
break;
}
}
I want to have the WPF controls inherited like this,
public class BaseView : UserControl
{
...
}
public class BaseListView : BaseView
{
...
}
public class TeachersListView: BaseListView
{
}
public class StudentsListView : BaseListView
{
}
Here "BaseListView" is the base classed. This classes may have several functionalities which depends on the ListView present in "BaseListView". I want to inherit this "BaseListView" to several views which may add several column's and with different databindings. So my requirement is
class BaseListView : BaseView
{
This class will have the UI parts like commandStrip and followed by "Empty ListView".
This "ListView" may not hold any columns in it.
}
class StudentsListView: BaseListView
{
In **XAML** part, Columns and its appropriate Data Binding will be added. I need to access the controls in .cs file. so that i can access the controls.
void FindAndHighlightColumn()
{
// get the columns to find and search the list view and highlight.
}
}
How can i achieve this, what is the right way to do this.
I think you want to create a UserControl (Items- or ContentControl) with some basic functionality like FindAndHighlightColumn().
You can create your BaseListView "lookless" and then use ControlTemplates to get several flavours of it (create styles like "StudentListViewStyle" etc., each with an appropriate ControlTemplate). A ControlTemplate is a view, so you can specify different bindings in each template, to access controls by their names you need to define a convention with certain parts named 'PART_XY' etc., look at the standard ProgressBar-Control for an example.
Are you using Model-View-ViewModel architecture? After reading your question, my initial thought is that I would not use inheritance to the extent you are thinking of. Instead, I would consider composition.
For example, could you create a composite StudentsListView by composing a .xaml file of multiple user controls? Maybe a user control to display your commandStrip and another user control to display the appropriate ListView (e.g. StudentListViewUserControl, TeacherListViewUserControl, etc.).
I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).
In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)
frmForm fm=new frmForm();
fm.tabMain.Selected=tabVitamins;
I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.
Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.
Please advice.
Thanks,
Karthick
When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.
To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.
For example:
frmForm myForm = FindForm() as frmForm;
if(myForm != null)
myForm.tabMain.SelectedTab = myForm.tabVitamins;
If the control is on some other form, this code won't do anything.
By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.
SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.
It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.
However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.
In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.
Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();
Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.
If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.
Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :
private frmForm ParentForm;
public frmForm UCParentForm
{
set { ParentForm = value; }
}
So then in your main form's code, perhaps in the Load event like this :
private void frmForm_Load(object sender, EventArgs e)
{
TheUserControl.UCParentForm = this;
}
or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().
In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :
private TabControl mainFormTabControl;
public TabControl MainFormTabControl
{
set { mainFormTabControl = value; }
}
imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.
good luck !
You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.
In your user control constructor try something like this.
private frmForm fm;
public YourUserControl(frmForm fm)
{
this.fm = fm;
}
Then you could use.
fm.tabMain.Selected=tabVitamins;
Does that help?