I am working on modifying a control on a existing site. All controls from the site inherit form a base class. I have a requirement to hide several links on the master page so I wrote this method on my control:
private void HideCartLink (bool visible)
{
Control control1 = Page.Master.FindControl( "link1" );
control1.Visible = visible;
Control control2 = Page.Master.FindControl( "link2" );
control2.Visible = visible;
}
I then moved on to another control and I had to do the same thing. So I refactored my code and modified my base class with this:
public void HideMasterPageControl (string controlName, bool visible)
{
Control control = Page.Master.FindControl( controlName );
control.Visible = visible;
}
and added this method on my controls:
void CartLinkVisible(bool visible)
{
////hide cart link
HideMasterPageControl("link1", visible);
HideMasterPageControl("link2", visible);
}
Now I moved on to a third control and realized I have to do the same thing.
Should I refactor my code one more time so that my base class has a method that knows specifically which links to hide? Or should I leave my base class generic and let my controls decided what to hide?
I am not sure what the rule is here...
If I'm honest, I'd be more inclined to turn this upside-down. I'd have something that added the buttons/links as necessary, based on the criteria of the page. This would then give you something more like a toolbar, and in fact you could make it configurable (or based on user privileges) which buttons/links appear on each page.
This saves you from the awkward position of working out on what basis to hide things that may or may not even be there.
Of course, assuming you can't do this, you're probably making the best of a bad job to be honest. Don't worry too much further about the about the final implementation. With only a few controls you're okay and would be making it worse by over-complicating it.
You might want to consider the Visitor pattern, though. This would mean you implement a class that "visits" all the links on a given control and goes to configuration to look up whether they should be hidden or not. This would save you hardcoding the control names for the links, and allow the method to be in a base class that has no idea what actual controls there are.
I'd have a generic base method that would call a virtual method to get a list of links to hide and cycle through them. The base implementation of the second method would contain the list of most commonly hidden links. Each control I'd just overwrite the method to add or modify that list.
Though, I have to agree with Neil, it seems better if you flip the logic and decide which links need to be included vs. which need to be excluded.
Related
I want to fill an updatepanel with new dynamic controls in response to a button click.
However, I also want to be able to access the values of these dynamic controls in response to an event in one of the controls.
Specifically, I want the button to bring up two dropdownmenus. One of the menus (or both if need be) is in another update panel. I want the first menu in the update panel to change its data in response to a value getting selected in the other menu.
I think my problem is that when I cause a postback with one dropdownmenu I lose the other dropdownmenu because I created it in the button_click handler.
I know I should create dynamic controls in the Page_Init method (or so ive heard) but I only want the controls to show up if the button is clicked. There are other buttons on the page which need to create a different set of dynamic controls.
Thanks.
There are a lot of ways you can handle this, and which approach to take really depends on your project's requirements and your available resources.
The smoothest way to do it that would generally provide the best user experience would be to use a Javascript technique to hide and show controls as the page required them. JQuery is the library I would recommend for this. On the most basic level, you simply wire the control's activation (such as a button_click event) and hide or show a div containing the dynamic content as necessary, like so:
$("#control").show();
// and
$("#control").hide();
Alternatively, you can do this in C# by using the Visible property on many of the normal web controls for ASP.NET. The usual code-behind approach would look something like this:
private void btnControl_Click(object sender, EventArgs e)
{
var dynamicControl1 = FindControl("dynamicControl1");
dynamicControl.Visible = false; // or true, as the case may be
}
This particular approach is mostly attached to code-behinds, though, which I would encourage you to avoid if possible. They are practically impossible to test and will make projects a pain to work in. You can use a similar approach in the MVC3 framework, of course, it will just be a little different how you send and receive the control you are setting to not be visible. The other benefit this has that is kind of nice is that if something is set to not be visible, it tends not to even be displayed in the HTML generated by the templating engine (YMMV depending on the engine, but I know this is true in Razor). So someone viewing the source of your webpage won't be able to see inactive controls, which may or may not be something that appeals to you.
EDIT: I see the problem is less to do with how to display these things, and more with how to create and read them back given on-the-fly input.
I'm sure there's a way to do this with Javascript (which would more than probably be the cleanest and best way to do this), but I'm not good enough with JS to know the answer to that one. The way you would handle this in ASP.NET is make the div you're going to add controls to server-side (by using runat='server', then add what you need there. Again, the trivial code-behind approach would be something like:
private void btnControl_Click(object sender, EventArgs e)
{
foreach(var checkBoxChecked in chkBoxes.Where(x => x.Checked))
{
div.Controls.Add(new WebControl()) // or whatever the heck else it is you need.
}
}
This presumes that you have an IEnumerable<CheckBox> to iterate over, of course. You may also want an IList<WebControl> to keep track of all the junk you're adding. You will also need to make sure the CSS is applied properly to the div for the controls you're adding. And again, code-behinds are pretty awful and I use the example only because it'd be easy to spin up in a project to test for yourself.
I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.
I have a c# winform that is a template for all the forms in my project.
My problem is how do I setup some properties for the controls that will be added in the inherited forms from the baseform.
For example I want all the textboxes that will be in the forms to have a specific color, or call an extension method.
Right now I tried the simple idea that popped out:
foreach (Control c in Controls)
{
if(c is ComboBox)
{
//do something
}
if(c is TextBox)
{
//do something
}
}
I put this code in the base form load event, but with no luck. I tried changing the modifiers from the inherited form to protected, but with no luck.
Is there any solution to this problem? Or I am obliged to put this code in all of my forms that inherit baseForm?
Custom Controls are the solution to the problem you have at hand. Simply extend existing Controls to have attributes of your desire and then you could these controls in all of your Forms.
You're beginning to think along the right lines, but you're not quite there yet. The solution to this problem is definitely object-oriented inheritance, but you must be careful not to violate other important principles of OOP, namely encapsulation.
To put things a different way, the form should not be required to "know" about the properties of the controls that it contains. It shouldn't know or care that it contains a ComboBox or a TextBox or a ListView.
Instead, you should subclass each of the child controls that you want to modify, and set their default properties there. Then, you would just add an instance of your custom subclassed control to your form, rather than the built-in control.
So, for example, your custom TextBox class might look like this:
public class CustomTextBox : TextBox // inherit from TextBox
{
public CustomTextBox()
{
// default constructor
}
// other logic...
}
I'm working on an application that presents the user with varied data, depending on the object being viewed. The objects are all of the same interface just with extended properties beyond once distinguished.
I'm looking for the "best" way to display a type-dependent control to the user. I would like to use tabs but I'm stuck with .NET 2.0 and from what I can gather the only way to hide/show tabs are to remove them and re-add them. That might be the best way but that leads to issues regarding blinking of the GUI components, keeping tabs on the active tab when reloading, etc.
I could make custom controls for each and either have them all loaded and hide/show when necessary (which I have done in the past on projects), or dispose and re-instantiate them...
To clarify best, I would say the closest balance between code elegance and program efficiency.
I have used and have had the best luck with loading them all and then showing/hiding the ones needed.
Disposing and re-instantiating everything always made things very messy.
In order to not have load time be horrible, you can instantiate them on first use. Something like:
IView LoadView(Type dependantType)
{
// get the view or create one
IView view = GetView(dependantType);
if (view == null)
{
view = InstantiateViewAndAddToForm(dependantType);
AddView(view);
}
//
// do some binding to your model or whatever here
//
// make the correct view visible
foreach (IView v in Views)
view.Visible = v == view;
}
Could you just create a panel for each object and have a dictionary associate the object type and the panel?
You could just tell the panel to bring to front if they are all the same size, or set all Panels.Visible to be false, and just set the one you need to be true.
I have used DockPanel Suite for applications that require multiple tabs.
It is an open source project, so you can actually modify the code if you wish.
The Suite has many functions, however, if you can just use the Tabs.
My question is simple: how bad is the following snippet of code? How would you do it?
CancelEventHandler _windowClosing;
private CancelEventHandler WindowClosing
{
set
{
clearEventHandlerList();
this.Closing += value;
_windowClosing = value;
/*
* if calling the method with null parameters,
* it will set up itself as the primary control on the Window
*/
_windowClosing(null,null);
}
get
{
return _windowClosing;
}
}
private readonly CancelEventHandler[] CONTROLS = null;
private int current = 0;
public InitializerForm()
{
InitializeComponent();
/*
* these are the handlers for the different controls,
* in the order of appereance to the user
*/
STATES = new CancelEventHandler[] { handler1, handler2, etc. };
WindowClosing = CONTROLS[0];
}
private void clearEventHandlerList()
{
foreach (CancelEventHandler c in CONTROLS)
{
this.Closing -= c;
}
}
private void handler1(object obj, CancelEventArgs e)
{
if (obj == null)
{
//hide every other control, but this one, also set up if necessary
}
else
{
//do something
WindowClosing = CONTROLS[++current]; // set the next control to show
e.Cancel = true;
}
}
The point would be that the code wouldn't close a form, but instead show another component on it, and the set the way to handle that (this is mobile platform, so clicking OK button on the top generates a closing event). This is because showing several forms (4 or 5) one after another to the user makes the app blink, and also very annoying, while replacing just components is much smoother. This model works, but seems very nasty, and I would like a cleaner way to handle this.
Update:
I updated the code sample so that variable names are somewhat speaky. Still, I'm convinced this is awful, (a) but not sure how much, and more importantly, (b) how to do it better.
Update 2:
So, it seems that the code is still a bit mysterious.
Now here's what the problem is:
I show the user a form, which instructs him what to do in several languages. He proceeds by clicking OK on the window. Next, I ask for his language, and then a few questions (where his/her GPS is, etc.) like this. After he could answer the questions (this shouldn't take more than a few seconds each), I show him a splash screen (I load stuff in a separate thread meanwhile), which has a picture. Showing these forms one after another makes the whole application start slow, and filled with UI lags.
Here's what I do to work around the lags: I put the content of the windows into panels, and put those panels one on another, and hide every one of them but the one that should be visible to the user. (current variable) Each of the windows does different things, so I need to change handler of the window closing event in addition. In this code the part which enables the panel is in the same function (handler1, handler2, etc.) with the part which handles the window closing event. If the arguments are null, it does the former, if it isn't (that means it was triggered by the user) it does the latter.
I need an extensible solution to this so that I can insert and remove dialogs anytime I want (the order and the pointers to the functions are stored in the CONTROLS field, and this seems to be very convenient, if you actually understand it. Although it is never easy to change the entire content of a form, there ought to be a simpler way to do this, as well a nicer one, that is what I'm looking for.
I hope this time I could explain how the model works.
I think it might be theoretically possible to make that code more delightfully diverting, perilously puckish, jovially jeopardous, cheerily chancy and unwarily whimsical but it would require some serious thought.
somehow your code makes me want to cry, i´m sorry. i read it twice and all i know about it is that it "doesStuff" with "STATES".
if you really want some help on this one you will have to work on it yourself first...
Use, XML! It's human-readable!
More seriously-
It seems like you're trying to create some sort of configuration wizard, so I'd start by researching that. Regarding your particular solution, I generally advocate very strongly against the "layered panel" approach. I do so because I maintain apps written by people who found this approach, or the related "hidden tabs on a tab control" approach, to be a good idea. It's not, and maintainers will curse your name for years to come.
That being said, what alternatives are there? Well, one alternative is what you've already dismissed because of its "flicker". I'd say that, in general, the flicker isn't that big of a deal for a quick and dirty application. It might be a good idea to make sure that your new window is called up before closing the old one, for example. (I'm assuming this is possible, I haven't developed on a mobile device.)
Another possibility might be a less-evil version of your layered panels. Instead of throwing a half-dozen panels into one form, create a separate user control for each wizard page and then add/remove the user controls to a containing form. This can avoid your flicker and will prove to be much easier to maintain because each page is in a different control. This might also ease any subsequent "Back" button functionality and make your data structures more naturally defined because those user controls will be associated with a specific logical bit of data. It's still not ideal, but it's probably good enough for a one-off solution.
A third technique, if you foresee extensive wizard modification as the product matures, might be to generalize the creation of your user controls by defining them in a more logical/declarative manner (e.g. via XML). If you dynamically generate sensible controls based on XML, then modifying the panels might be as easy as diving into your XML and doing something like:
<Questions>
<Question type="Text"> <!-- generate a textbox for the answer field -->
Favorite Color:
</Question>
<Question type="Number" range="0-255"> <!-- Maybe this is a spinner -->
The answer to life, the universe, and everything:
</Question>
</Questions>
That's just off the top of my head, and completely overkill for any one-off application, but it's a possibility.
Now, let me caveat this by saying this might work, but it may not be the answer to your real problem - that of a slow and unresponsive UI when you have a lot of forms. The real answer may be to just go ahead and do all separate forms, but have each form load its child forms in a background thread while the user is staring at the first form.
But assuming you're still set on this, I'd start off by making a separate class just to handle the Panel stacking/hierarchy. Call it PanelManager. You would instantiate the PanelManager and associate it with the main form, then add Panels to it (perhaps keyed to a String) and set the order. In the main form, have the closing handler call PanelManager.CloseCurrentPanel() and if there are no more Panels to show then it's time to close the main form.
Time for pseudo-code! Here's a quick idea for the class, i'll leave it to you to implement it:
public class PanelManager {
// constructor
public PanelManager (Form ownerForm);
// short-cut properties
public Panel this[int idx]
{ get; set; }
public int Index
{ get; set; }
// main functionality
public int AddPanel (Panel p);
public void SetPanelOrder (Panel p, int idx);
public void RemovePanel (Panel p);
public void RemovePanelAt (int idx);
// shows the first Panel
public void Show ();
// shows Panel[idx]
public void Show (int idx);
// adds the panel to the top of the stack and displays it
// returns the index of the panel
public int AddPanelAndShow (Panel p);
// hides the current panel, displays the one underneath it
// returns false if there are no more panels
public bool HideCurrentPanel ();
}
in the constructor for the main form, instantiate it by new PanelManager (this), then in the closing event handler, call panelManager.HideCurrentPanel () and then figure out whether or not you need to close it after that.