Let's say I want to create an Application similar to the Windows Explorer. I'd need some modules (assemblies) like these
- MyApp.Modules.NavPane
- MyApp.Modules.Ribbon
- MyApp.Modules.StatusBar
- MyApp.Modules.Body
Then I have another 2 modules for my entry point and a shell
- MyApp.Core
- MyApp.Shell
So now my issue is: what is the correct way to lay out my app? Do I design everything in my ShellView and just hard-reference my modules?
By hard-reference I mean I set an actual reference to my module assembly, constructor inject the ViewModels I need (NavPaneViewModel, RibbonViewModel, ...) and put them in my ShellView with ContentControls
Here some pseudo-ish code
public class ShellViewModel
{
public Screen NavPane { get; }
public ShellViewModel(NavPaneViewModel navPane, ...)
{
NavPane = navPane;
// ...
}
}
and my ShellView
<UserControl x:Class="MyApp.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions><!--...--></Grid.RowDefinitions>
<Grid.ColumnDefinitions><!--...--></Grid.ColumnDefinitions>
<ContentControl x:Name="NavPane" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" />
</Grid>
</UserControl>
Related
I have read several article, tutorial, example. But I'm still unable to make navigation between page and windows happens.
> Visual Studio Community 2019
> .NET Framework: 4.7.2
To make it simple, I have 3 pages and 1 window ALL VIEW IS FULL SCREEN FOR KIOSK APPLICATION. Details page as below:
MVVMApps.sln
│
├── PageInitialize.xaml
├── PageHome.xaml
└──── PageSelectLanguage.xaml
└── WinMessage.xaml
I have try MvvmLight by GalaSoft but stuck on navigate from PageInitialize.xaml to PageHome.xaml. And I just found article from GalaSoft that WPF is coming soon for INavigationService. Most tutorial I found is sampling for Xamarin.
Plus I got undefined assembly using Microsoft.Practices.ServiceLocation; which I see it's available on Enterprise. On Nuget, installing MvvmLight will install CommonServiceLocator too.
public class ViewModelLocator
{
private static bool initialized;
public ViewModelLocator()
{
//Fix to keep blend happy
if (initialized) { return; }
initialized = true;
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<PageInitializeViewModel>();
SimpleIoc.Default.Register<PageHomeViewModel>();
SimpleIoc.Default.Register<PageSelectLanguageViewModel>();
SimpleIoc.Default.Register<WinMessageViewModel>();
SetupNavigation();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public PageInitializeViewModel PageInitializeViewModel => ServiceLocator.Current.GetInstance<PageInitializeViewModel>();
public PageHomeViewModel PageHomeViewModel => ServiceLocator.Current.GetInstance<PageHomeViewModel>();
public WinMessageViewModel WinMessageViewModel=> ServiceLocator.Current.GetInstance<WinMessageViewModel>();
public static void Cleanup()
{
// TODO Clear the ViewModels
}
private void SetupNavigation()
{
var navigationService = new Helpers.NavigationService<Helpers.NavigationPage>();
navigationService.ConfigurePages();
SimpleIoc.Default.Register<Helpers.INavigationService<Helpers.NavigationPage>>(() => navigationService);
}
}
I have go through some of tutorial without MvvmLight:-
Navigating between views in WPF / MVVM
Navigation with MVVM
MVVMTest
Each article use different approach and since I'm not familiar with it, once error line appear and no Intellisense suggestion, I cannot continue to find the solutions.
Is it hard to use MVVM in WPF if I have multiple Page and Window screen? Currently, I have a complete WPF App but it use code-behind. I want to move to MVVM since I've read that MVVM is better than code-behind somewhere. MVVM for single page is not a problem as I have done before and it is totally awesome when using MVVM.
Should I retain to use code-behind in WPF if navigation is almost impossible to have workable answer?
Start simple and set navigationservice, viewmodellocators and everything but the basics to one side for now.
I would avoid all those ....locator classes anyhow. They necessarily rely on an anti pattern IMO.
Just use viewmodel first and a single window app.
The basic pattern involves a MainWindow, the Datacontext of which is MainWindowViewModel.
You might want a menu or some such but the part you will switch out is the content of a contentcontrol.
Bind the Content property of your ContentControl to a public object property on mainwindowviewmodel. Call that CurrentViewModel for the sake of discussion.
Define a viewmodel and usercontrol per view you will switch between. Thus HomeView and HomeViewModel, LoginView and LoginViewModel. And so on.
In a resource dictionary, create a datatemplate for each view associating your usercontrol with the type of it's viewmodel.
Merge this resource dictionary in app.xaml.
To navigate.
Instantiate a new viewmodel of the sort you need.
Set CurrentViewModel to that instance.
It will then be templated into UI.
There are numerous variations of this - it's called viewmodel first and you should be able to easily google a few examples.
Here's one I wrote for a slightly different purpose:
https://social.technet.microsoft.com/wiki/contents/articles/52485.wpf-tips-and-tricks-using-contentcontrol-instead-of-frame-and-page-for-navigation.aspx
You can use a similar approach with pages if you really want pages:
A simplified view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:Page1ViewModel}">
<local:Page1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Page2ViewModel}">
<local:Page2/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ViewSelect"
ItemsSource="{Binding ViewChoices}"
SelectedItem="{Binding SelectedViewChoice, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Frame Grid.Column="1"
Content="{Binding SelectedViewChoice.VM}"
NavigationUIVisibility="Hidden"
/>
</Grid>
</Window>
The very much simplified viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<ViewChoice> ViewChoices { get; set; }
= new ObservableCollection<ViewChoice>
{
new ViewChoice{ Name="Page One", VM=new Page1ViewModel()},
new ViewChoice{ Name="Page Two", VM=new Page2ViewModel()},
};
private ViewChoice selectedViewChoice;
public ViewChoice SelectedViewChoice
{
get { return selectedViewChoice; }
set { selectedViewChoice = value; RaisePropertyChanged(); }
}
ps
If you decide to learn prism I would start with delegatecommand and stop there until you have written at least one wpf app.
There's a HUGE slew of functionality in PRISM and most apps don't actually benefit from regions and dynamic composition.
If you prefer mvvmlight ( I do ) then for core you're best getting the source code and using that. You want commandWPF namespace and this has a reliance on net old in the nuget package. The version that does not will not support command canexecute requery well.
I hope that's sufficient info without being overwhelming. Tricky to both be clear and not drown someone with info.
I am beginner and have an issue in setting a WPF project and following MVVM pattern; I do not see how to link the view to the viewmodel with the organization below :
I have set 3 folders : Model, View and ViewModel, both at the root of the project named "Company.App.UI".
The App.xaml and MainWindow.xaml are at the root of the project.
Starting with this, I want control the content displayed in the client area of the MainWindow by :
- having the rendered views in the folder 'View' as UserControls, for example 'LoginView.xaml'
- having the corresponding view model in the folder 'ViewModel', for example 'LoginView.xaml.cs'
Then what I did in MainWindow.xaml is :
<Window x:Class="Company.App.UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodel="clr-namespace:Company.App.UI.ViewModel"
xmlns:view="clr-namespace:Company.App.UI.View" <!-- does not work, not a namespace -->
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type viewmodel:LoginViewModel}">
<view:LoginView/> <!-- does not work -->
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<ContentControl Content="{Binding ClientArea}"/>
</StackPanel>
</Grid>
</Window>
And in MainWindow.xaml.cs :
using System.Windows;
using System.Windows.Controls;
using Company.App.UI.ViewModel;
namespace Company.App.UI
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private UserControl _ClientArea = null;
public UserControl ClientArea
{
get { return _ClientArea; }
set { _ClientArea = value; }
}
public MainWindow()
{
if (_ClientArea == null) { ClientArea = new LoginViewModel(); }
InitializeComponent();
}
}
}
The LoginView is a simple UserControl with one Label just to see it is what it is.
If I put my LoginView.xaml at the root of the project, next to MainWindow.xaml, it works ...
What am I doing wrong / missing ?
I do not want to use any frameworks (PRISM and so on) for getting this to work.
My apologies if my post is a duplicate but I have also fail to find it while searching.
Thanks,
Update
I use VS2013 with 0 updates / patches / etc.
Everything is in the same project.
The errors output is :
The type or namespace name 'View' does not exist in the
namespace 'Company.App.UI' (are you missing an assembly reference?)
The name "LoginView" does not exist in the namespace
"clr-namespace:Company.App.UI.View".
The type 'view:LoginView' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
LoginView.xaml :
<UserControl x:Class="Company.App.UI.ViewModel.LoginViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label>User control login</Label>
</Grid>
</UserControl>
LoginViewModel.cs :
using System.Windows.Controls;
namespace Company.App.UI.ViewModel
{
public partial class LoginViewModel : UserControl
{
public LoginViewModel()
{
}
}
}
In LoginView.xaml change this:
x:Class="Company.App.UI.ViewModel.LoginViewModel"
to this
x:Class="Company.App.UI.ViewModel.LoginView"
because this is a control not a ViewModel
Also, this is how the LoginView.xaml.cs should look like (didn't see your implementation):
using System.Windows.Controls;
namespace Company.App.UI.View
{
/// <summary>
/// Interaction logic for LoginView.xaml
/// </summary>
public partial class LoginView : UserControl
{
public LoginView()
{
InitializeComponent();
}
}
}
when you will get the hang of it (mvvm) I would recommend using the mvvm light toolkit for the plumbing (there is no need to reinvent the wheel)
Exactly .... Do whatever changes Igor has told you .
along with that ,
Change your MainWindow.xaml.cs
if (_ClientArea == null) { ClientArea = new LoginViewModel(); }
to
if (_ClientArea == null) { ClientArea = new LoginView(); }
Also as per my understanding you just want to display one lable from user control to main window and want to study MVVM concept. So here is the explaination for your example which may help you
<Grid>
<!--connect to viewmodel-->
<Grid.DataContext>
<viewmodel:LoginViewModel></viewmodel:LoginViewModel>
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!--import user control-->
<view:LoginView Grid.Row="0"></view:LoginView>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<ContentControl Content="{Binding ClientArea}"/>
</StackPanel>
</Grid>
Note - Try to keep zero code in any of code behind . Thats the main
purpose of MVVM . It should just have
Model ...(class file which should have purely just proprties )
View ...(usercontrols, xaml,window file which should contain just
xaml code with zero code behind)
Modelview... (class file which should contain purely connection
between view and model, which should not contain any object of
view or model.It connects through the binding)
also i dont know for what purpose you created 'ClientArea' ... Did you define its content somewhere?
Let me know if you need any help... I have some sample demo projects on MVVM.
Post with similar purpose that also helped me to get things :
Binding a ContentControl to UserControl, and reuse same instance,
with the excellent topic within here : How to preserve the full state of the View when navigating between Views in an MVVM application?.
Another good point to start I found after I post my question (...) : https://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
Basically what I want to achieve is to manage the content area within one single window and whithout any framework and more precisely manage "transactions", that is switching from one screen to another upon user interaction.
Thanks for all the comments, things are getting clearer.
Project: c#, wpf, .net4.0, Gmap.Net.Presentation 1.7.1.
What I have:
My custom MapControl class inherited from GMap.NET.WindowsPresentation.GMapControl class.
public sealed class MapControl : GMapControl
{
/* Some special data. */
public MapConrol()
: base()
{
/* Some init actions. */
}
/* Overrided and additional methods. */
}
And, for example, I have some custom UserControl class.
Code:
public sealed partial class MapObjectMarkerUiControl : UserControl
{
/* Some special data. */
public MapObjectMarkerUiControl()
{
/* Some init actions. */
}
/* Overrided and additional methods. */
}
Xaml:
<UserControl x:Class="MapCustomControls.MapObjectMarkerUiControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Width="40" Height="40" RenderTransformOrigin="0.5, 0.5">
<Grid>
<!-- Some visual controls: text, buttons, etc. -->
</Grid>
</UserControl>
Example of custom user control:
What I need:
Is there any way to add it to the map with reference to the geo coordinates? Something like this one:
gmap.AddCustomUserControl(UserControl customMarker, double latitude, double longitude);
May be I should to inherit my UserControl from other class or implement some Gmap.NET interface that allow to add my widget to the map.
Any advice, tips, help?
P.S. If I solve this problem, I will post it here. I guess it will very helpful for others.
Also, I found a lot of questions about GMap on StackOverflow and so on, and everywhere I saw Overlay class.
GMapOverlay markersOverlay = new GMapOverlay("markers");
gmap.Overlays.Add(markersOverlay);
In my verstion I haven't this one. I have already exist built-in marker overlay into GMap class.
gmap.Markers - ObservableCollection of the GMapMarkers.
And there is no way to create my own overlays and add it to GMapControl object.
UPDATE 0:
First idea in my head. Just add GMapMarkers on the map with some special tag by id of map object, for example. And OnRender() of the GMapControl find all the markers on the screen, parse their ids and draw above my wpf UserControls. But I hope there is some internal mechanics in the GMapControl for that.
I believe the solution is easier than what you've tried, you can derive from GMapMarker, e.g.:
class CustomMarker: GMapMarker
{
public string Description { get; set; }
public CustomMarker(PointLatLng pos, string description)
: base(pos)
{
Description = description;
Shape = new CustomMarkerElement();
((CustomMarkerElement)Shape).lblDesc.Content = description;
}
}
This marker instance gives you access to the UI properties within an own CustomerMarkerElement (an UserControl within the project):
<UserControl x:Class="WpfApplication3.CustomMarkerElement"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Ellipse
Fill="DarkKhaki"
Height="40"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="40" />
<Label
x:Name="lblDesc"
Content="TST"
Foreground="White"
FontWeight="Bold"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
Margin="0,6"
VerticalAlignment="Top"
Width="40"/>
</Grid>
</UserControl>
The downside is that afaik there is no way to have this in an MVVM conform way (e.g. define the custom markers within an item template).
I'm building application with multiple modules. Each of module contains a viewmodel and view.
I would like to create solution, for automatically show SelectedModule in a part of my application.
Assume that I have MainWindow view like this:
<catel:UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com" xmlns:dxsch="http://schemas.devexpress.com/winfx/2008/xaml/scheduler"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
x:Class="OrchestraCatel.Views.MainWindow">
<DockPanel LastChildFill="True">
<ContentControl x:Name="Ribbon" DockPanel.Dock="Top">
<!-- My Ribbon-->
</ContentControl>
<ContentControl x:Name="MainView" DockPanel.Dock="Top">
<!-- My Main Page-->
</ContentControl>
</DockPanel>
</catel:UserControl>
And I would like to show a UserControl specified in another DLL inside MainView ContentControl after Ribbon item click.
Both, Ribbon Button and MainView view are in module DLL
I had an idea to include SelectedViewModel inside my MainPage ViewModel, and then, create DataTemplates for each ViewModel, but this solution will break my vision of Modules, which should be independent.
Is there a way in Catel to replace a view with another one? From anywhere?
I think Prism solves this issue for you. Please take a look at the integration of Prism with Catel:
https://catelproject.atlassian.net/wiki/display/CTL/Catel.Extensions.Prism
Let's say I have a user control test1.xaml and has a frame control name frame1. On my second user control, how do I reference the test1.xaml in my test2.xaml in order to manipulate the property of the control in test2.xaml.cs? Because I know that test1 test = new test1(); will not work because im not instantiating it and not referencing it. May I ask how?
In the MVVM approach both views / user controls could use the same view model that they are data bound to. Now when the first control causes a value change in one of the properties exposed by that viewmodel this is automatically reflected in the second user control.
Ok. I will not write the code with DependencyProperties because It smells. I will write instead a simple code which does such stuff using MVVM. But I want to note that you must read an article "WPF Apps With The Model-View-ViewModel Design Pattern" by Josh Smith.
Here is a simple code which contains one main window and two user controls Test1 and Test2. And only one ViewModel - GodViewModel, which is the viewModel for both Test1 and Test2. In fact, as a rule there are 1-1 mapping between ViewModel and View. I created only one ViewModel for the simplicity.
The window code:
<Window x:Class="WpfApplication99.MainWindow"
x:Name="GodWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:WpfApplication99.View"
Title="MainWindow"
xmlns:local="clr-namespace:WpfApplication99"
DataContext="{Binding Vm, ElementName=GodWindow}">
<StackPanel>
<view:Test1 />
<view:Test2 />
</StackPanel>
</Window>
public partial class MainWindow : Window
{
ViewModel.GodViewModel _vm = new ViewModel.GodViewModel();
public ViewModel.GodViewModel Vm
{
get { return _vm; }
set { _vm = value; }
}
public MainWindow()
{
InitializeComponent();
}
}
The ViewModel code:
namespace WpfApplication99.ViewModel
{
public class GodViewModel
{
public string Text { get; set; }
}
}
test1 code (code behind is empty):
<UserControl x:Class="WpfApplication99.View.Test1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="45" d:DesignWidth="167">
<Button Content="{Binding Text}"
Height="26"
Name="button1"
Width="144" />
</UserControl>
test2 code (code behind is empty):
<UserControl x:Class="WpfApplication99.View.Test2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" >
<TextBox Text="{Binding Text}" Height="69" Width="232" />
</UserControl>
Using this code you can use the same property Text in both Test1 and Test2. In your question, you wrote that you had a property in test1 and want to use it in test2. So imagine that the solution provided just put the test1's one property Text into GodViewModel.
Perhaps, you want to use it in the code behind. In this case, you should create a separate ViewModel for test1 and test2 user controls. I'm not able to describe all details there. So, please read the article. I'm sure that MVVM pattern is a key thing in WPF.
You can add DependencyProperty of type YourViewModelBase to your test2. Than write some in the place you create an instance of your controls. Of course, if you use MVVM. However, as far as I understand, you should not do things like that according to MVVM.
if you don't have YourViewModelBase, you may create an abstract class for your test1 with necessary properties or just pass test1 as UserControl and then try to cast it to test1 in your test2 code.