Popout a user control into a new window - c#

I want to make it so that some of my user controls have the ability to 'pop out' into a new window. How I see it working is that the user control will remain where it currently it, but will send a copy of its current state into a new window. I also want this functionality to be in a base class so that derived classes will have this functionality.
Here is what I have so far:
public class PopoutControl : XtraUserControl
{
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this);
Dock = DockStyle.Fill;
PopoutForm.Show();
}
}
public partial class PopoutControlTest : PopoutControl
{
public PopoutControlTest()
{
InitializeComponent();
}
private void OnPopoutRequest(object sender, EventArgs e)
{
Popout();
}
}
This works except that it removes the user control from the original form where it is located - in order to place it on the new form - how can I solve this?
William

You should make a copy of the control instead of passing a reference, for example, by implementing some "Clone" method:
public class PopoutControl : XtraUserControl
{
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this.Clone());
Dock = DockStyle.Fill;
PopoutForm.Show();
}
public PopoutControl Clone()
{
var p = new PopoutControl();
// implement copying of the current state to p here
// ...
return p;
}
}
EDIT: For a general approach to clone or serialize Windows Forms controls, read this article:
http://www.codeproject.com/Articles/12976/How-to-Clone-Serialize-Copy-Paste-a-Windows-Forms

Your PopOut() has to be changed. Create a clone of the 'this'. Add the cloned object into the new form created. Implement the ICloneable interface in your PopOutControl class. Your clone() method has to be implemented such that it has the same state of your 'PopOutControl' object, ie 'this'.
public void Popout()
{
XtraForm PopoutForm = new XtraForm();
PopoutForm.Controls.Add(this.Clone());
Dock = DockStyle.Fill;
PopoutForm.Show();
}

Related

set child winform properties from a parent form when using simple injector c#

I have a form1 (not mdi) which displays dialog on button click event, dialog basically is a pop up form which shows data on datagridview control.
I am using simple injector.
PopUpForm has a property called LocationData which is a datatable. I need to set that property in form1 (parent) so that data can be displayed on the PopUpForm when it is displayed on the screen.
Sorry, i am new to simple injector and still learning, any help or guidence would be appreciated. I even don't know if i am doing in a right way.
form1
On button click event
this._formOpener.ShowModalForm<PopUpForm>();
PopUpForm
public partial class PopUpForm : Form
{
public DataTable LocationData { get; set; }
public PopUpForm()
{
InitializeComponent();
}
private void PopUpForm_Load(object sender, EventArgs e)
{
dgvNearestLocations.DataSource = LocationData;
}
}
program class
static class Program
{
private static Container container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Bootstrap();
Application.Run(container.GetInstance<Form1>());
}
private static void Bootstrap()
{
// Create the container as usual.
container = new Container();
// Register your types, for instance:
container.RegisterSingleton<IFormOpener, FormOpener>();
container.Register<Form1>(Lifestyle.Singleton); ;
container.Register<PopUpForm>(Lifestyle.Singleton); ;
// Optionally verify the container.
container.Verify();
}
}
FormOpener
public class FormOpener : IFormOpener
{
private readonly Container container;
private readonly Dictionary<Type, Form> openedForms;
public FormOpener(Container container)
{
this.container = container;
this.openedForms = new Dictionary<Type, Form>();
}
public DialogResult ShowModalForm<TForm>() where TForm : Form
{
using (var form = this.GetForm<TForm>())
{
return form.ShowDialog();
}
}
private Form GetForm<TForm>() where TForm : Form
{
return this.container.GetInstance<TForm>();
}
}
First of all, you copied the FormOpener probably from this answer. But you missed the part about Forms needing to be transient. Don't register your forms as Singleton. Especially because you dispose them, this will work one and exactly one time. The next time you would want to show a Form you will get an ObjectDisposedException.
When you register the Forms as Transient Simple Injector will tell you that the forms implement IDisposable and this is (of course) correct. But because you take care of disposing in the FormOpener you can safely suppress this warning. Register your forms like this:
private static void RegisterWindowsForms(
this Container container, IEnumerable<Assembly> assemblies)
{
var formTypes =
from assembly in assemblies
from type in assembly.GetTypes()
where type.IsSubclassOf(typeof(Form))
where !type.IsAbstract
select type;
foreach (var type in formTypes)
{
var registration = Lifestyle.Transient.CreateRegistration(type, container);
registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent,
"Forms are disposed by application code. Letting Simple Injector do this " +
"is problematic because that would need a scope, which is impossible to do.");
container.AddRegistration(type, registration);
}
}
To come to your question:
What you need is some extra infrastructure to initialize the Form.
By letting your forms implement an interface IFormInit<T> you can pass data to the form and directly show it.
public interface IFormInit<T> : IDisposable
{
DialogResult InitAndShowForm(T data);
}
To let Simple Injector create the forms based on this interface we need to register them in the container. We can let Simple Injector search for all closed implementations by supplying a list of assemblies, like this:
container.Register(typeof(IFormInit<>), assemblies, Lifestyle.Transient);
Notice that Simple Injector will automatically merge these registrations with the ones from RegisterWindowsForms. So you can now get an instance of Form by calling:
container.GetInstance<PopupForm>();
or
container.GetInstance<IFormInit<SomeDataClass>>();
You can now add this code to your FormOpener class:
public DialogResult ShowModalForm<TData>(TData data)
{
Type formType = typeof(IFormInit<>).MakeGenericType(typeof(TData));
dynamic initForm = this.container.GetInstance(formType);
DialogResult result = (DialogResult) initForm.InitAndShowForm(data);
initForm.Dispose();
return result;
}
This will get the Form from the container based on the IFormInit<T> type that it implements. When you get the form, you call the function on the interface instead of directly call Form.ShowDialog(). When the form is closed you dispose of the Form.
Note: The use of dynamic typing maybe needs clarification. Why it is needed is inspired by the QueryHandler pattern described here.
Usage is as follows:
// Add a specific class to pass to the form
public class LocationDataWrapper
{
public DataTable LocationData { get; set; }
}
public partial class PopUpForm : Form, IFormInit<LocationDataWrapper>
{
public PopUpForm() => InitializeComponent();
// Implement the interface, the loaded event can be removed
public DialogResult InitAndShowForm(LocationDataWrapper data)
{
dgvNearestLocations.DataSource = data.LocationData;
return this.ShowDialog();
}
}
On button click event
DialogResult result = this._formOpener.ShowModalForm(new LocationDataWrapper
{
LocationData = locationDataTable,
});
You can create wrapper or data classes for each form and it will automatically show the correct form, when you let this Form implement IFormInit<ThisSpecificDataClass>.

