Return value from usercontrol after user action - c#

Essentially I need the same thing that Form.ShowDialog() offers, but with a UserControl.
Inside a winform, I load a UserControl, which should allow a user to select an item from a list, and return it back to the caller.
For example:
var item = myUserControl.SelectItem();
Obviously, returning from a control's method is very simple. But how can I make it wait until user performs the required action with the control?
I can subscribe to an event of the control, but this path is not ideal.
Put simply, I want a UserControl's method to return after user clicks a specific button on it.

Simply put, a UserControl is really just a custom control and just like you drop a TextBox or a ListBox on your WinFrom, you drop your UserControl on the form.
Treat your UserControl just like you would treat any other control, like TextBox or ListBox.
So, just like you get the value from a TextBox through TextBox.Text or the SelectedValue or SelectedItem from a ListBox, you would call a method from your UserControl to return the SelectedItem.
Often times when the OK button is clicked or the form is closed is when in your code you would go through each of your form's controls getting their values. Presumably, you would do some validation to make sure proper values were entered, too.
Therefore, when your form is accepted is when you would call your UserControl's method to get the selected item. You don't have to subscribe to an event to wait for that to happen. Again, just treat it like you would treat a normal ListBox.
EDIT:
Knowing now more about the nature of your question this is my answer:
Say you have a UserControl that looks like this:
In the code behind you are going to have to set up an Event to monitor when the the OK button has been clicked inside the UserControl. This event will also notify a subscriber what the choice was that the user selected in your list:
public partial class SelectFromListUserControl : UserControl
{
public class SelectedItemEventArgs : EventArgs
{
public string SelectedChoice { get; set; }
}
public event EventHandler<SelectedItemEventArgs> ItemHasBeenSelected;
public SelectFromListUserControl()
{
InitializeComponent();
}
private void btnOK_Click(object sender, EventArgs e)
{
var handler = ItemHasBeenSelected;
if (handler != null)
{
handler(this, new SelectedItemEventArgs
{ SelectedChoice = listBox1.SelectedItem.ToString() });
}
}
}
On your main form you will have code patterned similar to the following.
There should be a routine to create or make visible this special user control.
It will hook the event in the user control so that the main form will be notified.
It will draw the user control.
The event handler will retrieve the value selected in the user control and then clear the user control and/or bring up another user control.
private void ShowSelectFromListWidget()
{
var uc = new SelectFromListUserControl();
uc.ItemHasBeenSelected += uc_ItemHasBeenSelected;
MakeUserControlPrimaryWindow(uc);
}
void uc_ItemHasBeenSelected(object sender,
SelectFromListUserControl.SelectedItemEventArgs e)
{
var value = e.SelectedChoice;
ClosePrimaryUserControl();
}
private void MakeUserControlPrimaryWindow(UserControl uc)
{
// my example just puts in in a panel, but for you
// put your special code here to handle your user control management
panel1.Controls.Add(uc);
}
private void ClosePrimaryUserControl()
{
// put your special code here to handle your user control management
panel1.Controls.Clear();
}

Embed it in a form and call the form modally (ShowDialog)?

But how can I make it wait until user performs the required action with the control?
The question is more about how to wait for the user to select item and click OK button without blocking entire user interface.
The answer is simple: Async/Await feature.
private readonly SelectCompletionSource as new TaskCompletionSource(of ResultType)
public async function SelectItemAsync() as ResultType
me.Visible = true
return await SelectComplectionSource.Task
end function
public function OK() as boolean
me.Visible = false
dim Result = me.SelectedItem
SelectComplectionSource.
SetResult(Result)
end function
To get an Item one calls
dim Item = await UserControl.SelectItemAsync
UserControl is shown to the user without blocking user interface. The selection task is started but paused until the result is ready.
By clicking OK button, user invokes OK function that queries selected item and makes selection task into completed state.

Related

Restart a UserControl in Windows Forms-App (.Net Framework) C#

