I'm refactoring some code and I've gotten into the practice of doing this:
protected void Page_Init(object sender, EventArgs e)
{
Logger.Info("Page Initialization.");
//Provides highlighting/docking functionality at the start, but overshadows controls in more complex scenarios.
RadDockZone1.Visible = (RadControlStates.SplitterStates.Count == 0);
ControlRegeneration.RegenerateReportMenu(lstBxHistorical, lstBxCustom);
ControlRegeneration.RegeneratePaneChildren(RadPane2);
ControlRegeneration.RegenerateDockZones(Page);
ControlRegeneration.RegenerateDocks(RadDockLayout1, RadDock_Command, UpdatePanel1);
}
I'm wondering if it is good practice to pass Page and Page controls to other functions like this.
I was considering creating a singleton that will hold references to the relevant page controls, and then accessing the controls through that instance instead.
Something like...
public class DashboardPageControlsRepository
{
private static readonly DashboardPageControlsRepository instance = new DashboardPageControlsRepository();
private DashboardPageControlsRepository() { }
private Control myPanel;
public static DashboardPageControlsRepository Instance
{
get { return instance; }
}
public void SetPageState(Page page)
{
myPanel = Utilities.FindControlRecursive(page, "UpdatePanel1")
}
public Control Panel
{
get { return myPanel; }
}
}
Then, during page init before anything happens I would go and grab all my controls -- allowing me to access them through here rather than passing them down.
Any thoughts on how to handle this?
The problem with creating singletons in this manner is that the static instance will exist for the lifetime of the AppDomain (until it is recycled). On top of that, multiple requests accessing the singleton will be attempting to mutate the singleton's state independently.
What services would this repository offer other than as a container for control references?
The other thing I would mention, is don't specialise your methods too much, you should consider the least required type approach to method design, e.g. you currently have:
public void SetPageSize(Page page)
In which the method is only really interested in accessing the Controls collection of the System.Web.UI.Control type. You could redefine the method as:
public void SetPageSize(Control control)
Related
I have a WinForm that I have used to build and test an email newsletter. This form contains a number of methods and events.
I am now adding a new feature to my program to allow split testing (A/X Test) and therefore creating up to 4 different newsletters for a campaign.
Therefore I'd like to extend my form to accommodate both normal newsletters and A/X newsletters. I'm going to add two different modes to my form. Something like:
private enum CampaignMode { Normal, AxTest };
They will be very similar in appearance, except a number of controls' visibility will change.
Apart from that, almost all methods and events will have two separate ways of handling.
What design pattern should I use so that I don't have to create a new separate form?
For simplicity, let's say my form has the following methods:
Constructor: probably receiving the EditMode as a parameter
Load
Create: Button click event
SetControlViews: Based on EditMode set the visibility of controls
MethodA: Specific to Normal mode
MethodB: Specific to AxTest mode
Sounds like Template Method would be a good candidate.
Make a base class which takes care of logic for the basic initialization, Load(), Create(), SetControlViews() and then make Method() virtual and override in two derived classes according to specialized logic. Also, the constructor of each derived class could do some specialized initialization.
This way you don't even need a CampaignMode enum (or EditMode if you meant those to be identical). The existence of different entities there is illustrated by the existence of different derived classes.
I would simply have pairs of functions for each method/event and then have the main handler call the appropriate one, depending on the value of CampaignMode.
public enum CampaignMode { Normal, AxTest };
public partial class DemoForm : Form
{
private CampaignMode campaignMode;
public DemoForm(CampaignMode mode)
{
InitializeComponent();
campaignMode = mode;
SetControlsVisibility();
}
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
//Set normal controls visible;
//Set axtest controls invisible;
}
else
{
//Set normal controls invisible;
//Set axtest controls visible;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (campaignMode == CampaignMode.Normal)
{
MethodA();
}
else
{
MethodB();
}
}
private void MethodA()
{
}
private void MethodB()
{
}
}
Note: if there are many controls, then it is neater to have a visibility function with a bool parameter:eg
private void SetNormalVisibility(bool isNormal)
{
//normalTextBox.Visible = isNormal;
//normalButton.Visible = isNormal;
//axTestTextBox.Visible = !isNormal;
//axTestButton.Visible = !isNormal;
}
In which case, change the SetControlsVisibility function to:
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
SetNormalVisibility(true);
}
else
{
SetNormalVisibility(false);
}
}
HTH
Jonathan
I'm trying to work with Windows Forms and User Controls and thus far it's been nothing but a headache. I can't make the form or the controls static because the designer doesn't like that and when I use Singleton on my form and controls, the designer still throws errors at me.
My FormMain:
public partial class FormMain : Form
{
private static FormMain inst;
public static FormMain Instance
{
get
{
if (inst == null || inst.IsDisposed)
inst = new FormMain();
return inst;
}
}
private FormMain()
{
inst = this;
InitializeComponent();
}
MainScreen.cs:
public partial class MainScreen : UserControl
{
private static MainScreen inst;
public static MainScreen Instance
{
get
{
if (inst == null || inst.IsDisposed)
inst = new MainScreen();
return inst;
}
}
private MainScreen()
{
inst = this;
InitializeComponent();
}
If the constructor of MainScreen is public the program runs, but when I change it to private I now get an error in FormMain.Designer.cs saying "'Adventurers_of_Wintercrest.UserControls.MainScreen.MainScreen()' is inaccessible due to its protection level". It points to this line:
this.controlMainScreen = new Adventurers_of_Wintercrest.UserControls.MainScreen();
I think this is the instance of the class that the designer makes by default. Should I ditch the designer? Or is there a way around this? Or is there another way to make class properties accessible without using Singleton (since I can't seem to make the form or controls static)? Any help would be greatly appreciated.
You need to keep a reference to each instance of each form if you want to access the public properties of the instantiated form.
One way is to have a class with a static variable for each type of form:
class FormReferenceHolder
{
public static Form1 form1;
public static Form2 form2;
}
This way you would set the static variable whenever you instantiate a form, and then you can access that variable from anywhere in the program. You can go one step further with this and use properties that set up the form if it doesn't already exist:
class FormReferenceHolder
{
private static Form1 form1;
public static Form1 Form1
{
get
{
if (form1 == null) form1 = new Form1();
return form1 ;
}
}
}
...
static void Main()
{
Application.Run(FormReferenceHolder.Form1 );
}
I think I answered a previous question about this, which looks like it is what got you started down this route. The first point is that I wasn't recommending this pattern specifically, just trying to teach you more about how software developers can manage scope.
That said, the problem you are facing isn't insurmountable. You could hobble a public constructor by throwing an exception at runtime and not at design time, for instance, and modify Program.cs to use the static Instance instead of manually constructing the form.
But.
As I said in the other question, the better option would be to change architecture so that you don't need your library code to directly manipulate the GUI in the first place.
You can do this either by just having the GUI ask the library questions when it thinks it needs new data (simple functions) or by letting the GUI be notified when something needs to change. Either method would be better than having the library fiddle with labels directly.
A good place to start would be something like an MVC (model-view-controller) architecture, which I was alluding to in my previous answer. It might be best, though, to give us an idea of what your high-level program structure looks like now on a bit more detail. What are the main classes you are using in your system (not just the ones you've mentioned so far)? What is the main responsibility of each, and where does each live? Then our recommendations could be a little more specific.
EDIT
So, I have mocked up a quick demo of a possible alternative architecture, based on your comment.
I have the following in my project:
FormMain (Form)
TitleScreen (UserControl)
InGameMenu (UserControl)
MainScreen (UserControl)
GameController (Class)
GameModel (Class)
I didn't use Date and LoadSave, for now.
FormMain simply has an instance of each UserControl dropped on it. No special code.
GameController is a singleton (since you tried to use this pattern already and I think it would be helpful for you to try using a working version of it) that responds to user input by manipulating the model. Note well: you don't manipulate the model directly from your GUI (which is the View part of model-view-controller). It exposes an instance of GameModel and has a bunch of methods that let you perform game actions like loading/saving, ending a turn, etc.
GameModel is where all your game state is stored. In this case, that's just a Date and a turn counter (as if this were going to be a turn-based game). The date is a string (in my game world, dates are presented in the format "Eschaton 23, 3834.4"), and each turn is a day.
TitleScreen and InGameMenu each just have one button, for clarity. In theory (not implementation), TitleScreen lets you start a new game and InGameMenu lets you load an existing one.
So with the introductions out of the way, here's the code.
GameModel:
public class GameModel
{
string displayDate = "Eschaton 23, 3834.4 (default value for illustration, never actually used)";
public GameModel()
{
// Initialize to 0 and then increment immediately. This is a hack to start on turn 1 and to have the game
// date be initialized to day 1.
incrementableDayNumber = 0;
IncrementDate();
}
public void PretendToLoadAGame(string gameDate)
{
DisplayDate = gameDate;
incrementableDayNumber = 1;
}
public string DisplayDate
{
get { return displayDate; }
set
{
// set the internal value
displayDate = value;
// notify the View of the change in Date
if (DateChanged != null)
DateChanged(this, EventArgs.Empty);
}
}
public event EventHandler DateChanged;
// use similar techniques to handle other properties, like
int incrementableDayNumber;
public void IncrementDate()
{
incrementableDayNumber++;
DisplayDate = "Eschaton " + incrementableDayNumber + ", 9994.9 (from turn end)";
}
}
Things to note: your model has an event (in this case, just one of type EventHandler; you could create more expressive types of events later, but let's start simple) called DateChanged. This will be fired whenever DisplayDate changes. You can see how that happens when you look at the property definition: the set accessor (which you will NOT call from your GUI) raises the event if anyone is listening. There are also internal fields to store game state and methods which GameController (not your GUI) will call as required.
GameController looks like this:
public class GameController
{
private static GameController instance;
public static GameController Instance
{
get
{
if (instance == null)
instance = new GameController();
return instance;
}
}
private GameController()
{
Model = new GameModel();
}
public void LoadSavedGame(string file)
{
// set all the state as saved from file. Since this could involve initialization
// code that could be shared with LoadNewGame, for instance, you could move this logic
// to a method on the model. Lots of options, as usual in software development.
Model.PretendToLoadAGame("Eschaton 93, 9776.9 (Debug: LoadSavedGame)");
}
public void LoadNewGame()
{
Model.PretendToLoadAGame("Eschaton 12, 9772.3 (Debug: LoadNewGame)");
}
public void SaveGame()
{
// to do
}
// Increment the date
public void EndTurn()
{
Model.IncrementDate();
}
public GameModel Model
{
get;
private set;
}
}
At the top you see the singleton implementation. Then comes the constructor, which makes sure there's always a model around, and methods to load and save games. (In this case I don't change the instance of GameModel even when a new game is loaded. The reason is that GameModel has events and I don't want listeners to have to unwire and rewire them in this simple sample code. You can decide how you want to approach this on your own.) Notice that these methods basically implement all the high-level actions your GUI might need to perform on the game state: load or save a game, end a turn, etc.
Now the rest is easy.
TitleScreen:
public partial class TitleScreen : UserControl
{
public TitleScreen()
{
InitializeComponent();
}
private void btnLoadNew(object sender, EventArgs e)
{
GameController.Instance.LoadNewGame();
}
}
InGameMenu:
public partial class InGameMenu : UserControl
{
public InGameMenu()
{
InitializeComponent();
}
private void btnLoadSaved_Click(object sender, EventArgs e)
{
GameController.Instance.LoadSavedGame("test");
}
}
Notice how these two do nothing but call methods on the Controller. Easy.
public partial class MainScreen : UserControl
{
public MainScreen()
{
InitializeComponent();
GameController.Instance.Model.DateChanged += Model_DateChanged;
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Model_DateChanged(object sender, EventArgs e)
{
lblDate.Text = GameController.Instance.Model.DisplayDate;
}
void Instance_CurrentGameChanged(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void btnEndTurn_Click(object sender, EventArgs e)
{
GameController.Instance.EndTurn();
}
}
This is a little more involved, but not very. The key is, it wires up the DateChanged event on the model. This way it can be notified when the date is incremented. I also implemented another game function (end turn) in a button here.
If you duplicate this and run it, you'll find that the game date is manipulated from lots of places, and the label is always updated properly. Best of all, your controller and model don't actually know anything at all about the View-- not even that it's based on WinForms. You could as easily use those two classes in a Windows Phone or Mono context as anything else.
Does this clarify some of the architecture principles I and others have been trying to explain?
In essence the problem is that when the application runs, it's going to try to instantiate the main form-window. But by using the Singleton pattern, you're essentially forbidding the application from doing that.
Take a look at the sample code here:
http://msdn.microsoft.com/en-us/library/system.windows.forms.application.aspx
You'll notice in particular this section:
[STAThread]
public static void Main()
{
// Start the application.
Application.Run(new Form1());
}
Notice how the program is trying to instantiate Form1. Your code says, nah, I don't really want that since you mark the constructor as private (same holds true for static forms as well). But that's counter to how windows forms is supposed to work. If you want a singleton form-window, just don't make any more. Simple as that.
I have two classess and a Userform. I am trying not to use any Form related code in my classess but i am rather new to OOP. In the CreateGraph() method, i would like to prompt the user with a Yes/No dialog. The code in the method would continue based on the result. I have read some examples on MVP but not exactly sure how i can implement in this case.
Can someone guide me on this? I do believe there is some serious design issues in my code
//singleton class
public class MyApplication
{
private int state;
private static MyApplication instance = null;
public void Task1()
{
GraphGenerator gg = new GraphGenerator();
gg.CreateGraph();
state = 1;
}
public void Task2()
{
//some other tasks..
state = 2;
}
}
Class where i have issue..
public class GraphGenerator
{
public void CreateGraph()
{
//some code for creating a graph..
//here i want to prompt the user with a
// Yes/No dialog box..
}
}
The userform
public partial class Form1 : Form
{
private void btnTask1_Click(object sender, EventArgs e)
{
MyApplication ma = MyApplication.Instance;
ma.Task1();
}
private void btnTask1_Click(object sender, EventArgs e)
{
MyApplication ma = MyApplication.Instance;
ma.Task2();
}
}
Naming - MyApplication - is bad name for controller or presenter, use for example "form name" + "presenter" for naming.
Singleton. Controller or presenter should not be singleton. Inject it through constructor or create in ctor and than save to field or property with private setter in class. For example:
public Form1(FormPresenter presenter)
{
InitializeComponent();
this.presenter = presenter;
}
All other on this simple sample is normal. But for buttons event handlers you can use events in presenter/controller and fire in button event handler specific to presenter/controller events.
Also try to look for MVC/MVP frameworks. Look here for related question:
Implementing MVC with Windows Forms
As I remember there is Microsoft Smart Client Guidance (CAB / Microsoft Composite Application Block) for that.
First , it is best to design your classes as much as possible such that you don't need to intermingle UI code with your domain objects. Can you restructure the code so that the caller/owner of GraphGenerator decides if it needs to ask the user something instead of GraphGenerator doing it? Try to keep GraphGenerator solely focused on his task or making graphs.
Failing that, you could define events (or delegates or callback interface, but lets call these related mechanisms events for now) within GraphGenerator such that the caller/owner can listen for notifications from GraphGenerator and in turn interact with the user. For example, define an event called QueryConfirmSave to raise and the caller can handle the event and prompt the user and then pass back a boolean as an EventArg field.
(The code would be something like this (from the hip, not editor checked):
GraphGenerator gg = new GraphGenerator();
gg.QueryConfirmSave += new EventHandler<ConfirmSaveArgs>(GraphGenerator_QueryConfirmSave);
and then:
private void GraphGenerator_QueryConfirmSave(object sender, ConfirmSaveArgs args)
{
if (MessageBox.Show("Save?") == DialogResult.Yes)
{
args.SaveConfirmed = true;
}
}
You need MessageBox.Show(...).
I implemented an Undo/Redo system in my application but because it is a separate class from my main form it cannot access any of its controls. Currently I'm passing every control that needs undoing or redoing in to the class's constructor but it is quickly becoming one of the largest constructors in my project.
It'd be nice if only this class were allowed to access to my form's controls. If I pass in the form to the constructor I still cannot access the control's because they are all private. I suppose the simple solution would be to make all the controls I need to use public but what is the ideal or elegant solution?
P.S. I have already read about the technique of having a public property for everything required and passing in the form to the constructor but that will end up being quite a bit of properties and the solution does not seem very applicable in this situation.
Thanks!
Very, very, very rarely is it good practice to pass UI controls or forms to a class. This is just a maintenance/dependency nightmare in the making.
You don't want your class to know diddly about your form's controls, all it should care about is its data.
Your form knows itself, knows the data it needs to save and how to restore it to the proper controls.
If all you are looking for is undo/redo capability, you want to look into the Memento design pattern. You don't need to make a fully propertied class unless you have some other business need for it.
For the memento pattern.
A simple object holds the state you want to preserve....
internal class Memento: IMemento
{
private object _state;
public void SetState(object state)
{
_state = state;
}
public object GetState()
{
return _state;
}
}
A caretaker object manages the mementos for the form.
public class Caretaker
{
private Dictionary<int,IMemento> _mementos = new Dictionary<int,IMemento>();
public void AddMemento(int tag, IMemento memento)
{
_mementos.Add(tag, memento);
}
public IMemento GetMemento(int tag)
{
if (_mementos.ContainsKey(tag))
{
return _mementos[tag];
}
return null;
}
}
The form saves a new memento to the Caretaker after changing state. We take advantage of the extreme flexibility of the object class...
private void SaveMemento()
{
Memento memento = new Memento();
string[] state = {TextBox1.Text, TextBox2.Text};//expand as needed
memento.SetState(state);
_caretaker.AddMemento(_currentStateIndex, memento);
_currentStateIndex++;
}
The form reverts as needed.
private void RevertToPreviousState()
{
Memento memento = (Memento)_caretaker.GetMemento(--_currentStateIndex);
string[] state = (string[]) memento.GetState();
TextBox1.Text = state[0];
TextBox2.Text = state[1];//expand as needed
}
Forgot IMemento --just a tagging interface, in case you want to put the caretaker in another assembly for full Memento pattern implementation.
public interface IMemento
{
}
accessing the controls for tracking state is aint the best way, but if thats wat you want then i would suggest you access the controls which are private through reflection
http://social.msdn.microsoft.com/forums/en-US/clr/thread/ef7cc349-cf35-4b01-95b3-b71675f9b9d5/
I have a Panel I want to fill with some UserControl(s) at runtime. These controls are complex and may be interdependent, so I'd like them:
to be editable with Visual Studio designer;
to be in the same context (= defined in the same class);
Both of the requirements are a must-have.
Considering UserControl is itself an indexed collection of Control(s), I started designing my controls in the same class, without caring about real positioning of them (that will be specified runtime). I already used the same identical approach with DevComponents ribbon containers (with much satisfaction), so I initially thought the same was possible with standard UserControl(s).
I eventually realized that a Control can be inside only one Control.ControlCollection instance at a time, so I couldn't use the Controls property and add controls to another panel without removing them from the original "dummy" UserControl.
My question is: is there a clean and supported way to create this designer-aware UserControl collection? I would call this approach a pattern because it really adds code clearness and efficiency.
Thanks,
Francesco
P.S.: as a workaround, I created a DummyControlContainer class that inherits UserControl and keeps a Dictionary map filled at ControlAdded event (code follows). Wondering if there's something cleaner.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded += new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender, ControlEventArgs args)
{
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}
After testing and using it in a real world project, I'm more and more convinced that my solution was clean and safe if you need such a pattern. This container is intended to be filled with controls such as panels or similar. To prevent some bad behaviors with bindable data sources, I provided each new control added to this container with its own BindingContext. Enjoy.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded +=
new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender,
ControlEventArgs args)
{
// If the added Control doesn't provide its own BindingContext,
// set a new one
if (args.Control.BindingContext == this.BindingContext)
args.Control.BindingContext = new BindingContext();
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}