I am currently building an application based on a real world scenario, to help me learn and understand WPF and MVVM. To that end I have read and worked through Karl Shifflett's "In The Box" VSIX, and I was able to adapt most of the concepts to the application that I am working on.
While I think MVVM is a powerful design pattern, it does (seemingly) make things that were once trivial (e.g. displaying messages, navigation, interacting with multiple window), not so trivial or straightforward. Now onto the crux of my problem / confusion.
The WPF application that I am working on is a Windows based application, and I am working from a set of basic requirements:
A basic login screen
After a successful login, close the login screen and open the actual application
Simulate a typical program workflow (opening "child" windows via button clicks, displaying modal windows, etc.)
Preform data validation / error handling
Log out
I am used to working with MDI Applications on a windows platform where interactions on a parent form cause child forms to open; I understand that MDI is not something that WPF supports and I am fine with approaching development from a different perspective. My UI would still work in a similar manner to a MDI application though: I have my application layout, and as I interact with that layout my application will respond by opening windows, displaying messages, and so on. It isn't clear to me (via MVVM) how to interact with multiple windows, or how well MVVM would scale to a large application with many windows / views.
I am not opposed to using something like Prism, but I haven't found a good article on how Prism approaches my particular problem very well. Any help, advice, feedback, or otherwise is greatly appreciated!
Have you tried looking at nRoute Framework?
A link can be found here
There are actually some good tuturials about prism
Link 1
Link 2 (Part II of Link1)
Link 3
For a more straight forrward application (not very complex and modular), you can always create a aplication, with a main window that manages child usercontrols (login window, menu window, other windows ...)
For example, create a window a contentpresenter in it, and in codebehind:
public partial class ShellWindow: Window
{
public enum PagesTypes { Login, Home }
PagesTypes currentOpenedPage;
LoginUserControl login;
HomeUserControl home;
public WindowController()
{
InitializeComponent();
login = new LoginUserControl ();
login.GoToPage += new LoginUserControl.ChangePageHandler(GoToPage);
GoToPage(PagesTypes.Login);
}
public void GoToPage(PagesTypes page)
{
switch (page)
{
case PagesTypes.Login:
//Close last opened usercontrol,
....
//open new usercontrol
login = new LoginUserControl();
contentpresenter.content = login;
break;
//other pages cases
....
}
currentOpenedPage = page;
}
}
And in for example the login usercontrol:
public partial class LoginUserControl : UserControl
{
internal delegate void ChangePageHandler(ShellWindow.PagesTypes toPage);
internal event ChangePageHandler GoToPage;
public LoginUserControl()
{...}
//Methods for login
.....
internal void LoginOK()
{
if(this.GoToPage != null)
GoToPage(ShellWindow.PagesTypes.Home);
}
}
You can build a good dynamic using this method changing usercontrols, simulating diferent windows.
Hope this gives you some ideas.
MVVMing your child windows actually can be kind of easy, especially if you decide that a tabbed interface is OK. Your outer window's view model simply has a collection of ChildWindowViewModel. You create a new tab just by creating the new view model, asking the outer window to add it to it's collection, and WPF's DataTemplate awesomeness will take care of the proper display. You'll have to do some fiddling to get tab 'close' operations working the way you want. It's kind of a pain but doable.
If you really want to do MDI, there's nothing built into WPF for it (I think Microsoft has decided that it is a bad UI pattern now?), but there may be 3rd party controls out there for it. Any good one will still mirror this solution where their MDI container control will bind to your list of child window view models.
Related
Pre-warning, I'm new to C# and XAML, but I'm really enjoying Windows 10 UWP apps. I've got a question on how to appropriately handle a SplitView.
I've got a Main Page, in which I have a SplitView control. In the SplitView Content, I've added a Frame for navigation to other pages. I want to add the Hamburger button on the child page to open the SplitView on the Main Page, but I can't access the SplitView control from the child page. How can I make the SplitView control accessible so that the hamburger button within the sub-page can open the SplitView pane?
The alternative is to add a header in the Main Page and have a static hamburger button there, but I don't like this option as it makes handling the text header content more difficult. Another is to copy the SplitView to each page. I don't want to do this either.
Any advice would be fantastic! Thank you.
I would highly recommend you take your alternative option of including the hamburger button in the main page. Users always expect it to be in the same location every time and changing this approach will probably result in a bad user experience.
You also don't want to be repeating code and thus you don't want to recreate the button on every page as well as any additional functionality like the open/close commands.
Rather than referencing elements from one page to another, a better practice is to keep things loosely coupled. This can be done with a messenger plugin which sends an event from one page to the other which can give it instructions on what you want to do. That way the other page only has to listen for the event instead of holding strong references. To streamline some of this process you could inherit from a base class which implements the messenger functionality.
That would provide a solution to your button and your header text situations but setting them up is out of the scope of this question. Depending on the size of you app and your goals, you might like to look into existing frameworks which helps in designing maintainable apps. A good Mvvm framework I would recommend checking out is MvvmCross which also cross platform and contains a messenger plugin.
Good luck with your app.
I found that solution :
In the MainPage, in your SplitView pane button method, add a SplitView reference as parameter in Navigate() :
private void SlitViewPaneButton_Tapped(object sender, TappedRoutedEventArgs e)
{
var frame = contentFrame;
Page page = frame?.Content as Page;
if (page?.GetType() != typeof(ChildPage))
{
frame.Navigate(typeof(ChildPage), SplitViewName);
}
}
In your ChildPage.xaml.cs :
protected override void OnNavigatedTo(NavigationEventArgs e)
{
SplitView sv = new SplitView();
sv = e.Parameter as NavigateControls;
}
You can now do sv.IsPaneOpen = false, in your ChildFrame code.
Note : if you want to pass several Controls, create a Class with these Controls as variables, and use an instance as parameter.
As stated above, it is better to keep your hamburger button in your main page for a couple of reasons. One is the consistency mentioned above. Second, you would have to recreate the hamburger button in each of your content pages instead of just once in the MainPage.xaml. Additionally, keep in mind, there are different kinds of interactions with the SplitView menu in terms of how it pops in and out and how it is displayed, all listed below.
Inline – When the menu pane is opened, it pushes the content over. When it’s closed, the content goes back to its original location
Overlay – When the menu pane is opened, it lays on top of the content. When it’s closed, it is invisible.
Compact Overlay – When the menu pane is opened, it lays on top of the content. When it’s closed, the pane is still visible in Compact Mode.
Compact Inline – When the menu pane is opened, it pushes the content over. When it’s closed, the content goes back to its original position but the pane is still visible in Compact Mode.
You can also see a quick intro into the SplitView here.
http://jamesqquick.com/windows-10-splitview-intro/
My goal
I am working on a project in C# using Visual Studio 2013. The project is one that I intend to contain a lot of pages. These pages are all linked together using buttons. My problem is that I cannot come up with an efficient and elegant solution for this.
My attempts
So far I have came up with two potenial solutions to my problem. First I added extra forms and then on button press I hid the current form and displayed the new form, like so:
Form2 frm = new Form2();
frm.Show();`
Form1.Hide();
While this does work, I have two problems with it.
My project will end up with hundreds of forms
The transition between forms looks sloppy. I am aiming for a browser like transition by where all navigation occurs on one window, without opening and closing others.
The second potential solution I tried incorporated the use of Panels. So I essentially created each page on a different Panel. Then the appropriate panel was shown upon a button press and the rest were hidden. Like this:
private void button1_Click(object sender, EventArgs e)
{
mainMenuPanel.Hide();
submenuPanel1.Show();
submenuPanel2.Hide();
submenuPanel3.Hide();
submenuPanel4.Hide();
}
This is exactly what I was looking for however my issue with it is that managing the vast amount of panels quickly became a nightmare. Editing the controls on a Panel that was hidden behind 9 other Panels and as the number of panels in my project was only going to grow - this does not seem like the ideal solution in its current form.
In my head I thought there maybe an option in Visual Studio 2013 that allows me to 'hide' the Panels I am not using on the form, or drag them off the form temporarily. Is that an option in Visual Studio.
If not do any of you know a more efficient and manageable way of achieving this?
Thanks in advance.
If you are stuck using WinForms, your best bet is probably using UserControls. you can actually extend the UserControl class out to be a "page" ie: UserControlPage. This makes the form much simpler in function, but you will need to do some finicky work with handling events /passing data if the controls need to talk to each other.
if you aren't nailed into using Winforms, WPF supports all of this natively, and has wonderful tools for building all the pages you would need, and storing/populating your data, and propagating events.
If you want to have single form with changing content, and you don't want to mess up with panels in one form, then solution is user controls. You will be able to create them dynamically and add to form controls. Also there is no mess, because your form will be very simple - you can have single 'placeholder' control which will be used to dock user control which is currently displayed (e.g. panel control):
private void ShowContent(Control content)
{
placeHolderPanel.Controls.Clear(); // clear current content
placeHolderPanel.Controls.Add(content); // add new
content.Dock = DockStyle.Fill; // fill placeholder area
}
Usage:
private void button1_Click(object sender, EventArgs e)
{
ShowContent(new FooUserControl());
}
You could subclass the Panel class and create as many of those custom panels as needed, then they would be inserted on your Main Form, and managed as you described.
The advantage is that you would be able to individually edit them as a separate user control.
The drawback is that you lose direct event handling of controls on those panels from the main form. You can still define your own events on those panels and delegate the individual control events.
There's always a trade-off somewhere.
Cheers
I am redesigning my WinForms application using WPF, I was only 2 weeks in to the project and quickly learn't that many of the controls I needed would require custom controls while WPF allows me to design these easily.
So far I have a MainWindow.xaml, Login.xaml and Menu.xaml,
The idea is to display the login, upon verification show the menu then if that closes back to login inside the main window which would be full screen.
To get the functionality working I simply created the login and menu forms with the WindowStyle="none" and centered it to the screen, this obviously doesn't work because there is still a form but not linked to the main form.
In winforms I used MDI but reading this forum and looking at question MDI is frowned on, I looked at tab controls. So far I can find tutorials on using 1 form to display it but can not find a suitable tutorial for going login>menu>login
I don't want to be spoonfed, this project is just a practice project to try and get to grips with WPF but there are a lot of questions relating to the subject and everyone handles it differently it seams.
Thanks
Without creating an overly complex answer, and without including concepts that are obviously new to you such as DelegateCommand(s) or WindowManager(s), this is a bare bones example of a Fullscreen application showing many different "sub windows" (which are not windows per se, but rather UserControls)
MainWindow:
<Window x:Class="FullScreenAppSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowState="Maximized"
WindowStyle="None">
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ShowLogin()
{
var loginview = new LoginView();
this.Content = loginview;
}
private void ShowMenu()
{
var menu = new MenuView();
this.Content = menu;
}
}
LoginView:
<UserControl x:Class="FullScreenAppSample.Login.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- your login screen UI here -->
</UserControl>
MenuView:
<UserControl x:Class="FullScreenAppSample.Menu.MenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- your Menu UI here -->
</UserControl>
This is what could be called a "View First" approach, where the View dictates the "flow" of the application by taking the responsibility of instantiating other views and making them visible.
That being said, I'll take a minute to address your assertion that
everyone handles it differently
Yes. The mainstream approach to creating WPF applications is something called MVVM, which was conceived as a WPF-specific version of Martin Fowler's Presentation Model. There are, however, many interpretations and many different versions of MVVM out there, together with many MVVM Frameworks, such as MVVM Light, Caliburn.Micro, and Microsoft's Prism (amongst many others).
Each of these frameworks provide the basic tooling (base classes, helper classes, services, abstractions, event aggregators and whatnot) to ease the development of large-scale, complex WPF applications.
Bottom line: there is not a definitive "right way" to handle things like View and ViewModel instantiantion / management in WPF, that will depend on your choice of MVVM Framework and the specifics of your project, such as need for testability.
I, personally, have taken various parts and components (and concepts, too) from several different frameworks and composed my own, ViewModel-first MVVM approach. I would recommend that you take some time to analyze your project requirements and consider whether using any of these.
Instead of thinking in terms of "forms" and "windows" think of how you might design this application as if it were a web application. The login "screen" can simply be a user control laid on top of everything else that prevents the user from interacting with any of the controls below while it is displayed.
Also don't reference controls within each other directly. Instead add events to each of your user controls and bind them to the appropriate methods in other controls in your MainWindow.
I'm in the beginning stages of designing an application using Prism and have a question. In all the reference material I've been able to find, there is lots of details on creating a single screen application, but I have a requirements beyond that.
I would like the have two windows showing (Multi screen), both with the exact same layout but each looking at a difference source of information for their data. In other words, I have datasource A and datasource B that update very frequently and I need to monitor both of them at the same time.
Is there a way to launch a prism app multiscreen in this manner or would it better to launch separate processes for each source?
Thanks.
This should be pretty simple. Launching a new Window for each ought to do what you need (the user would have to move the window to the second monitor... I suppose you could investigate some p/invoke magic to move the window to the proper monitor if you wanted).
Do you need something more complicated?
If it's the same view with different data, I'd use MVVM and spin them off sort of like this:
MyFirstViewModel vm1 = new MyFirstViewModel();
MySecondViewModel vm2 = new MySecondViewModel();
MyView view1 = new MyView();
view1.DataContext = vm1;
MyView view2 = new MyView(vm2);
view2.DataContext = vm2;
view1.Show();
view2.Show();
Hopefully your view models can be reusable too so you wouldn't need to write a class for each, but hopefully this illustrates the strategy a little.
Does a Winform Framework exist for something similar to ASP.NET Masterpage or MS Access SubForm ?
With MS Access SubForm you can do like ASP.NET Masterpage. It's a huge loss of time with Winform when having to create a lot of complex form. You have to compensate with either Code Generation which create code duplication or do Runtime Dynamic Form which is much more difficult.
I searched on the Internet but can't find any.
The closes thing to Master Pages is Form Inheritance. It is regular class inheritance but also supported by the Designer. To try it:
1) Add a form with Ok and Cancel Buttons, Build project (essential)
2) Choose Project, Add new item, Windows and then the Inherited Form template. Pick the Form from step 1) as the base Form. Add some controls.
3) Repeat step 2) a few times
4) make some Buttons to show the Forms, Build and Test
5) Go back to the Form from 1) and change a few things (Background), run again
Your other tool are UserControls, they work much the same as in ASP.NET. You develop them like Forms and apply them as Controls.
You can add forms to a form, or to a panel on a form.
public Form1()
{
InitializeComponent();
Form2 embeddedForm = new Form2();
embeddedForm.TopLevel = false;
Controls.Add(embeddedForm);
embeddedForm.Show();
}
You will need to set the FormBorderStyle to None, unless you want to have an actual moveable form inside your form.
I was in a bit of a hurry at the time of posting, but Henk is right. You should consider creating a user control for this instead. Not to be confused with a custom control, which is intended for when you need to do your own drawing instead of using collections of standard Windows controls.