Accessing object created in different Window in WPF application

I have created one window and declared 2 instances of my object, then I modified them and wanted to pass to another Window. My questions are:
How would i do that ?
(I can pass simple types such as string or int trough window constructor but passing my own object giving me an error (Inconsistent Accessibility parameter order is less accessible then method))
Does it have any connection with dataContext ?
Can anybody explain to me how I can achieve that (in the simplest possible way)? What are the correct ways to do that ?
Here is part of my code (everything is in one namespace):
public partial class Main_window : Window
{
Order myOrder = new Order();
Menu menu = new Menu();
public Main_window()
{ InitializeComponent() }
private void OpenSecondWindow(object sender, RoutedEventArgs e)
{
Second_Window SecondWindow = new Second_Window();
Second.ShowDialog();
}
}
// Second Window class
public partial class Second_Window : Window
{
public Second_Window(Order someOrder)
{ InitializeComponent(); }
}
Make sure that the Order type, and any other type you intend to inject the SecondWindow with, is defined as a public class:
public class Order { ... }
A non-public type cannot be part of the signature of a public method or constructor.

C# Creating a global object of a class for use on multiple forms

I want to create a multiple objects of a class, edit them on one form and then use those objects on multiple forms. Hopefully the code below will try and highlight the problem.
I created a class as such...
public class Player
{
public string name;
public int goals;
}
On Form1 I have created tom and dan, and added buttons that will increase their goal count when pressed.
public partial class Form1 : Form
{
Player tom = new Player()
{
name = "Tom",
goals = 5
};
Player dan = new Player()
{
name = "Dan",
goals = 7
};
public void UpdateForm()
{
label1.Text = tom.name;
label2.Text = dan.name;
}
public Form1()
{
InitializeComponent();
UpdateForm();
}
private void button1_Click(object sender, EventArgs e)
{
tom.goals += 1;
}
private void button2_Click(object sender, EventArgs e)
{
dan.goals += 1;
}
}
I then want to be able to display their names and the new number of goals on form2. The problem I have is that those objects don't exist in that context.
{
public partial class Form2 : Form
{
public void UpdateForm2()
{
label1.Text = tom.name;
label2.Text = tom.goals;
label3.Text = dan.name;
label4.Text = dan.goals;
}
public Form2()
{
InitializeComponent();
UpdateForm2();
}
}
}
How do I make those objects global and editable between forms? I've tried to find an answer but haven't found one that quite matches this use case. I would appreciate any help and advice.
Your Form1 class is like any other class. Therefore, you can create 2 properties in it:
public partial class Form1 : Form
{
public Player Tom { get; private set; }
public Player Dan { get; private set; }
this.Tom = new Player()
{
name = "Tom",
goals = 5
};
this.Dan = new Player()
{
name = "Dan",
goals = 7
};
// The rest of your code
}
In Form2 introduce a property:
public partial class Form2 : Form
{
public Form1 CallingForm {get; set;}
public Form2()
{
InitializeComponent();
UpdateForm2();
}
}
Set that property before you show the second form like this (put this code in your first form):
Form2 f2 = new Form2();
f2.CallingForm = this;
Then in the second form you can access the players like this:
label1.Text = CallingForm.Tom.name;
If you have more players then create a List<Player> property instead.
Some other notes
Try following the .NET naming conventions and instead of public fields, use properties. Properties can be used for databinding and encapsulation, validation and has other benefits as well.
public class Player
{
private int goals;
public string Name {get; set;}
public int Goals
{
get { return this.goals; }
set
{
if (value < 0)
{
throw new ArgumentException("Goals cannot be less than 0.");
}
this.goals = value;
}
}
}
You need to know the concept of namespace. If you create an object in namespace of one form, you cannot use that particular object without referring that particular form.
So far what I can see, you've created two instances of object player in form namespace. But that's not there in form2 namespace. Hence you're getting that error.
You can use many methods commonly used to update such as
calling child window's method to update the values.
using mediator
adding an event listener to form2 on value change.
Using a global instance of an object instance or a static object is not a best practice.
You can pass the list of players in the constructor of the second form.
Yes , must transport data new form. Meybe use
send parameter to Form2 contructor parameter or use form2 Set Method.
For Example :
private readonly Player _tom;
private readonly Player _dan;
public Form2(player tom, player dan) // or use List<Player> parameter
{
InitializeComponent();
_tom = tom;
_dan = dan;
}
another way
// Form2.cs
private Player _tom;
private Player _dan;
public void SetPalyers(Player tom, Player dan)
{
_tom = tom;
_dan = dan;
}
Think about encapsulation. Your Form2 cannot access the instantiated class objects because they are contained within Form1.
When you write the following:
label1.Text = tom.name;
The compiler is looking for the object Form2.tom, but it doesn't exist.
You have to tell it where to look, like so:
label1.Text = Form1.tom.name;
To make these objects available to all chosen classes without using the Form1 prefix, the chosen classes would have to be subclasses of Form1. Other than that, you'd have to refer back to them in the way I explained previously.
It seems like you need a service class that holds a List<Person>. Then just have your two forms share the same instance of that service. Or the lazy option is to make the service a static class.
Update
You could also consider implementing the Observer pattern in the service.
http://www.dofactory.com/net/observer-design-pattern

