Efficient technique for sending text messages to UI - c#

I have a WPF Windows app. The viewmodel calls a method in the format of Model.TrySomething(), which returns a boolean if anything in TrySomething logically fails. If false is returned, the UI can throw a message back to the user.
What is the best way to bubble this message up from the model?

This is how we do it on our projects. Works fine:
// your event args might include more properties
public class ShowMessageBoxEventArgs : System.EventArgs
{
public string Title { get; set; }
public string Text { get; set; }
}
// example of your model base
public class MyModelBase
{
public event EventHandler<ShowMessageBoxEventArgs> ShowMessageBox;
protected void RaiseShowMessageBox(string title, string text)
{
if (ShowMessageBox == null)
return;
var _Args = new ShowMessageBoxEventArgs
{
Text = text,
Title = title
};
ShowMessageBox(this, _Args);
}
}
// for this sample, this is your view model
public class MyModel : MyModelBase
{
public void DoSomething()
{
// TODO: Do Something
base.RaiseShowMessageBox("DoSomething", "Complete!");
}
}
// this is your window or in app.xaml.cs (where we do it)
public partial class MainWindow : Window
{
MyModel m_MyModel = new MyModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = m_MyModel;
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
bool m_Loaded = false; // only once
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if (m_Loaded)
return;
m_Loaded = true;
// allow model to show messagebox
m_MyModel.ShowMessageBox += (s, arg) =>
{
MessageBox.Show(arg.Text, arg.Title);
};
}
}
Best of luck!

If the message which you want to display is a modal dialog, you can write a service (lets name it MessageDialogService) which is injected in your viewmodel and then call a MessageDialogService.Show() method. This method creates a new WPF window and shows the message.
This service can then be used in any of your ViewModels to show messages.

Related

How do I get a label in a form to display the values of a property

In my C# windows form I have 2 forms. I would like to display a collection of strings in a label on a form. When I debug I show the 2 elements in my array but they are not showing in the label I am passing it to. When I hover of toString the data is there but how to I pass it to sender so it will display in the label control I have on my form?
In the snippet of code below to data is in toString but how do I get it from there down to sender.ToString????
public AccountReport_cs(Func<string> toString)
{
this.toString = toString;
}
private void AccountReport_cs_Load(object sender, EventArgs e)
{
label1.Text = sender.ToString();
}
This is another piece of the code that will open form2 where the information should be displayed.
private void reportButton2_Start(object sender, EventArgs e)
{
AccountReport_cs accountReport = new AccountReport_cs(allTransactions.ToString);
accountReport.ShowDialog();
}
Here is the last piece of code and this will show how the data gets to EndOfMonth.
public class Transaction
{
public string EndOfMonth { get; set; }
}
public override List<Transaction> closeMonth()
{
var transactions = new List<Transaction>();
var endString = new Transaction();
endString.EndOfMonth = reportString;
transactions.Add(endString);
return transactions;
}
If you need to send information between forms, the best thing you can do is create a property in the target form and assign the value you want to send before displaying the form; thus you will not need to change the default constructor of the form.
// Destiny form
public partial class FormDestiny : Form {
// Property for receive data from other forms, you decide the datatype
public string ExternalData { get; set; }
public FormDestiny() {
InitializeComponent();
// Set external data after InitializeComponent()
this.MyLabel.Text = ExternalData;
}
}
// Source form. Here, prepare all data to send to destiny form
public partial class FormSource : Form {
public FormSource() {
InitializeComponent();
}
private void SenderButton_Click(object sender, EventArgs e) {
// Instance of destiny form
FormDestiny destinyForm = new FormDestiny();
destinyForm.ExternalData = PrepareExternalData("someValueIfNeeded");
destinyForm.ShowDialog();
}
// Your business logic here
private string PrepareExternalData(string myparameters) {
string result = "";
// Some beautiful and complex code...
return result;
}
}

Passing a variable to a second window using MVVMLight?