I have a Windows Forms App in C# with multiple UserControls.
In the UserControl1 I create panels dynamically, each panel containing multiple elements (checkbox, label listbox, picturebox, and multiple comboboxes). The values inside these elements differs between panels because I import the values from a Database. Also, I can add or remove elements from the listbox. Basically each panel is a presentation for a Pizza menu where you select the size (each size has its own price) and add or remove ingredients (from the listbox).
If you select one (or more) of the pizza's it is added in the UserControl2 (designated as a shopping cart). You can select multiple (different) pizza items from the UserControl1, and all of them will appear in the UserControl2 in the "shopping cart".
All these UserControls are contained in a panel in a Form and can be accessed by clicking a corresponding button.
My question is, how do I reload the UserControl1 from UserControl2?
Basically, after I'm done making an "order" (which can have multiple different items), I want to make a new "order" and I want the UserControl1 to look just like it was when I started the app.
I realize that I have to call the UserControl1_Load() method, but how do I do that from the UserControl2?
Or is there another method of "resetting" the UserControl1?
Obviously, I'm kind of new to C# so, please, have mercy on my soul.
Thank you very much in advance for your help!
I would implement an interface which each UserControl should implement to support this feature. The interface is a contract describing that the control has implemented a method. Just try to cast all usercontrols to that interface to support the restart functionality.
You should change it for your needs, but here is an example:
public interface ISupportInitialize
{
void Initialize();
}
public partial class MyUserControl : UserControl, ISupportInitialize
{
public MyUserControl()
{
// whatever you need to initialize
Initialize();
}
public void Initialize()
{
// remove old content if exists
// add new content
}
}
And in your main window:
public void ReInitializeControls()
{
// assume that panel1 contains the controls.
var userControlsWithInitialize = panel1.Controls.OfType<ISupportInitialize>();
foreach(var control in userControlsWithInitialize)
control.Initialize();
}
Calling the Load() method may not be sufficient. If you want a total reset behavior, you can remove the current control and add a brand new one in place of it.
Assuming you know how to implement an event handler, this goes inside the user control which triggers the reset operation, as a member variable:
public EventHandler OnOrderCompleted;
And when the order is completed, the control should invoke:
private void button1_Click(object sender, EventArgs e)
{
if (OnOrderCompleted != null)
{
OnOrderCompleted.Invoke(this, new EventArgs());
}
}
To keep coupling at minimum, this control should not directly know about the user control to be reset, but should inform any observers about the completion of an order. In our case, this can be the form hosting all the controls: (We name the triggering control orderControl here)
public Form2()
{
InitializeComponent();
orderControl.OnOrderCompleted += OnOrderCompleted;
}
private void OnOrderCompleted(object sender, EventArgs e)
{
Point currentLocation = productsControl.Location;
Controls.Remove(productsControl);
productsControl = new UserControl1();
productsControl.Location = currentLocation;
Controls.Add(productsControl);
}
Hope this helps.

How do I make a class in c# so I can click a button on 1 form to update data on a second form?

I am currently making an rpg using Winforms for a school project. However my knowledge on classes is so limited that I'm having trouble making a proper class that takes in data from 1 form, is used in the second form, then sent back to the first form.
The process I'm trying to accomplish is like this:
main form opens a second form that displays items in a listbox.
1
when you select an item and press a button to use it, the items effects are applied.
2
The data for the effect is in the first form where many other calculations are made with the same data.
3
I keep running into the problem of making a new object of a class and the data from the first form is reset. How would I go about either using an existing object from the first form, or creating a reference class maybe?
This is the function I want to run on the first form when the button on the second form is clicked.
public void SmallPot()
{
currentPHP += pHP * .25;
if (newPHP > pHP)
{
newPHP = pHP;
}
pHPBarUpdate = (int)(newPHP / pHP * 377);
pnlCurrentPHP.Width = pHPBarUpdate;
newPHP = currentPHP;
}
Expected:
When I click the use button on the popup form it closes and the items effects are displayed on the Main form.
What Happens:
Since I create a new object of form one in form two, all my variables are reset to 0 before the calculation, resulting in nothing happening after the second form closes.
I will give you a general guideline to implement a solution based on event definition and raising
Let's start from your second form where you need to communicate to the first form the event
public class Form2 : Form
{
// start creating the delegate type
public delegate void OnItemSelected(string itemName);
// declare the public event that this form will raise
public event OnItemSelected ItemSelected;
protected void cmdItemUse_Click(object sender, EventArgs e)
{
// When the user clicks to select an item....
string itemName = GetItemSelectedFromList();
// Check if someone is interested in this item selection
if(ItemSelected != null)
ItemSelected.Invoke(itemName);
}
}
Now we change something in the first Form. We need to create the second form and before displaying it we subscribe to the event exposed by the second form
public class Form1 : Form
{
... other stuff....
protected void cmdOpenSelection_Click(object sender, EventArgs e)
{
using(Form2 frm = new Form2())
{
// Subscribe the event giving it a method inside this class
// that doesn't return anything and receives a string
// as required by the delegate type of the event
frm.ItemSelected += handleItemSelection;
frm.ShowDialog(); // frm.Show();
}
}
private void handlerItemSelection(string itemName)
{
// This method is a custom Event handler and inside Form1
// will be called by Form2 through the Invoke on the event variable
}
}
In the example above I choose to pass a simple string, but of course you could pass anything including a reference type like an instance of a class containing all the info
required by the Item selection.

