I want to show a popup when someone click on a button, the button is in a listview which is load from a webservice, how can I send a message to the view to call the AlertDialog function ?
I was maybe not very clear I paste my code bellow to see what I tried :
In my viewmodel :
public void editPost(Post item)
{
PostToEdit = item;
// Call the popup function
}
In my view :
public Dialog showEditPopup()
{
var customView = LayoutInflater.Inflate(Resource.Layout.EditDialog, null);
var builder = new AlertDialog.Builder(this);
builder.SetView(customView);
builder.SetPositiveButton("Save", SaveClicked);
builder.SetNegativeButton("Cancel", CancelClicked);
return builder.Create();
}
I tried to create an onclick function to initialize my AlertDialog in the View
var editButton = FindViewById<Button>(Resource.Id.editButton);
editButton.Click += delegate { ShowDialog(EditDialog); };
But the application crash because the posts aren't load at the time of the oncreate so the editButton is null and the event click cannot be set, so I want to create the popup in the viewmodel.
One way you can go about this is to set (from the view) an Action or Func callback on the VM, which will show the dialog, and it can be initiated from the VM.
The other option would be to have an interface defined by the VM which the View will implement (for example IViewInteraction which has a method like ShowDialog(string text).
I would suggest the first approach.
Probably there's going to be more than one place where you will need to show an alert dialog in your app.
In MvvmCross there's a plugin to show dialogs in all platforms. You implement calling it from view-model and it will work on all platforms.
https://github.com/brianchance/MvvmCross-UserInteraction
I suggest you add it by NuGet
Related
In my program I have two windows, the first one being my main window with a text box and the second one having an entry field with a button to update the text box in the first window. I'm a beginner in terms of using WPF and coding in C# in general, but is there a way to pass a pointer or reference of my main window to the second window so the second window can edit the text box of my first window? Is that even the right way to think about solving this issue?
WPF assumes you are binding your forms to a ViewModel object. This object can be bound to more than one form to give you different views and capabilities, so in this case you'd bind the same ViewModel to both forms, and what is changed in your edit form will appear automatically in your main form.
Your question is a bit vague and there are many approaches to accomplishing this. MVVM as Steve Todd mentions, is one.
However, it sounds like you simply want to open the window as a dialog. In your second window's code behind, be sure your textbox has a name in XAML and then access it create and easily accessible property that gets and sets your textbox value.
public MyTextContent
{
get => this.MyTextBox.Text;
set => this.MyTextBox.Text = value;
}
You can control the return value based on conditions (such as OK or Cancel buttons) if you like by using click events. The window contains a DialogResult property. The default is false, so you will need to set this somewhere.
this.DialogResult = true; // OK
Then in your main window's code behind, create a new instance of the window, assign it's property and show it. This will need to be done during a click event of a button or some similar trigger
var myDialog = new MyDialogWindow()
{
MyTextContent = "Textbox Starting Value";
}
bool? result = myDialog.ShowDialog(); // Returns when the dialog window is closed.
if(result != null && result)
{
this.LocalTextBox.Text = myDialog.MyTextContent; // Copy the text to the main textbox.
}
Typically you do this in data context of your main window. You use IoC to pass an instance of popup notification service in the constructor and create a private reference. You call that service method that displays the popup notification where user can enter async (and await) for its response or use reactive extensions to subscribe to submit action of that button. A thing to look out for is that you can update ui only in dispatcher thread and do not forget to dispose the subscription after you have finished using the window.
I am building a Windows Forms application that I would like to potentially port to WPF and GTK# in the future. I am interested in using the MVP pattern to accomplish this.
For a simple preferences dialog I have a designer created form that implements a view interface with events that the presenter can listen for when the dialog is saved or closed. I use the designer to create data bindings between the preferences frame's controls and the .NET project settings, so I am doing supervising presenter.
interface IPreferencesDialogView
{
event EventHandler Save;
event EventHandler Cancel;
}
public partial class PreferencesDialog : Form, IPreferencesDialogView
{
private PreferencesDialogPresenter presenter = null;
public event EventHandler Save;
public event EventHandler Cancel;
public PreferencesDialog()
{
InitializeComponent();
presenter = new PreferencesDialogPresenter(this);
}
private void PreferencesDialog_FormClosing(object sender, FormClosingEventArgs e)
{
if (this.DialogResult == DialogResult.OK)
{
Save?.Invoke(this, EventArgs.Empty);
}
else
{
Cancel?.Invoke(this, EventArgs.Empty);
}
}
}
My model uses the .NET project settings to store application settings since it is available in Mono and I can use it with both WPF and GTK#.
class PreferencesDialogPresenter
{
private readonly IPreferencesDialogView view;
public PreferencesDialogPresenter(IPreferencesDialogView view)
{
this.view = view;
view.Save += (o, e) => { Properties.Settings.Default.Save(); };
view.Cancel += (o, e) => { Properties.Settings.Default.Reload(); };
}
}
On my main form I also some very specific code to Windows Forms, a cascade button which cascades all open MDI windows. It's very simple using the LayoutMdi method provided by Windows Forms (something Java Swing does not have).
private void cascade_Click(object sender, EventArgs e)
{
this.LayoutMdi(MdiLayout.Cascade);
}
This to me seems to be working great so far. The view knows nothing about the model or the presenter and the model knows nothing about the view or the presenter. However, I have a few questions.
Is there anyway to simplify my event patterns? I really don't like having to pass arguments I do not use.
Ideally I would have only a single event, Closed, and I would forward the dialog result to the presenter. I do not like the Save/Cancel logic being in the view. However, the DialogResult type is Windows Forms specific, so I can't use it with GTK#. Could I create my own wrapper type? Is that what is usually done?
How would I go about showing this dialog? For example, on the Main Form I have a "Preferences" menu item. When it is clicked, who should the main form's presenter tell to open the preferences dialog, should it tell the preference dialog's view or presenter to show itself? This question arises because ShowDialog is obviously Windows Forms specific.
How would I even go about putting the MDI cascade logic into my presenter or is it not even worth it to bother in this case?
Is there anyway to simplify my event patterns? I really don't like having to pass arguments I do not use.
Ideally I would have only a single event, Closed, and I would forward the dialog result to the presenter. I do not like the Save/Cancel logic being in the view. However, the DialogResult type is Windows Forms specific, so I can't use it with GTK#. Could I create my own wrapper type? Is that what is usually done?
Yes, what I do and believe is the best practice is to create specific events that are related to the action occurring. NOT just passing events up from the UI. So, a single Close event including a simple enum to indicate whether to save or cancel. Your presenter would contain the logic to determine based on that enum whether to do Properties.Settings.Default.Save(); or Properties.Settings.Default.Reload();
Then in your non Windows Forms view, you would still need to invoke that event, but it would be up to the view to decide whether to save or cancel all the time, or whether to implement a custom Save/Cancel dialogue to get this info from the user.
I am making a windows phone 7 and trying to do it using MVVM. I would like to keep my view model as clean as possible but I am unsure on how to make a dialog box. I am using MVVM light and I know they have Messaging system or something but not really sure how to use it.
I would like to use Guide.BeginShowMessageBox as this seems to give more features than the standard dialog box.
How can I do this without breaking the MVVM pattern. AS when I load up the view I want to have a loaded trigger to be triggered and then check some conditions. If conditions are met show the Dialog.
// Vm
public RelayCommand MainPageLoaded
{
get
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
// breaks MVVM now as have view code in viewmodel. Need to take out somehow
Guide.BeginShowMessageBox("Test", "Test network", new List<string>() { "Yes", "No" }, 0, MessageBoxIcon.Warning, asyncResult =>
{
int? returned = Guide.EndShowMessageBox(asyncResult);
// if yes then work offline mode? Maybe another property in ViewModel will get set to say offline mode?
}, null);
}
return null;
}
set
{
// Not sure what to put here.
}
}
// View
<i:Interaction.Triggers>
<i:EventTrigger>
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding MainPageLoaded}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Edit
Another problem I am having is. I have a list that is bound to some data that is stored in this property
public ObservableCollection<ContactGroup> ContactGroups { get; set; }
then on tap I have a relaycommand that should be triggered
public ICommand GroupContactTapped
{
get
{
return new RelayCommand<GestureEventArgs>(e =>
{
var selectedTextBlock = e.OriginalSource as TextBlock;
MessageBox.Show(selectedTextBlock.Tag.ToString());
});
}
}
Yet I don't know how to find which object was "tapped" without casting the source to a textblock.
Assuming that you have one mainpage/view that hosts all the other views, like a mainwindow:
I send a message event from the viewmodels, and the dialog box is handled in the code behind of the main window. This is the only codebehind I have in my project so I find it acceptable that the rest of the project can be strictly MVVM, with this one exception.
I send the message with the following (converted from VB so it might need work):
object message = new DialogMessage("YourMessage", YourFunctionThatHandlesCallback) {
Button = MessageBoxButton.YesNo,
Caption = "Caption Goes Here"
};
Messenger.Default.Send(message);
I register for the dialog box with the following in the main page code behind:
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
''single initialization of messanger for catching message box
Messenger.[Default].Register(Of DialogMessage)(Me, Sub(msg)
Dim result = MessageBox.Show(msg.Content, msg.Caption, msg.Button, MessageBoxImage.Warning)
''Send callback
msg.ProcessCallback(result)
End Sub)
End Sub
End Class
I could not succesfully convert the C# lambda so I had to leave it in VB. Hope this helps
There is a MessageBoxService in the Cimbalino Phone Windows Toolkit!
You can use that in a MVVM architecture.
What it truly means to follow "the MVVM pattern" is a very subjective thing.
For instance, some people will say you shouldn't show/launch a messagebox (of any type) from the VM, while others will say this is fine.
As with any ambiguity, you'll need to balance adherence to a pattern, with what's most appropriate for a specific project, with what's appropriate for the people developing and maintaining the code base.
In terms of MvvmLight, the messaging system it uses is for communicating from a viewmodel to either another viewmodel or a view, not for displaying messages to the user.
If you are going to use Guide.BeginShowMessageBox, particularly from a viewmodel, beware that it is non-blocking. If you want it to behave like a "regular" MessageBox you'll need to use it with a ManualResetEvent so that it's not possible to continue to interact with the app while the messagebox is displayed.
I am launching a window using HtmlPage.Window.Navigate(Uri, "_blank"); from a Grid row using a context menu for some custom editing specific to a page referred by the row. I am using telerik-grid in silver-light. I want to refresh the grid when user closes that window. How can i do this?
Please share some idea on, Is this possible? or where to look for start to tackle this?
UPDATE: I am using Silver-light MVVM model. The child window I am opening from silver-light is the the web browser, I would actually need a technique where I can have the grid in the silver light application to get refreshed when I close the web browser. Additionally i am using RIA service to expose the methods for the web browser.
Expose a method in your Silverlight app that is accessible from Javascript.
namespace SilverlightApplication
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("Page", this);
}
[ScriptableMember]
public void UpdateDataGrid()
{
myDataGridItemsReload(); // your routine
}
}
}
Then in the newly opened window, you can execute a method on the Silverlight instance
<script>
window.onunload = refreshParentGrid;
function refreshParentGrid() {
var hostDiv = window.opener.document.getElementById("silverlightControlHost");
var control = hostDiv.children[0];
control.Content.Page.UpdateDataGrid();
}
</script>
I'm new to Caliburn.Micro and I'm wondering what is the best way to handle user Login/Logout cycles in my application. I saw some suggestions online to implement this using an empty Shell-View which switches between the LoginView and the main application view, each with a custom ViewModel of course.
I don't really like this solution, because for me these are 2 separate windows with very different properties (Title, Icon, Size) and it seems an unclean solution two change one window to look like the other. Another problem is, that the Login Window comes from an utility library which I don't control and which doesn't use Caliburn.Micro, it's a plain old Window which gives me an event when the user clicks "Login".
I also saw suggestions to display this Dialog in the Bootstrapper startup method, but the problem I see with that is that the user can choose to "Logout" of the application which should display the Login dialog again. It seems wrong to me to handle the switching between the Views in the Bootstrapper.
What I would like is to have some sort of ApplicationViewModel or ApplicationController which works like a Caliburn Conductor, but instead of switching between Views inside a Window, it should switch between the LoginWindow and the MainWindow and should also handle Closing of the whole application (which also requires a Logout). On Activation it would show the LoginWindow, handle the Login event and then switch to the Main Window (Shell). If the user chooses to "LogOut", the event should bubble up to the ApplicationViewModel/Controller again which would deactivate/close the MainWindow, perform the Logout and then show the LoginDialog again. Similar a Close event would do the Logout, but then Shutdown the whole application.
So my questions are:
What do you think about this solution and do you have another/better one?
How do I implement this? ;-)
Thanks a lot!
I think the solution to your problem is fairly easy.
In a nutshell you are creating one ViewModel as Shell which is represented with a Login Window when the application starts. If the user logs in successfully this window closes and the same instance of the viewModel is displayed in a Content Window. If the user is doing a logout, the Login Window is shown again.
First of all create an interface IShell which exposes two delegates LoginSuccessful and Logout
public interface IShell
{
Action LoginSuccessful { get; set; }
Action Logout { get; set; }
}
Next create a class ShellViewModel which implements IShell
public class ShellViewModel : Screen, IShell
{
public ShellViewModel()
{
LoginSuccessful = delegate { };
Logout = delegate { };
}
public Action LoginSuccessful { get; set; }
public Action Logout { get; set; }
public void DoLogin()
{
LoginSuccessful();
}
public void DoLogout()
{
Logout();
}
}
The methods DoLogin and DoLogout are Actions which can be bound to a Button or whatever control appropriate for you.
Next step is to override the OnStartupMethod in your Bootstrapper. This premises that you have an instance of the WindowManager and ShellViewModel exported by an IoC Framework of your choice.
protected override void OnStartup(object sender, StartupEventArgs e)
{
var windowManager = IoC.Get<IWindowManager>();
var viewModel = IoC.Get<IShell>();
viewModel.LoginSuccessful =
() => GuardCloseAndReopen("Content");
viewModel.Logout =
() => GuardCloseAndReopen("Login");
windowManager.ShowWindow(viewModel, "Login");
}
private void GuardCloseAndReopen(string shellViewMode)
{
var windowManager = IoC.Get<IWindowManager>();
var shellScreen = IoC.Get<IShell>() as Screen;
Application.ShutdownMode = ShutdownMode.OnExplicitShutdown;
shellScreen.TryClose();
Application.ShutdownMode = ShutdownMode.OnLastWindowClose;
windowManager.ShowWindow(shellScreen, shellViewMode);
}
The trick to this is: If the DoLogout method is called, the current window gets closed by calling TryClose on the ShellViewModel. At the same time you prevent the application from being shutdown by setting the Application.ShutdownMode to OnExplicitShutdown. Then using the windowmanager, you create another window in Login Mode by passing "Login" as Context information to the windowManager. This is actually the same ViewModel, however, with a different visual representation.
For Logout you are doing the same thing just around.
To get this working using Caliburn Conventions, you need a special project structure as seen here (and explained there):
Now I challenge you to take this code and create a little sample application. Create a Login View (which does Login with a Button or whatever) and create a Content View with a Logout Button using the LoginSuccessful/ Logout Methods.
This will solve your issue with a minimum of Code and classes. Hope this will be helpful to you.
I've had a go at creating something that basically works but probably needs a bit more work to be really usable. The fully comments and source can be found on this post Caliburn.Micro Login Window sample on my website.
I used the IEventAggregator of Caliburn.Micro to control the transition between the two windows. You get this code to open the login screen:
public void Handle(LoginEvent message)
{
LoginWindow loginWindow = new LoginWindow();
loginWindow.Login += new EventHandler<LoginEventArgs>(this.LoginWindow_Login);
loginWindow.Cancel += new EventHandler(LoginWindow_Cancel);
loginWindow.ShowDialog();
}
this same source is used for both the first time the app opens and when the Logout event is published. the Logout event looks like this:
public void Handle(LogoutEvent message)
{
Application.Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;
message.Source.TryClose();
Application.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
this.events.Publish(new LoginEvent());
}
When a login is successful it uses this code to open the main window which is based on a ViewModel:
ContentViewModel viewModel;
viewModel = IoC.Get<ContentViewModel>();
viewModel.Username = e.Username;
this.windowManager.ShowWindow(viewModel);