User Control as container at design time

I'm designing a simple expander control.
I've derived from UserControl, drawn inner controls, built, run; all ok.
Since an inner Control is a Panel, I'd like to use it as container at design time. Indeed I've used the attributes:
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
Great I say. But it isn't...
The result is that I can use it as container at design time but:
The added controls go back the inner controls already embedded in the user control
Even if I push to top a control added at design time, at runtime it is back again on controls embedded to the user control
I cannot restrict the container area at design time into a Panel area
What am I missing? Here is the code for completeness... why this snippet of code is not working?
[Designer(typeof(ExpanderControlDesigner))]
[Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design", typeof(IDesigner))]
public partial class ExpanderControl : UserControl
{
public ExpanderControl()
{
InitializeComponent();
....
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
internal class ExpanderControlDesigner : ControlDesigner
{
private ExpanderControl MyControl;
public override void Initialize(IComponent component)
{
base.Initialize(component);
MyControl = (ExpanderControl)component;
// Hook up events
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
s.SelectionChanged += new EventHandler(OnSelectionChanged);
c.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
}
private void OnSelectionChanged(object sender, System.EventArgs e)
{
}
private void OnComponentRemoving(object sender, ComponentEventArgs e)
{
}
protected override void Dispose(bool disposing)
{
ISelectionService s = (ISelectionService)GetService(typeof(ISelectionService));
IComponentChangeService c = (IComponentChangeService)GetService(typeof(IComponentChangeService));
// Unhook events
s.SelectionChanged -= new EventHandler(OnSelectionChanged);
c.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving);
base.Dispose(disposing);
}
public override System.ComponentModel.Design.DesignerVerbCollection Verbs
{
get
{
DesignerVerbCollection v = new DesignerVerbCollection();
v.Add(new DesignerVerb("&asd", new EventHandler(null)));
return v;
}
}
}
I've found many resources (Interaction, designed, limited area), but nothing was usefull for being operative...
Actually there is a trick, since System.Windows.Forms classes can be designed (as usual) and have a correct behavior at runtime (TabControl, for example).
ParentControlDesigner doesn't know what you want do. It only knows you want your UserControl to be a container.
What you need to do is implement your own designer which enables design mode on the panel:
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MyCtrlLib
{
// specify my custom designer
[Designer(typeof(MyCtrlLib.UserControlDesigner))]
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
// define a property called "DropZone"
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel DropZone
{
get { return panel1; }
}
}
// my designer
public class UserControlDesigner : ParentControlDesigner
{
public override void Initialize(System.ComponentModel.IComponent component)
{
base.Initialize(component);
if (this.Control is UserControl1)
{
this.EnableDesignMode(
(UserControl1)this.Control).DropZone, "DropZone");
}
}
}
}
I learned this from Henry Minute on CodeProject. See the link for some improvements on the technique.
In addition to the answer above. It is mentioned in the comments, that the user is able to drag the WorkingArea. My fix for that is to include the WorkingArea panel in another panel, setting it to Dock.Fill. To disallow the user to change it back, I have created a class ContentPanel that overrides and hides the Dock property:
class ContentPanel : Panel
{
[Browsable(false)]
public override DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = DockStyle.Fill; }
}
}
For me, this makes it sufficiently safe. We are only using the control internally, so we mainly want to prevent developers from accidently dragging things around. There are certainly ways to mess it up anyway.
To prevent the working area from being moved/resized in the designer you have to create a class for that working area that hides the Location, Height, Width, Size properties from the designer:
public class WorkingArea : Panel
{
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Point Location
{
get
{
return base.Location;
}
set
{
base.Location = value;
}
}
...
}

