(MVP Pattern) How to forward error messages from Presenter to View? - mvp

I have a method where the user can search for a article number and if its available in the database the articlenumber gets bound to a BindingList. Now I want to let the user know if the article is not available in database. How do I do that the right way?
Just pass the message errorMessage to my interface method?
Presenter:
string errorMessage;
_view.ErrorMessage(errorMessage);
View:
public void ErrorMessage(string errorMessage)
{
MessageBox.Show(errorMessage);
}
Would you do it the same way?

We bubble an event. In the presenter you register that event:
public event PresenterEventHandler Message;
And then raise it like so:
PresenterEventArgs pe = new PresenterEventArgs("Error message", Status.Error);
this.Message(this, pe);
Then in the view:
protected override void OnInit(EventArgs e)
{
this.presenter = new MyPresenter(this, MyBusinessService.Instance);
this.presenter.Message += new PresenterEventHandler(presenter_Message);
}
void presenter_Message(object sender, PresenterEventArgs pe)
{
// display error message
}
You can pass different types of statuses back to the view in this way, and not just error messages. We have Success, Error, Locked, Warning, Help.

In the case of error messages I would call some base functionality. This way you could choose wether to update the status window on the bottom left and/or display a modal message box.
In the presenter:
_windowManager.NoItemFound(errorMessage)
In the window manager:
_statusBox.Text = errorMessage; MessageBox.Show(errorMessage);

We should not re-invent the wheel ....
You should simply throw an exception in your model.
Then, the view will catch the exception using a try catch block.
In the "catch", show your message box.

If its MVP - Passive View, then the View interface should have a property that could read:
public interface IArticleListView {
// more stuff here...
bool ErrorMessageVisible { set; }
string ErrorMessage { set; }
// more stuff here...
int ArticleNumber { get; }
}
public interface IPresenter {
public void Update();
}
public class ArticleListPresenter : IPresenter {
IViewArticleList _view;
public ArticleListPresenter(IArticleListView view) {
this._view = view;
// you could update the view here or with an update method,
// completely up to you.
}
// Assuming you are using a fine grained presenter
public void HandleArticleNumberSearch() {
bool articleNotFound;
// search for the article ...
if ( articleNotFound ) {
this._view.ErrorMessageVisible = true;
this._view.ErrorMessage = string.Format("The article #{0} was not found.", number);
}
}
}
public class ArticleList : Page, IArticleListView {
ArticleListPresenter _presenter;
public bool ErrorMessageVisible {
set { this.lblErrorMessage.Visible = value; }
}
public bool ErrorMessage {
set { this.lblErrorMessage.Text = value; }
}
public int ArticleNumber {
// You have to do some sort of validation here, but I'll keep it simple
get { return Integer.Parse(this.txtArticleNumber.Text); }
}
protected override void OnInit(EventArgs e) {
this._presenter = new ArticleListPresenter(this);
}
protected void Page_Load(object sender, EventArgs e) {
// this implementation keeps the state in the View, and updates it
// in the presenter: Passive View
if(!this.IsPostBack) {
this._presenter.Update();
}
}
protected void OnSearchArticleButtonClick(object sender, EventArgs e) {
this._presenter.HandleArticleNumberSearch();
}
}

That's what I do.
Another way I've read about would be for the model to know how to show an error (perhaps by an ErrorMessagePresenter) so the error is detached from the original presenter.
I haven't really found a use for that, for me, it always ends in the presenter and view implementing both interfaces.

Related

Problem adding a new record in my Xamarin app

I am using Freshsmvvm in my project and I want to display a list of operations,
this is my method from the crud
public List<Operation> GetAll()
{
try
{
return connection.Table<Operation>().ToList();
}
catch (Exception ex)
{
StatusMessage = $"Error: {ex.Message}";
}
return null;
}
In my viewModel i'm have a list and a method to obtain the saved records
private List<Operation> _listOp;
public List<Operation> ListOp
{
get { return _listOp; }
set
{
_listOp = value;
RaisePropertyChanged();
}
}
private void GetOp()
{
ListOp = App.OperationRepository.GetAll();
}
*add the GetOp method in the constructor to load in the collectionview*
public override void Init(object initData)
{
GetOp();
}
What happens is that the list does not update, I have to close the application and when I open it again, the entered record appears.
This is the list without adding a new record
This is the list with a log after restarting the app
You can use the method OnAppearing() to refresh your list.
protected override async void OnAppearing()
{
base.OnAppearing();
ListOp = App.OperationRepository.GetAll();
}