check whether a button in another window is clicked

I want to open a window if a button is clicked, and that button is located in another window.
So how to check whether a button in another window is clicked or not?
Now I am coding in a class called 'RightButton.cs'
I want to open a window called 'PopUp' when 'Add' button in 'Reason' window is clicked.
PaidOutReason paid = new PaidOutReason(trnprt, apiParameters);
paid.ShowDialog();
if (paid.btnSave.ClickMode == new ClickMode())
{
PopUpBanks popu = new PopUpBanks(this);
popu.Show();
}
This one was working perfectly, but I had to remove ShowDialog() and replace it with Show(). Then it was not working.
This is for a POS system. It has a user Control called 'Keyboard'. When the 'Reason' window is opening this Keyboard also want to be opened. Therefore I had to replace ShowDialog() with Show().
I'd add an event to the window, and bind an event handler to it.
class WndWindow{
BtnPaid_Click(object sender, EventArgs e){
using(var paid = new PaidOutReason()){
paid.BtnAddClick += Paid_BtnAddClick;
paid.ShowDialog();
paid.BtnAddClick -= Paid_BtnAddClick;
}
}
Paid_BtnAddClick(object sender, EventArgs e){
var popu = new PopUpBanks();
popu.Show();
}
}
class PaidOutReason{
public event EventHandler BtnAddClick;
BtnAdd_Click(object sender, EventArgs e){
//Do standard event handler code
BtnAddClick?.Invoke(this, e);
}
}
If there's any sort of checks you need to perform you can do that before reading the event, and simply return if checks fail.
You can use static controlls in your app. Start with declaring static window object in App.xaml.cs, for example
public static PaidOutReason paidOutWindow;
then, in App constructor method, after InitializingComponent(), initialize static window class:
paidOutWindow = new PaidOutReason();
You may wonder what it gives to you. Since it's POS application, you are likely to use the same set of windows quite often and repeatedly, means you can hold window object in memory and refer to it (and change, when needed). Also, after doing such thing, you will have access to all structures inside PaidOutReason object, by typing
App.paidOutWindow.FunctionName();
and finally, you should have access to all window functions such as ShowDialog().
If you are using MVVM pattern, then you can use command binding for showing the PopupBanks window.
For example:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand AddCommand { get; set; }
public ViewModel()
{
AddCommand = new RelayCommand(AddCommandHandler);
}
private void AddCommandHandler()
{
IPopUpBanks popu = new PopUpBanks(this);
popu.Show();
}
}
PaidOutReason view:
1. <Button x:Name="Add" Command={Binding AddCommand}/>
2. Set the above viewmodel as datacontext of PaidOutReason view.
Create a interface IPopUpBanks containing Show() method and implement this interface on PopUpBanks view.
Handle the Click event of btnSave:
PaidOutReason paid = new PaidOutReason(trnprt, apiParameters);
paid.btnSave.Click += (ss, ee) =>
{
PopUpBanks popu = new PopUpBanks(this);
popu.Show();
};
paid.Show();
Instead of the Keyboard Window, I made it a user control and then initialized an event in there. Then I insert that Keyboard User Control to the PaidOutReason Window and then called the event. Then I was able to use ShowDialog() to call the window.

C# Passing control reference to another form