Access to creator object

I have a simple windows application in C# with 3 forms.
first form is main form (its name is FrmMain), second is FrmData and third is FrmShow.
In main form (FrmMain) I have created an instance from second form (FrmData) and show it :
public partial class FrmMain : Form
{
public Form FrmModifyData; //for FrmData
int PersonCode;
public FrmMain()
{
InitializeComponent();
}
private void btnShowDataForm_Click(object sender, EventArgs e)
{
FrmModifyData= new FrmData();
FrmModifyData.ShowDialog();
}
}
but I can't access from FrmModifyData to FrmMain fields like PersonCode .
How can I access to creator object's field?
Note: I'm a beginner.
thanks.
You would need to add a property to your FrmModifyData class to take an instance of the FrmMain class. Then you can do this:
FrmModifyData = new FrmData();
FrmModifyData.ParentData = this;
FrmModifyData.ShowDialog();
Then inside FrmModifyData you would have access to the public members of FrmMain. Obviously this is kind of quick and dirty and not very reusable so i would suggest adding more explicit properties to FrmModifyData with only the data you need to use.
If you want to access PersonCode field, you should declare it as public. No visibility modifier will make it private, hence not accesible from other casses.
I would make it something like this.
With this way you're able to use the FrmModifyData in other forms.
I know it's an old post, but yes, you did read it :)
public partial class FrmMain : Form
{
// public Form FrmModifyData; <-- do not declare it in your FrmMain
// (is't a modal dialog, so you won't get more instances)
public int PersonCode {get; set;}
public FrmMain()
{
InitializeComponent();
}
private void btnShowDataForm_Click(object sender, EventArgs e)
{
FrmData FrmModifyData = new FrmData();
FrmModifyData.PersonCode = this.PersonCode;
DialogResult result = FrmModifyData.ShowDialog();
if(result == DialogResult.Ok)
{
// do something with the result
this.PersonCode = FrmModifyData.PersonCode;
}
}
}

Categories

Resources