I am building an application to teach myself MVVM and with some Googling (and some trial an error) I have managed to get to the point where I can open a second window from the ViewModel but not to pass a variable from one page to the other. This is my ViewModel.
public VendorSelectViewModel()
{
Ping ping = new Ping();
PingReply pingresult = ping.Send("192.168.1.10");
if (pingresult.Status.ToString() == "Success")
{
LoadVendorsAsync();
}
else
{
LoadVendors();
}
NextCommand = new RelayCommand(NextWindow);
}
public ICommand NextCommand { get; private set; }
void NextWindow()
{
Console.WriteLine(selectedVendor.VendorName);
Messenger.Default.Send(new NotificationMessage("NextWindow"));
}
In my view I have this
public VendorSelectWindow()
{
InitializeComponent();
_vm = new Biz.Invoicer.VendorSelectViewModel();
DataContext = _vm;
Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived);
}
private void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == "NextWindow")
{
var invoicerWindow = new InvoicerWindow();
invoicerWindow.Show();
}
}
So I know (or I think I know) this may not be a "Best Practice" but I will come back to this and refactor as I get to know the MVVM patern and MVVM Light better. Currently I am trying to pass a variable from the ViewModel of the first page (VendorSelectViewModel) to the Second page (InvoicerWindow) but I haven't managed to the syntax correct.
What do I need to do to pass a variable from one page to the next?
First of all you can pass an arbitrary object as the parameter of the IMessenger.Send<TMessage> method - the TMessage type parameter is not restricted. E.g.:
//ViewModel:
void NextWindow()
{
//...
int someValue = 10;
Messenger.Default.Send(someValue);
}
//View:
public VendorSelectWindow()
{
//...
Messenger.Default.Register<int>(this, MessageReceived);
}
private void MessageReceived(int value)
{
//...
}
If however you find the NotificationMessage class particularly useful in your case you could make use of the generic NotificationMessage<T> version, which exposes additional property Content of arbitrary type T:
//ViewModel:
void NextWindow()
{
//...
int someValue = 10;
Messenger.Default.Send(new NotificationMessage<int>(someValue, "Notification text"));
}
//View:
public VendorSelectWindow()
{
//...
Messenger.Default.Register<NotificationMessage<int>>(this, MessageReceived);
}
private void MessageReceived(NotificationMessage<int> message)
{
var someValue = message.Content;
//...
}
Or, if that does not suit you, you could create your own class deriving from NotificationMessage and exposing additional members and use that as the message object.
Instead of passing a NotificationMessage to the messenger, you could pass an instance of your own custom type which may carry as many values you want:
void NextWindow()
{
Console.WriteLine(selectedVendor.VendorName);
Messenger.Default.Send(new YourPayload() {WindowName = "NextWindow", Parameter = "some value..:");
}
...
public VendorSelectWindow()
{
InitializeComponent();
_vm = new Biz.Invoicer.VendorSelectViewModel();
DataContext = _vm;
Messenger.Default.Register<YourPayload>(this, NotificationMessageReceived);
}
private void NotificationMessageReceived(YourPayload msg)
{
if (msg.WindowName == "NextWindow")
{
string param = msg.Parameter;
var invoicerWindow = new InvoicerWindow();
invoicerWindow.Show();
}
}
YourPayload is a custom class with two properties, WindowName and Parameter.

Passing data from textbox to datagridview

I am working in Visual Studio running a Windows application.
I am wondering if I can fill a DataGridView from a TextBox, that was a passed value itself?
For example, the user would search for a patient from a dialog form. The patient's name they select would populate a TextBox on my main form. I want that selected patients prior test history to populate a DataGridView on that main form within a tab.
Is this possible, if so how would I accomplish this?
It is possible. I would suggest setting up some sort of data binding. More specifically you will want some class that maintains state and data binds to your controls and possibly your dialog form. I don't know how much you are looking for so this might be going overboard but I would suggest something like this:
public class MainForm : Form
{
public MainForm(StateManager stateManager)
{
_stateManager = stateManager;
//data binding for your text box
txtPatientName.DataBindings.Add(nameof(txtPatientName.Text), stateManager, nameof(stateManager.PatientName));
//data binding for your grid
historyGrid.DataSource = stateManager.History;
}
private void btnShowForm_Click(object sender, EventArgs e)
{
using(var form = new DialogForm())
{
var result = form.ShowDialog();
if(result == DialogResult.Ok)
{
_stateManager.UpdatePatient(form.InputPatientName);
}
}
}
private StateManager _stateManager;
}
//this is the form where you enter the patient name
public class DialogForm : Form
{
//this holds the value where the patient's name is entered on the form
public string InputPatientName { get; set; }
}
//this class maintains your state
public class StateManager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string PatientName
{
get { return _patientName; }
set
{
_patientName = value;
OnPropertyChanged(nameof(PatientName));
}
}
public BindingList<MedicalHistoryItems> History => _history ?? (_history = new BindingList<MedicalHistoryItems>());
public void UpdatePatient(string patientName)
{
History.Clear();
var historyRetriever = new HistoryRetriever();
History.AddRange(historyRetriever.RetrieveHistory(patientName));
}
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(propertyName);
}
private BindingList<MedicalHistoryItems> _history;
private string _patientName;
}

Need know which element get focus when another element lost focus

