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.
Related
When using code behind, the code looks like this:
AnotherWindow x = new AnotherWindow();
x.Show() ;
// or x.ShowDialog()
But how can I achieve this using MVVM? Specifically Prism?
In case you need to build a dialog for asking user login input or progressing dialog, MahApps.Metro can be a useful toolkit as it provides you with some built-in dialog UI/functionalities with MVVM pattern. For more information, check some examples here:
https://mahapps.com/controls/dialogs.html
In Prism, there's the InteractionRequest for short-lived dialogs. If you're looking for a long living dialog, like a second application window or shell, you're stuck with new Window ... Show.
To make your dialog service mvvm-friendly, you should hide it behind an interface and make it as generic as possible. Using view model first here eliminates the need to specify a window type, because you can provide a default window that just contains one large ContentControl, and the view can be mapped as DataTemplate.
At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)
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/
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.
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.