Separating properly the view and the viewmodel - c#

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.

Related

Telerik RadSyntaxEditor does not load or highlight text

I am Trying to use the new RadSyntaxEditor from Telerik by following this guide.
This is the code I created:
private RadSyntaxEditor _syntaxEditor;
public RadSyntaxEditor SyntaxEditor
{
get => _syntaxEditor;
set
{
if (Equals(value, _syntaxEditor)) return;
_syntaxEditor = value;
OnPropertyChanged();
}
}
public CodeEditorViewModel()
{
SyntaxEditor = new RadSyntaxEditor();
}
public void Test()
{
using (StreamReader reader = new StreamReader("../../ViewModels/ShellViewModel.cs", Encoding.UTF8))
{
SyntaxEditor.Document = new TextDocument(reader);
}
var cSharpTagger = new CSharpTagger(SyntaxEditor);
SyntaxEditor.TaggersRegistry.RegisterTagger(cSharpTagger);
}
my xaml file:
<UserControl x:Class="CodeEditorControl.Views.CodeEditorView"
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"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="Test" Grid.Row="0">Test</Button>
<telerik:RadSyntaxEditor x:Name="SyntaxEditor" Grid.Row="1"/>
</Grid>
</UserControl>
The control is showing without a problem and is responding to input etc.
But neihter does the document load, nor is there any syntax highlighting.
The Reader loads correct and ReadToEnd() outputs the correct text (ShellViewModel is just a standard cs file with 36 lines).
I am using caliburn.micro and the MVVM design.
Edit: I set up a project with the same template but using code behind instead of binding. This works as intended. So the problem lies within the binding from caliburn.micro and telerik.
Any help is appreciated.
I've noticed that the property in CodeEditorViewModel is of type RadSyntaxEditor and the corresponding UI element is also RadSytanxEditor. Note that this produces a binding error in the Output pane of Visual Studio. I think that the Caliburn.Micro binding engine cannot create this type of relation and currently there are two separate instances of RadSyntaxEditor. The one defined in XAML and the other one defined in the view model. The document is loaded to the one defined in code, but because it is never used in the UI, there is nothing in the application.
To resolve this you can research the Caliburn.Micro framework and more specifically, how to use the naming conventions to data bind the model property to a corresponding property of the UI element. I think the current binding (via the convention) defaults to the Visibility property of RadSyntaxEditor.
Or you can simply use an explicit data binding like this:
<Button x:Name="Test" Grid.Row="0">Test</Button>
<ContentControl Content="{Binding SyntaxEditor}" Grid.Row="1"/>
Note that I've replaced the RadSyntaxEditor control with a ContentControl.

How to design the ShellView with Caliburn Micro?

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>

InitializeComponent "doesn't exist" and "no definition" for XAML map controls