I have many controls in a window. Requirement is to know which control gets the focus from the lost focus event of a control.
Say, A Text box and it has the focus. Now I am clicking a button. while doing this, need to know that i am moving the focus to button from the Text box lost focus event.
So how could i achieve this..
This is what I did and its working for me
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
lostFocusControl = e.OldFocus;
}
private void PauseBttn_PreviewKeyDown(object sender, KeyEventArgs e)
{
/**invoke OnPreviewLostKeyboardFocus handller**/
}
Hope it will help
You can use FocusManager to handle this,
In your LostFocusEvent, Use FocusManager.GetFocusedElement()
uiElement.LostFocus+=(o,e)=>
{
var foo=FocusManager.GetFocusedElement();
}
The following class watches the FocusManager for changes in focus, it's a looped thread so you have to put up with the fact that it's running but when focus changes it will just raise an event letting you know what changed.
Just add these two classes to your project.
public class FocusNotifierEventArgs : EventArgs
{
public object OldObject { get; set; }
public object NewObject { get; set; }
}
public class FocusNotifier : IDisposable
{
public event EventHandler<FocusNotifierEventArgs> OnFocusChanged;
bool isDisposed;
Thread focusWatcher;
Dispatcher dispatcher;
DependencyObject inputScope;
int tickInterval;
public FocusNotifier(DependencyObject inputScope, int tickInterval = 10)
{
this.dispatcher = inputScope.Dispatcher;
this.inputScope = inputScope;
this.tickInterval = tickInterval;
focusWatcher = new Thread(new ThreadStart(FocusWatcherLoop))
{
Priority = ThreadPriority.BelowNormal,
Name = "FocusWatcher"
};
focusWatcher.Start();
}
IInputElement getCurrentFocus()
{
IInputElement results = null;
Monitor.Enter(focusWatcher);
dispatcher.BeginInvoke(new Action(() =>
{
Monitor.Enter(focusWatcher);
results = FocusManager.GetFocusedElement(inputScope);
Monitor.Pulse(focusWatcher);
Monitor.Exit(focusWatcher);
}));
Monitor.Wait(focusWatcher);
Monitor.Exit(focusWatcher);
return results;
}
void FocusWatcherLoop()
{
object oldObject = null;
while (!isDisposed)
{
var currentFocus = getCurrentFocus();
if (currentFocus != null)
{
if (OnFocusChanged != null)
dispatcher.BeginInvoke(OnFocusChanged, new object[]{ this, new FocusNotifierEventArgs()
{
OldObject = oldObject,
NewObject = currentFocus
}});
oldObject = currentFocus;
}
}
Thread.Sleep(tickInterval);
}
}
public void Dispose()
{
if (!isDisposed)
{
isDisposed = true;
}
}
}
Then in your code behind, create a new instance of the Focus Notifier class and hook on to it's OnFocusChanged event, remember to dispose it at the end or the thread will keep your app open.
public partial class MainWindow : Window
{
FocusNotifier focusNotifier;
public MainWindow()
{
InitializeComponent();
focusNotifier = new FocusNotifier(this);
focusNotifier.OnFocusChanged += focusNotifier_OnFocusChanged;
}
void focusNotifier_OnFocusChanged(object sender, FocusNotifierEventArgs e)
{
System.Diagnostics.Debug.WriteLine(e.OldObject);
System.Diagnostics.Debug.WriteLine(e.NewObject);
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
focusNotifier.Dispose();
base.OnClosing(e);
}
}
have you tried to register your controls to Control.LostFocus event and there you can check for Form.ActiveControl, to determine which control currently has the focus

How to return a value from a Form in C#?