I have a button and hidden textbox on my main form. When I press the button it will hide the main form and show the second form. When I press the button on the second form I want to show the main form again but this time I need to show the hidden textbox. I know how to pass the text from a textbox to another form but not just the reference of the textbox.
You better pass the complete main form instance for the second form, and create a public function to set the textbox to visible, or create a property around it.
Something like:
//form1
Form2 second = new Form2(this);
}....
public void ShowTextBox()
{
textbox1.Visible=true;
}
//form2
Form parent;
public Form2(Form _parent)
{
parent=_parent;
}
///later
parent.Show();
parent.ShowTextBox();
Sounds to me like a custom event would be a better approach. Have the secondary form expose an event, which is raised at whatever appropriate time (your button press). In your main form, when you create your instance of your second form, subscribe to that event. Then run your "unhide" code from within the mainform's event subscription.
This keeps the coupling down on the two forms and results in much more easily maintainable and extensible code (for best effect, use interfaces, but events are a good middle ground for learning).
Something like this:
(it's been a long time since I worked with winforms, or events even, so if this needs refining let me know)
// your secondary/popup form's class
public partial class Form2 : Form
{
// add a custom event
public EventHandler<EventArgs> MyCustomEvent;
// link up your button click event
void InitializeComponent() {
myButton.Click += myButtonClick;
}
// when your button is clicked, raise your custom event
void myButtonClick(object sender, EventArgs, e) {
onMyCustomEvent();
}
// this "broadcasts" the event
void onMyCustomEvent() {
EventHandler<EventArgs> h = MyCustomEvent;
if (h != null) {
h(this, new EventArgs());
}
}
}
// your main form's class
public partial class MainForm
{
void InitializeComponent() {
// ...
}
void showForm2() {
var form2 = new Form2();
form2.MyCustomEvent += form2CustomEvent;
form2.Show();
}
void form2CustomEvent(object sender, EventArgs e) {
myHiddenTextBox.Visible = true;
}
}
All in all this is a much better approach in terms of code architecture. Now the popup doesn't care who opens it (it has no reference to the main form), and the custom event (which is really what you're after) can be managed to any level of control you need, without interfering how other thing work (for example, perhaps later you may want to have a different action that fires this same custom event...)
Food for thought.

Show/Refresh Button working properly

I have a checkbox in form1, when it is checked it makes a PictureBox in form2 visible but when I uncheck I want to refresh form2 so that the PictureBox is not visible. This code is in form1. It is a button that opens up the form if one if not open but if a form is open it refreshes it. The problem is that it is not refreshing. Can anyone tell me what is wrong?
private tuesday _FavoritesForm;
public void startbutton_Click(object sender, EventArgs e)
{
if (_FavoritesForm == null)
{
_FavoritesForm = new tuesday();
_FavoritesForm.Closed += new EventHandler(_FavoritesForm_Closed);
_FavoritesForm.Show();
}
else
{
_FavoritesForm.Refresh();
}
}
Calling Refresh on a form merely forces it to be repainted. There isn't any reason to assume that it will repaint differently. You would have to override the OnPaint() method in that form. Clearly you are not using OnPaint to draw an image, you are using a PictureBox. Setting that control's Visible property to false will make the image disappear, no additional help is needed.
I would add a public method on the secondary form to Show/Hide the picture because it appears the second form has no idea of the first form. Then the click / checkbox setting on the first form to instead of doing a "REFRESH" on the second, create the form if its not already done so. Once created, call whatever method you expose on the secondary form to specifically make visible or not as needed.
EDIT FOR CLARIFICATION
#a13xy, actually the reverse... The second form has no idea of the first, but yes, have a method that is public on the 2nd. Then on the FIRST form, in the click / value changed event of your checkbox, you just call the method from that... such as your sample code...
public void startbutton_Click(object sender, EventArgs e)
{
if (_FavoritesForm == null)
{ _FavoritesForm = new tuesday();
_FavoritesForm.Closed += new EventHandler(_FavoritesForm_Closed);
_FavoritesForm.Show();
}
else
{ _FavoritesForm.Refresh();
}
_FavoritesForm.ShowHide( IsCurrentForms.CheckBox.IsCheckedValue );
}
Not positive of your checkbox controls name, or its Checked value property, just call the second form's method directly with whatever your forms value is and the method in the SECOND form could be something like...
public void ShowHide( Boolean ShowTheImage )
{
// value provided as a direct parameter from the first form,
// THISform knows about its own Picture property and can directly
// set the visibility within its scoped control.
this.YourPicture.Visible = ShowTheImage
}

Categories

Resources