I am working on a simple map app that will zoom into and follow my location using GeoCoordinateWatcher. The problem is, whenever I finish putting everything into place, InitializeComponents() always throws an exception and my C# code won`t recognize my XAML map controls. Is this some kind of bug or am I just a total fool?
CODE SAMPLE:
public MainPage()
{
this.InitializeComponent(); // This whole line is underlined red
this.NavigationCacheMode = NavigationCacheMode.Required;
}
public void CenterUserLocation()
{
// Center MyMap on user location
this.MyMap.Center = myPoint; //MyMap is underlined red
this.MyMap.ZoomLevel = 10; //MyMap is underlined red
}
UPDATE: (Class Definitions C#)
namespace MapApp{
public sealed partial class MainPage : Page
{
GeoCoordinateWatcher watcher;
private Geopoint myPoint;
UPDATE: (XAML)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MapApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Maps="using:Windows.UI.Xaml.Controls.Maps"
x:Class="MapApp.MainPage"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.BottomAppBar>
<CommandBar>
<!-- LOCATION BUTTON -->
<AppBarButton x:Uid="LocateAppBarButton"
x:Name="LocateAppBarButton"
Label="location"
Icon="Map"
Click="LocateAppBarButton_Click" />
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Maps:MapControl x:Name="MyMap" HorizontalAlignment="Left" VerticalAlignment="Top" Height="580" Width="400"/>
</Grid>
</Page>
Your XAML and code-behind should be something like this:
XAML:
<Page x:Class="MyNamespace.MyCanvasCodeInline">
...
Code-behind:
namespace MyNamespace
{
public partial class MyCanvasCodeInLine // : Page - not needed but implied
...
Things you should check:
The code-behind class is defined as partial
The code-behind class qualified name (namespace + class name) is the same as the x:Class property of the XAML root node
The XAML root node is some class that implements IComponentConnector (Page, Window, UserControl, etc.)
Another way to do this is checking the xaml.cs file that gets generated "under" the XAML file, and compare its partial class definition with your code-behind class. They should match types, name and namespace.
Make sure that your XAML file has the correct Build Action (should be Page). I've seen Visual Studio change this when copy/pasting XAML files between projects.
If this isn't correct, you will get this error even though your class names and namespaces match between your XAML and code-behind.

How do I load controls in different ContentControls of a Shell using CaliburnMicro

By default when you use "ActivateItem(new Control());" your control is loaded into a ContentControl which with the name ActiveItem, fro example. . If I have multiple content controls on my page how would I load controls into them whilst retaining the ability to use the default functionality of being able to load controls into the the active item control.
for example I want to have a login control to be loaded into the Login ContentControl, and when a user successfully login I want a new control to be loaded into the ActiveItem ContentControl.
Thanx in advance.
If the ViewModel that gets binded to the UI contains a property with the name that matches a content control. The Content control view automatically gets resolved the the view supported by this property, provided this property itself is a ViewModel type and has been registed with Ioc container. For example
<ContentControl x:Name="LoginStatus"></ContentControl>
If there is a property LoginStatus on the main ViewModel (LoginStatus property itself is a ViewModel). The content control would correctly get rendered with the appropriate view.
This is an old question, but in case anyone is having the same issue, here is my solution:
Your main window that contain both (or even more than two) of your User Controls must be inherited from Caliburn.Micro.Conductor<Screen>.Collection.AllActive;
Your User Controls must be inherited from Caliburn.Micro.Screen;
You must also keep naming conventions in mind. If you use MenuUC as the name of a ContentControl in your View, also create a property named MenuUC in your ViewModel;
Initialize your UserControl as I do in Constructor;
Now you can use ActivateItem(MenuUC) and DeactivateItem(MenuUC) everywhere in your code. Caliburn.Micro automatically detects which one you want to work with.
Example XAML View code:
<Window x:Class="YourProject.Views.YourView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="YourViewTitle" Width="900" Height="480">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Menu Side Bar -->
<ContentControl Grid.Row="0" Grid.Column="0" x:Name="MenuUC" />
<!-- Panel -->
<Border Grid.Column="1" Grid.RowSpan="2" BorderThickness="1,0,0,0" BorderBrush="#FF707070" >
<ContentControl x:Name="PanelUC" />
</Border>
</Grid>
</Window>
Example C# ViewModel code:
class YourViewModel : Conductor<Screen>.Collection.AllActive
{
// Menu Side Bar
private MenuUCViewModel _menuUC;
public MenuUCViewModel MenuUC
{
get { return _menuUC; }
set { _menuUC = value; NotifyOfPropertyChange(() => MenuUC); }
}
// Panel
private Screen _panelUC;
public Screen PanelUC
{
get { return _panelUC; }
set { _panelUC = value; NotifyOfPropertyChange(() => PanelUC); }
}
// Constructor
public YourViewModel()
{
MenuUC = new MenuUCViewModel();
ActivateItem(MenuUC);
PanelUC = new FirstPanelUCViewModel();
ActivateItem(PanelUC);
}
// Some method that changes PanelUC (previously FirstPanelUCViewModel) to SecondPanelUCViewModel
public void ChangePanels()
{
DeactivateItem(PanelUC);
PanelUC = new SecondPanelUCViewModel();
ActivateItem(PanelUC);
}
}
In the above example, ChangePanels() acts as a method to load new User Control into your ContentControl.
Also read this question, it might be help you further.
You should have a look at Screen Conductors. See here.

Why doesnt Window.FindName() discover the x:Name of a button in a child UserControl? AKA how do NameScopes work?

So in the example code below, I create a UserControl UserControldChild which is a child of the main Window, Window1.xaml. Why does the FindName() method fail to find the "myButton" in the code below?
This must have to do with the WPF XAML NameScopes, but I have yet to find a good explanation as to how NameScope works. Can someone enlighten me?
//(xml) Window1.xaml
<Window x:Class="VisualTreeTestApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VisualTreeTestApp="clr-namespace:VisualTreeTestApplication"
Title="Window1" Height="400" Width="400">
<Grid>
<VisualTreeTestApp:UserControlChild/>
</Grid>
</Window>
//(c#) Window1.xaml.cs
namespace VisualTreeTestApplication
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Button btnTest = (Button)Application.Current.MainWindow.FindName("myButton");
// btnTest is null!
}
}
}
UserControl below:
//(wpf) UserControlChild.xaml
<UserControl x:Class="VisualTreeTestApplication.UserControlChild"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid x:Name="myGrid">
<Button x:Name="myButton" Margin="20" >Button</Button>
</Grid>
</UserControl>
//(c#) UserControlChild.xaml.cs (no changes)
namespace VisualTreeTestApplication
{
/// <summary>
/// Interaction logic for UserControlChild.xaml
/// </summary>
public partial class UserControlChild : UserControl
{
public UserControlChild()
{
InitializeComponent();
}
}
}
In case this doesn't get answered properly, I found an alternative to using FindName() documented in the post here.
You are correct - this has to do with XAML Namescopes.
This is (somewhat poorly) documented in the Name related APIs section of the XAML Namescopes page.
Basically, if you have a FrameworkElement or FrameworkContentElement, it will define its own name scope. If you call FindName() on a type that doesn't have a namescope, WPF searches up thet ree until it finds an element that does define a namescope, then searches within that namescope.
In your case, it's searching at Window's namescope (it's a FrameworkContentElement, so it defines its own scope). It just searches elements defined in that scope.
In your case, the button is in the UserControl's namescope, though, so Window.FindName() doesn't find it. There is no automatically searching down the tree into lower level scopes.
This is a good thing - your "Window" shouldn't know or want to know anything about internal details of a UserControl it's using. If you need properties within the UserControl, they should be exposed at the UserControl level - let the control manage its own children.

Categories

Resources