I have a main form (let's call it frmHireQuote) that is a child of a main MDI form (frmMainMDI), that shows another form (frmImportContact) via ShowDialog() when a button is clicked.
When the user clicks the 'OK' on frmImportContact, I want to pass a few string variables back to some text boxes on frmHireQuote.
Note that there could be multiple instances of frmHireQuote, it's obviously important that I get back to the instance that called this instance of frmImportContact.
What's the best method of doing this?
Create some public Properties on your sub-form like so
public string ReturnValue1 {get;set;}
public string ReturnValue2 {get;set;}
then set this inside your sub-form ok button click handler
private void btnOk_Click(object sender,EventArgs e)
{
this.ReturnValue1 = "Something";
this.ReturnValue2 = DateTime.Now.ToString(); //example
this.DialogResult = DialogResult.OK;
this.Close();
}
Then in your frmHireQuote form, when you open the sub-form
using (var form = new frmImportContact())
{
var result = form.ShowDialog();
if (result == DialogResult.OK)
{
string val = form.ReturnValue1; //values preserved after close
string dateString = form.ReturnValue2;
//Do something here with these values
//for example
this.txtSomething.Text = val;
}
}
Additionaly if you wish to cancel out of the sub-form you can just add a button to the form and set its DialogResult to Cancel and you can also set the CancelButton property of the form to said button - this will enable the escape key to cancel out of the form.
I normally create a static method on form/dialog, that I can call. This returns the success (OK-button) or failure, along with the values that needs to be filled in.
public class ResultFromFrmMain {
public DialogResult Result { get; set; }
public string Field1 { get; set; }
}
And on the form:
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
To call your form;
public void MyEventToCallForm() {
var result = frmMain.Execute();
if (result.Result == DialogResult.OK) {
myTextBox.Text = result.Field1; // or something like that
}
}
Found another small problem with this code... or at least it was problematic when I tried to implement it.
The buttons in frmMain do not return a compatible value, using VS2010 I added the following and everything started working fine.
public static ResultFromFrmMain Execute() {
using (var f = new frmMain()) {
f.buttonOK.DialogResult = DialogResult.OK;
f.buttonCancel.DialogResult = DialogResult.Cancel;
var result = new ResultFromFrmMain();
result.Result = f.ShowDialog();
if (result.Result == DialogResult.OK) {
// fill other values
}
return result;
}
}
After adding the two button values, the dialog worked great!
Thanks for the example, it really helped.
delegates are the best option for sending data from one form to another.
public partial class frmImportContact : Form
{
public delegate void callback_data(string someData);
public event callback_data getData_CallBack;
private void button_Click(object sender, EventArgs e)
{
string myData = "Top Secret Data To Share";
getData_CallBack(myData);
}
}
public partial class frmHireQuote : Form
{
private void Button_Click(object sender, EventArgs e)
{
frmImportContact obj = new frmImportContact();
obj.getData_CallBack += getData;
}
private void getData(string someData)
{
MessageBox.Show("someData");
}
}
I just put into constructor something by reference, so the subform can change its value and main form can get new or modified object from subform.
If you want to pass data to form2 from form1 without passing like new form(sting "data");
Do like that
in form 1
using (Form2 form2= new Form2())
{
form2.ReturnValue1 = "lalala";
form2.ShowDialog();
}
in form 2 add
public string ReturnValue1 { get; set; }
private void form2_Load(object sender, EventArgs e)
{
MessageBox.Show(ReturnValue1);
}
Also you can use value in form1 like this if you want to swap something in form1
just in form1
textbox.Text =form2.ReturnValue1
I use MDI quite a lot, I like it much more (where it can be used) than multiple floating forms.
But to get the best from it you need to get to grips with your own events. It makes life so much easier for you.
A skeletal example.
Have your own interupt types,
//Clock, Stock and Accoubts represent the actual forms in
//the MDI application. When I have multiple copies of a form
//I also give them an ID, at the time they are created, then
//include that ID in the Args class.
public enum InteruptSource
{
IS_CLOCK = 0, IS_STOCKS, IS_ACCOUNTS
}
//This particular event type is time based,
//but you can add others to it, such as document
//based.
public enum EVInterupts
{
CI_NEWDAY = 0, CI_NEWMONTH, CI_NEWYEAR, CI_PAYDAY, CI_STOCKPAYOUT,
CI_STOCKIN, DO_NEWEMAIL, DO_SAVETOARCHIVE
}
Then your own Args type
public class ControlArgs
{
//MDI form source
public InteruptSource source { get; set; }
//Interrupt type
public EVInterupts clockInt { get; set; }
//in this case only a date is needed
//but normally I include optional data (as if a C UNION type)
//the form that responds to the event decides if
//the data is for it.
public DateTime date { get; set; }
//CI_STOCKIN
public StockClass inStock { get; set; }
}
Then use the delegate within your namespace, but outside of a class
namespace MyApplication
{
public delegate void StoreHandler(object sender, ControlArgs e);
public partial class Form1 : Form
{
//your main form
}
Now either manually or using the GUI, have the MDIparent respond to the events of the child forms.
But with your owr Args, you can reduce this to a single function. and you can have provision to interupt the interupts, good for debugging, but can be usefull in other ways too.
Just have al of your mdiparent event codes point to the one function,
calendar.Friday += new StoreHandler(MyEvents);
calendar.Saturday += new StoreHandler(MyEvents);
calendar.Sunday += new StoreHandler(MyEvents);
calendar.PayDay += new StoreHandler(MyEvents);
calendar.NewYear += new StoreHandler(MyEvents);
A simple switch mechanism is usually enough to pass events on to appropriate forms.
First you have to define attribute in form2(child) you will update this attribute in form2 and also from form1(parent) :
public string Response { get; set; }
private void OkButton_Click(object sender, EventArgs e)
{
Response = "ok";
}
private void CancelButton_Click(object sender, EventArgs e)
{
Response = "Cancel";
}
Calling of form2(child) from form1(parent):
using (Form2 formObject= new Form2() )
{
formObject.ShowDialog();
string result = formObject.Response;
//to update response of form2 after saving in result
formObject.Response="";
// do what ever with result...
MessageBox.Show("Response from form2: "+result);
}
I raise an event in the the form setting the value and subscribe to that event in the form(s) that need to deal with the value change.

Categories

Resources