I have a windows form application and I have a requirement to allow user to add as many email IDs as he can on the form
I want an ability to add textbox control for email ID field on button click. Textbox should be added dynamically and N number of textboxes can be added on the form.
At the same time at any given point user should be able to remove textbox added. On click of 'Save' button all the 'n' emails need to be stored in DB.
Can anyone please advise how to do it in Windows form application?
You should probably create a custom user control to hold your email addresses and remove button. This way you can press the remove button to delete that email address/control.
Then you'll want your main form to have a button to add email addresses (an instance of your custom control) and then a FlowLayoutPanel to hold your customer controls.
Your custom control code should look something like:
public class EmailAddressControl : UserControl
{
public string EmailAddress
{
get
{
return(tbEmailTextBox.Text);
}
set
{
tbEmailTextBox.Text = value;
}
}
public EmailAddressControl()
{
InitializeComponent();
}
public event EventHandler<EventArgs> RemoveEmailAddress;
public void btnRemoveEmailAddress_Click(object sender, EventArgs e)
{
RemoveEmailAddress?.Invoke(this, EventArgs.Empty);
}
}
We have two sub-controls on the UserControl, tbEmailTextBox which is a textbox and btnRemoveEmailAddress which is a button. tbEmailTextBox will hold the email address your user types in. You can access it via the EmailAddress property from outside the control. btnRemoveEmailAddress will fire the RemoveEmailAddress event which will tell its subscribers that it should be closed.
As I said before your main form should have a FlowLayoutPanel and a couple of buttons. A button we'll call btnAddEmail will add email user controls to the screen and the panel we'll call flpEmailAddresses and the second button we'll call btnSaveEmails which will save your emails to the database (the buttons should not be children of the flow layout panel - mainly for looks).
Your main code form would look like:
public class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public void btnAddEmail_Click(object sender, EventArgs e)
{
EmailAddressControl email = new EmailAddressControl();
email.RemoveEmailAddress += RemoveEmailAddress;
flpEmailAddresses.Controls.Add(email);
}
public void RemoveEmailAddress(object sender, EventArgs e)
{
EmailAddressControl email = sender as EmailAddressControl;
email.RemoveEmailAddress -= RemoveEmailAddress;
flpEmailAddresses.Controls.Remove(email);
email.Dispose();
email = null;
}
public void btnSaveEmails_Click(object sender, EventArgs e)
{
foreach(EmailAddressControl email in flpEmailAddresses.OfType<EmailAddressControl>())
{
databaseinstance.SaveEmail(email.EmailAddress);
}
}
}
In the btnAddEmail_Click we are creating a new instance of your EmailAddressControl and subscribing to the RemoveEmailAddress event so the main form knows when to remove it. We then add it to the flpEMailAddresses panel.
When your user presses the btnRemoveEmailAddress it fures the RemoveEmailAddress event which is captured by the main form, in the RemoveEmailAddress event handler. In this handler we cast the sender object to EmailAddressControl unsubscribe from the RemoveEmailAddress event (to avoid memory leaks and to allow the control to be garbage collected) then we remove the control from flpEmailAddresses panel and finally dispose of the EmailAddressControl.
If the user clicks the btnSaveEmails button then the application loops over the EmailAddressControls that are children of flpEmailAddresses and retrieves the EmailAddress from each one and sends that to databaseinstance.SaveEmail. I'll leave it to you to implement databaseinstance.SaveEmail(string emailaddress) for yourself.
Related
I've got a usercontrol (MyUC) that is programatically added to a page (MainPage) several times.
In MyUC I set the DataContext to a view model like this:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new MyUCViewModel();
}
In my view model (MyUCViewModel) I have a collection of data items:
public MyDataItemCollection MyDataItems { get; private set; }
And in the constructor I have:
public MyUCViewModel()
{
this.MyDataItems = new MyDataItemCollection();
this.MyDataItems.ChosenItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ChosenItemsChanged);
this.MyDataItems.Add(new DataItem());
}
From the above MyDataItems has another collection in it for ChosenItems and I added a NotifyCollectionChangedEventHandler to it.
Other parts of my code add and remove from the ChosenItems collection and that part seems to work ok.
Next I have the event handler method in the view model:
private void ChosenItemsChanged(object sender, EventArgs e)
{
MessageBox.Show("Chosen Items Changed");
}
This also works and I get a messagebox everytime the user makes a change in the UI that affects the ChosenItems collection.
The part I'm trying to figure out now is how do I set it up so that my MainPage does something when the ChosenItemsChanged event fires in my user controls. What I want to do is have the MainPage loop through the generated MyUC controls on the page and make each usercontrol call a method.
You can add more event listeners in the MainPage like this:
MyUCViewModel viewModel = myUC.DataContext;
viewModel.MyDataItems.ChosenItems.CollectionChanged
+= new NotifyCollectionChangedEventHandler(MainPage_ChosenItemsChanged);
This is based on the comment as the question was a little misleading:
While not strictly MVVM, as your question appears to be, your should write your User Controls as if it was a third-party control and simply expose a custom event on it. User Controls should always be a black-box with a public interface. For a reusable control that is self-contained (as many are) MVVM is overkill.
e.g.
in your User Control add:
public event EventHandler<MyEventArgs> MyEvent
Create a MyEventArgs class deriving from EventArgs and get it to hold useful parameters (like the selected item).
In your main page add a handler to MyEvent on each User Control you dynamically add.
I actually think the MVVM model is flawed and all this sort of controlling logic and event handlers belong in a Controller class (MVCVM!), but that's another story. :)
Hi I have a silverlight MVVM application using MVVM light.
When I open the application a child window should popup and upon specifying the condition in the child window and clicking OK button the main window should display the details.
public MainPage()
{
ChildPage cp = new ChildPage();
cp.Show();
InitializeComponent();
}
upon hitting OK button on the child window this window should disappear and display a list of objects on the main window. In the View Model of the child window I have a RelayCommand OKCommand.
private void WireCommands()
{
OKCommand = new RelayCommand(GetEmployees);
}
private void GetEmployees()
{
IEnumerable<Employees> employees;
employees = from employee in Employees where employee.Name == selectedEmployee.Name select employee;
Employees= new ObservableCollection<Employee>(employees);
}
The Employees has the required result. But I dont know how to close the chils window and move the result to the parent window. Thanks in advance.
You can use (in increasing order of decoupling):
As you have a reference to the ChildPage in MainPage, you can access its properties.
Use standard .NET events, where the event is on the child page, and the subscribing is done in the MainPage
Use an event aggregator pattern. Several MVVM frameworks implement the event aggregator pattern.
Using .NET Events
ChildPage cp = new ChildPage();
cp.NameReceived += NameReceived;
cp.Show();
private void NameRecieved(object sender, NameReceivedEventArgs eventArgs)
{
// retrieve employees using eventargs.Name
}
Using Event Aggregator from Caliburn.Micro
public class MainPage : Screen, IHandle<NameReceivedMessage>
{
public MainPage(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(NameReceivedMessage message)
{
// retrieve employees using message.Name which is the inputted name
}
}
Here we are doing the employee retrieval in the MainPage, after receiving the name from the ChildPage. Alternatively, you could retrieve the employees in the ChildPage, and pass them in the event args/message.
To close the child window, you can either use the Close() Method of the ChildWindow or you can set the DialogResult property to true or false which also close it.
You have to do it in the code-behind of ChildPage on the OnClick event of the OK Button.
To access the Employees property of the ChildPage's ViewModel you can do something like that :
public MainPage()
{
ChildPage cp = new ChildPage();
cp.Closed += (s,e) =>
{
//Do something with (cp.DataContext as ChildPageViewModel).Employees
}
cp.Show();
InitializeComponent();
}
I have a button inside my UserControl. I have three instances of this UserControl on the same page.
How can I expose the click event of the button inside such that I can assign different events for each instance of my UserControl.
I think this is similar to concept behind exposing DependencyProperty but I don't understand how to do it for events.
Thanks.
I normally add an event of the same name (and same parameters) to the user control and subscribe to the child control's original event, so I can pass the event on:
public partial class ClickEventControl : UserControl
{
public event EventHandler<RoutedEventArgs> Click;
public ClickEventControl()
{
InitializeComponent();
}
private void aButton_Click(object sender, RoutedEventArgs e)
{
if (Click != null)
{
Click(sender, e);
}
}
}
I would also be interested if there is a more general way of doing it.
I have a UserControl in my WPF application.
I want to call a click event and do some things when the user clicked the UserControl.
The problem is- the UserControl doesn't have a click event.
I searched on the web and found that you can use the MouseLeftButtonUp event.
I tried it- but it doesn't respond to my clicks.
You didn't write what you are trying to do but if you need a click event maybe you are writing some kind of button (the Button class is actually "something you can click" with the visual representation in a control template you can replace)
If you need a button with complex content inside - put your user control inside a button
If you need a button that doesn't look like a button write a custom control template for button
If you need a button with extra functionality subclass button, add the extra data/behavior in code and put the display XAML in a style.
I think for your needs PreviewMouseLeftButtonUp(Down) event is more suitable. Then you need to handle ClickCount for counting amount of clicks and then raise your own event, on which other controls will know, that your control is clicked. There are much more methods on handling click event. You should look at this msdn article and this
UPDATE to handle both Click and DoubleClick
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_myCustomUserControl.MouseLeftButtonUp += new MouseButtonEventHandler(_myCustomUserControl_MouseLeftButtonUp);
_myCustomUserControl.MouseDoubleClick += new MouseButtonEventHandler(_myCustomUserControl_MouseDoubleClick);
}
bool _doubleClicked;
void _myCustomUserControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_textBlock.Text = "Mouse left button clicked twice";
_doubleClicked = true;
e.Handled = true;
}
void _myCustomUserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_doubleClicked)
{
_doubleClicked = false;
return;
}
_textBlock.Text = "Mouse left button clicked once";
e.Handled = true;
}
}
To test this example name your control as _myCustomUserControl and add a TextBlock named _textBlock to your MainWindow.xaml
Why not just use MouseDown?
Put the event in the User Control and simply do this:
private void MyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
MessageBox.Show("Clicked!");
}
}
I currently have a Form with a TabControl containing some TabPages. Each TabPage has several controls with validation logic and appropriate ErrorProviders. On my OK_Button_Clicked event, I call Form.ValidateChildren() in order to determine whether to save and close the form . Now, suppose I have a control in tab 1 that fails validation, but the currently visible tab is tab 2. When the user presses OK, he would get no visual indication as to why the form is not closing. I would like to be able to automatically switch to a tab where validation failed, so the user would see the ErrorProvider's indication of error.
One approach would be subscribing to the Validated and validating events of all appropriate controls, and knowing which tab each of them is in, whenever one fails validation, a list of tabs that failed validation could be built. Since no ValidationFailed event is generated as far as I know, this could be cumbersome (e.g. defining a boolean for each control, setting it to false before validation and to true on its Validated event). And even if I had such an event, I would be forced to listen to many validation events, one for each control that might fail validation, and maintain the list of unvalidated tabs in code. I should note here that subscribing directly to the TabPage's validation events doesn't work, because they pass as validated even if controls contained inside them fail validation.
Another approach could leverage the fact that the controls in my TabPage happen to be custom controls. I could then make them implement an interface such as:
interface ILastValidationInfoProvider
{
public bool LastValidationSuccessful {get; set;}
}
For example:
public MyControl : UserControl, ILastValidationInfoProvider
{
MyControl_Validing(object sender, object sender, CancelEventArgs e)
{
if (this.PassesValidation())
this.ErrorProvider.SetError(sender, null);
LastValidationSuccessful = true;
else
e.Cancel = true;
this.ErrorProvider.SetError("Validation failed!", null);
LastValidationSuccessful = false;
}
}
And then, after the call to ValidateChildren I could use code such as:
public void OK_Button_Click
{
if (form.ValidateChildren())
this.Close()
else
foreach (TabPage tab in this.TabControl)
foreach (Control control in tab.Controls)
{
ValidationInfo = control as ILastValidationInfoProvider
if (ValidationInfo != null && !ValidationInfo.LastValidationSuccessful)
{
this.TabControl.SelectTab(tab);
return;
}
}
}
I like this approach better but it doesn't cater to cases where the controls being validated are not custom.
I would gladly use a better approach. Any ideas?
EDIT I am using Form.AutoValidate = EnableAllowFocusChange (as recommended by Chris Sells in his WinForms book), So the focus can indeed change from controls that failed validation (including moving to other tabs). I have also updated the sample code for the custom control to emphasize the fact that the ErrorProvider resides internally inside it.
OK so I finally figured it out.
I keep a dictionary whose keys are the TabPages and the values are HashSets of unvalidated controls within the corresponding tab. This is easily done by subscribing to all the validating and validated events of the controls in each tab. Finally, in OK_BUtton_Click, if ValidateChildren fails, I know one of the hashsets will be none empty and I simply jump to the first unvalidated tab (only if the currently selected tab doesn't have any error itself).
Dictionary<TabPage, HashSet<Control>> _tabControls
= new Dictionary<TabPage, HashSet<Control>>();
public OptionsForm()
{
InitializeComponent();
RegisterToValidationEvents();
}
private void RegisterToValidationEvents()
{
foreach (TabPage tab in this.OptionTabs.TabPages)
{
var tabControlList = new HashSet<Control>();
_tabControls[tab] = tabControlList;
foreach (Control control in tab.Controls)
{
var capturedControl = control; //this is necessary
control.Validating += (sender, e) =>
tabControlList.Add(capturedControl);
control.Validated += (sender, e) =>
tabControlList.Remove(capturedControl);
}
}
}
private void Ok_Button_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
_settings.Save();
this.Close();
}
else
{
var unvalidatedTabs = _tabControls.Where(kvp => kvp.Value.Count != 0)
.Select(kvp => kvp.Key);
TabPage firstUnvalidated = unvalidatedTabs.FirstOrDefault();
if (firstUnvalidated != null &&
!unvalidatedTabs.Contains(OptionTabs.SelectedTab))
OptionTabs.SelectedTab = firstUnvalidated;
}
}
I think it's pretty sweet !