Windows Phone Page navigation in MVVM

I am using MVVM in one of the app. I have created different project for Model, View and View Model.
I need to navigate to another XAML from ViewModel. I found some solution using MVVM light. Is there any way of implementing navigation from view model without using MVVM light.
Simple as that,
IF you want to navigate from page1 to page2,
private void MoveToPage2FromPage1()
{
NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
How to perform page navigation on Windows Phone 8
You can store current page url on a notify property of Shared ViewModel in App. After that, it is easy to catch the change of this url and navigate to the correct url by observing it.
public class AppViewModel : INotifyPropertyChanged
{
public string CurrentPageURL { get; set; }
private string _currentPageURL;
public string CurrentPageURL
{
get { return _currentPageURL;}
set
{
if (_currentPageURL==value)
return; // to prevent reload the same page.
_currentPageURL = value;
NotifyPropertyChangedCurrentPageURL
}
}
// INotifyPropertyChanged implementations
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
// Store in static singleton instance of AppViewModel
public class App : Application
{
private static Lazy<AppViewModel> _ViewModel=new Lazy<ViewModel>();
public static AppViewModel ViewModel { get { return _ViewModel.Value; } }
....
public App()
{
AppViewModel.PropertyChanged=(s,a) =>
{
if (a.PropertyName=="CurrentPageURL")
{
NavigationService.Navigate(new Uri(AppViewModel.CurrentPageURL, UriKind.Relative));
};
}
}
}
// Usage sample
public class Page1ViewModel
{
private btnMoveNextPage_Click(object s, EventHandler a) {
App.ViewModel.CurrentURL="~/Page2.xaml";
}
}

windows phone loading data from MainViewModel

Noob question probably.
I am developing a mvm wp7 app where the map shows pushpins of salons. The database is retrieved from a link.
The problem i am struggling with is that the observable collection data is not being loaded from the App._ViewModel (where the json serializer parses the database and works fine). On debugging the app shows a plain map and thats all. On returning a string attribute from the database causes a break on that code. i tried messagebox as well to show the string, still crashes.
Heres the code:
mainviewmodel.cs
public class MainViewModel
{
public bool IsDataLoaded { get; private set; }
public ObservableCollection<SalonViewModel> SalonCollection { get; private set; }
public MainViewModel()
{
IsDataLoaded = false;
}
public ObservableCollection<SalonViewModel> LoadData()
{
SalonCollection = new ObservableCollection<SalonViewModel>();
var wednesday = new Uri("http://blehbleh.txt");
WebClient wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(wednesday);
return SalonCollection;
}
public void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(ObservableCollection<SalonViewModel>));
ObservableCollection<SalonViewModel> list = serializer.ReadObject(e.Result) as ObservableCollection<SalonViewModel>;
foreach (SalonViewModel b in list)
{
SalonCollection.Add(new SalonViewModel { sid=b.sid,sname=b.sname,sgeo_lat=b.sgeo_lat,sgeo_lon=b.sgeo_lon,
}
this.IsDataLoaded = true;
}
catch (Exception ex)
{
//throw ex;
MessageBox.Show(ex.Message);
}
}
The App.cs
public partial class App : Application
{
private static MainViewModel viewModel;
public static MainViewModel _viewModel
{
get
{
if (viewModel == null)
{
viewModel = new MainViewModel();
}
return viewModel;
}
}
void LoadData()
{
if (!_viewModel.IsDataLoaded)
{
_viewModel.LoadData();
}
}
etc
Heres the mappage.cs
private void salon_map_Loaded (object sender, RoutedEventArgs e)
{
foreach (SalonViewModel Salon in App._viewModel.LoadData)
{
MessageBox.Show(Salon.sname);
Pushpin p = new Pushpin();
p.Content = Salon.sname + System.Environment.NewLine + "Rate: ";
Layer.AddChild(p, new GeoCoordinate(Salon.sgeo_lon, Salon.sgeo_lat));
}
Map1.Children.Add(Layer);
}
In your MainViewModel LoadData function, OpenReadAsync() is an asynchronous function, and thus returning SalonCollection on the next line will return an empty ObservableCollection, since the callback function wc_OpenReadCompleted has not run yet.
Also, the reason the MessageBox.Show crashes is because you are attempting to call a UI function on a non-UI thread (solution to that here: Dispatcher.Invoke() on Windows Phone 7?)
Instead of returning the ObservableCollection and manually adding children to the map from that, try binding a MapItemsControl layer of the Map to the ObservableCollection of your view model. There's a decent example of doing that here: Binding Pushpins to Bing Maps in Windows Phone

WP7 Developpement : How to make the program wait until the end of an EventHandler?

When my view wants the value of LogoStation, it returns null because my program has not yet executed LoadStation_Completed.
I want my program waits that LoadStation_Completed is executed before continuing.
Thx
public class Infos
{
#region propriétés
private DataServiceCollection<SyndicObject> _infosStation;
public DataServiceCollection<SyndicObject> InfosStation
{
get
{
return _infosStation;
}
set
{
_infosStation = value;
}
}
#endregion
string nameStation;
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
private ImageSource _logoStation;
public ImageSource LogoStation
{
get
{
return _logoStation;
}
set
{
_logoStation = value;
NotifyPropertyChanged("LogoStation");
}
}
public Infos(string station)
{
nameStation = station;
getInfos();
}
public void getInfos()
{
SyndicationContext service = new SyndicationContext(new Uri("http://test/817bee9d-faf4-4680-9d05-e41c2c90ae5a/"));
IQueryable<SyndicObject> requete = (from objectSki in service.Objects
where objectSki.NOMSTATION == nameStation
select objectSki);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
InfosStation = new DataServiceCollection<SyndicObject>();
InfosStation.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(InfoStation_LoadCompleted);
InfosStation.LoadAsync(requete);
}
);
}
void InfoStation_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
LogoStation = new BitmapImage(new Uri(#"http://test/upload/" + InfosStation[0].LOGO, UriKind.Absolute));
}
}
By using the property setter you are using NotifyPropertyChanged (correctly) to tell the UI bound to LogoStation that it has been updated. This should mean that the UI will display nothing initially and then the image when the load has completed.
Without seeing your view code what you have here looks correct - apart from the fact that your Infos class doesn't inherit from INotifyPropertyChanged. This means that the event never gets sent.
Update your class definition and you should be good to go.

Prevent event from being fired when calling expandItem()

the title says almost everything. When I execute the expandItem() function programmatically I do not want the fired event causing a nodeExpand() call.
I have implemented the ExpandListener:
#Override
public void nodeExpand(ExpandEvent event)
{
System.out.println("This should only appear when the user clicks the node on the UI");
}
When I call the expandItem() function of the Tree class, there is always an event fired. This is the code of the original Tree class:
public boolean expandItem(Object itemId) {
boolean success = expandItem(itemId, true);
requestRepaint();
return success;
}
private boolean expandItem(Object itemId, boolean sendChildTree) {
// Succeeds if the node is already expanded
if (isExpanded(itemId)) {
return true;
}
// Nodes that can not have children are not expandable
if (!areChildrenAllowed(itemId)) {
return false;
}
// Expands
expanded.add(itemId);
expandedItemId = itemId;
if (initialPaint) {
requestRepaint();
} else if (sendChildTree) {
requestPartialRepaint();
}
fireExpandEvent(itemId);
return true;
}
What I did now to get this work is:
m_Tree.removeListener((ExpandListener)this);
m_Tree.expandItem(sItemId);
m_Tree.addListener((ExpandListener)this);
Is there any nicer approach?
You could try to create a switch to your listener. For example:
private boolean listenerDisabled;
void setListenerDisabled(boolean disabled)
{
listenerDisabled = disabled;
}
#Override
public void nodeExpand(ExpandEvent event)
{
if(listenerDisabled) return;
System.out.println("This should only appear when the user clicks the node on the UI");
}
and disable the listener when needed.
If there are more than one listener, you could try creating a subclass of Tree and overriding some methods.

Resources