I am trying to run a simple application based on catel 4 but I didn't even show the window. I don't have errors or warnings
Here is the code of the test application and the application itself.
<catel:DataWindow x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:catel="http://catel.codeplex.com"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox Content="Check me to continue" IsChecked="{Binding UserAgreedToContinue, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
</Grid>
public partial class MainWindow : DataWindow
{
public MainWindow()
: base(DataWindowMode.Custom)
{
InitializeComponent();
}
}
public class MainWindowViewModel : ViewModelBase
{
public override string Title { get { return "Just acknowledge"; } }
public bool UserAgreedToContinue
{
get { return GetValue<bool>(UserAgreedToContinueProperty); }
set { SetValue(UserAgreedToContinueProperty, value); }
}
public static readonly PropertyData UserAgreedToContinueProperty = RegisterProperty("UserAgreedToContinue", typeof(bool));
}
What am I doing wrong? Why does not even start the window?
https://www.dropbox.com/s/qjf1khq10y606ql/WpfApplication1.zip?dl=0
Enable debug log listener:
public App()
{
#if DEBUG
LogManager.AddDebugListener();
#endif
}
It immediately shows you what's wrong:
The view model of the view 'WpfApplication1.MainWindow' could not be
resolved. Make sure to customize the IViewModelLocator or register the
view and view model manually
This is because you don't have the namespaces that are usually used in MVVM. You can of course customize everything but since you are a beginner, I recommend the following actions:
Move view model to WpfApplication1.ViewModels namespace
Move view to WpfApplication1.Views namespace (both xaml and code-behind)
Change startup uri in App.xaml to "/Views/MainWindow.xaml"
I also strongly recommend that you read the getting started guide.
Related
I'm pretty new in MvvmCross but I'm working with mvvm for awhile. I know how to compose usercontrols with nested usercontrols. Now with mvvmcross I got stucked to show two or more usercontrols in another usercontrol. I don't use any other framework than MvvmCross.
My Rootview looks like this:
`<views:MvxWpfView
x:Class="MvvmCrossTest.Wpf.Views.RootView"
xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf"
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="450" d:DesignWidth="800">
<DockPanel>
<TextBlock Text="Root" DockPanel.Dock="Top"/>
<ContentControl x:Name="MainMenuVM" Content="{Binding MainMenuVM}" DockPanel.Dock="Top" />
</DockPanel>
</views:MvxWpfView>`
The corresponding ViewModel looks like this:
using MvvmCross.Commands;
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTest.Core.ViewModels
{
public class RootViewModel: MvxNavigationViewModel
{
private readonly IMvxViewModelLoader _mvxViewModelLoader;
public RootViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService, IMvxViewModelLoader mvxViewModelLoader) : base(logProvider, navigationService)
{
_mvxViewModelLoader = mvxViewModelLoader;
ShowMainMenu();
}
private MainMenuViewModel _mainMenuVM;
public MainMenuViewModel MainMenuVM
{
get { return _mainMenuVM; }
set
{
SetProperty(ref _mainMenuVM, value);
RaisePropertyChanged(() => MainMenuVM);
}
}
public MvxCommand ShowMainMenuCommand { get; set; }
public void ShowMainMenu()
{
MainMenuVM = (MainMenuViewModel)_mvxViewModelLoader.LoadViewModel(MvxViewModelRequest.GetDefaultRequest(typeof(MainMenuViewModel)), null, null);
}
}
}
The simplified View I want to show in the contentcontrol looks like this:
<views:MvxWpfView
x:Class="MvvmCrossTest.Wpf.Views.MainMenuView"
xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf"
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"
Background="Aqua"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</views:MvxWpfView>
And also the corresponding ViewModel.
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTest.Core.ViewModels
{
public class MainMenuViewModel : MvxNavigationViewModel
{
public MainMenuViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
}
}
}
Instead of the View/ViewModel I see a text that says "MvvmCrossTest.Core.ViewModels.MainMenuViewModel".
I don't want to use Xamarin!
I created a project where I show you some basics.
simple
You can find the samples here.
the RootView;
<views:MvxWpfView xmlns:local="clr-namespace:SomeProject.Views" ...>
<Grid>
<Label Content="Hello from RootView" />
<local:NestedView />
</Grid>
</views:MvxWpfView>
the NestedView;
<views:MvxWpfView ...>
<Grid>
<Label Content="Hello from NestedView" />
</Grid>
</views:MvxWpfView>
extended (custom presenter)
With this custom wpf presenter you can easily navigate to views while specified themn in a container. With that, you can close and open views from the view model while using the mvvmcross navigation service.
I copied this from another project. You can find the whole implementation in my sample project here.
the RootView;
<views:MvxWpfView ...>
<Grid>
<Label Content="Hello" />
<ItemsControl region:MvxContainer.Id="RootViewRegion"/>
</Grid>
</views:MvxWpfView>
the RootView code behind;
[MvxContentPagePresentation(WrapInNavigationPage = true, NoHistory = false)]
public partial class RootView: MvxWpfView<RootViewModel>
{
public RootView()
{
InitializeComponent();
}
}
the RootViewModel;
public class RootViewModel: MvxNavigationViewModel
{
private readonly IMvxNavigationService _navigationService;
public IMvxAsyncCommand ShowNestedViewModelCommand { get; protected set; }
public RootViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
this._navigationService = navigationService;
// navigate to nested view
this.ShowNestedViewModelCommand = new MvxAsyncCommand(() => this._navigationService.Navigate<NestedViewModel>());
ShowNestedViewModelCommand.Execute();
}
}
the NestedView code behind;
[MvxWpfPresenter("RootViewRegion", mvxViewPosition.NewOrExsist)]
public partial class NestedView : MvxWpfView<NestedViewModel>
{
public NestedView()
{
InitializeComponent();
}
}
good to know
when you have problems with navigation and view model connection use this code behind. It loads the viewmodel for the specific view;
[MvxContentPagePresentation(WrapInNavigationPage = true, NoHistory = false)]
public partial class SomeView : MvxWpfView<ViewModels.SomeViewModel>
{
public SomeView()
{
InitializeComponent();
if (!(ViewModel is ViewModels.SomeViewModel))
{
if (Mvx.IoCProvider.TryResolve<ViewModels.SomeViewModel>(out var someViewModel))
{
ViewModel = someViewModel;
return;
}
var _viewModelLoader = Mvx.IoCProvider.Resolve<IMvxViewModelLoader>();
var request = new MvxViewModelInstanceRequest(typeof(ViewModels.SomeViewModel));
request.ViewModelInstance = _viewModelLoader.LoadViewModel(request, null);
ViewModel = request.ViewModelInstance as ViewModels.SomeViewModel;
Mvx.IoCProvider.RegisterSingleton<ViewModels.SomeViewModel>(ViewModel);
}
}
}
it seems that MvvmCross is wiring up Views and ViewModels automatically
I know nothing about MvvmCross, but for "automatically" to work there should be an agreement and rules, e.g. attributes or matching names (xViewModel <-> xView). Currently there is nothing I can see what connects MainMenuViewModel with views:MvxWpfView.
Anyway what you see
I see a text that says "MvvmCrossTest.Core.ViewModels.MainMenuViewModel"
is the view trying to visualize non-control as text due to missing data template.
It's easy to fix by just adding data template
<DataTemplate DataType="{x:Type local:MainMenuViewModel}">
<views:MvxWpfView />
</DataTemplate>
somewhere (e.g. into views:MvxWpfView.Resources) and the view will be displayed.
I am trying to set up a navigation between views using a MVVM pattern. My application contains a MainWindow and two views with a button each. When I click on the button in the View1 I want to set up the View2 on the MainWindow.
I have found several tutorials witch explain how to navigate from a view to another with a button on the main window (simulate a tabControl), it works but it is not what I want.
I'm looking for something like :
View1_View.xaml.cs :
public partial class View1_View : UserControl
{
private View1_ViewModel _viewModel = new View1_ViewModel();
public View1_View()
{
InitializeComponent();
}
private void Btn_SwitchToView2_Click(object sender, RoutedEventArgs e)
{
MainWindow.SwitchToView2();
}
}
MainWindow.xaml.cs :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new View1_View();
}
public void SwitchToView2()
{
this.DataContext = new View2_View();
}
}
My problem is if I do so, from the class View1_View I cannot access to the method SwitchToView2() if it is not static, and if it is static I lose the context of the MainWindow.
How should I proceed ?
Thanks.
I would recommend using a ContentControl to switch the part of your main view.
This could look like this (short form just to give you an idea; without INotifyPropertyChanged).
Create an empty interface of type ISwitchableViewModel.
Add a property to your main ViewModel
public property ISwitchableViewModel MyViewModel {get; set;}
Create two classes that implements the interface ISwitchableViewModel. Each for each view you want to show (View1 and View2 in your example) and call them ViewModel1 and ViewModel2.
When you press the button in your xaml set the MyViewModel to View1 or View2; whatever your logic is.
In your xaml add this at the place where you want to show the switchable content.
<ContentControl Content="{Binding MyViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewModel:ViewModel1}">
<view:View1 />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ViewModel2}">
<view:View2 />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
When you set the MyViewModel in your MainViewModelthe UI will show automatically the correct view for that viewmodel.
You can achieve this by creating the views and assigning them to a content control.
Lets assume you have this content control in your main view.
<Window x:Class="MVVM.MainWindow"
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"
xmlns:local="clr-namespace:MVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button x:Name="ChangeView" Click="SwitchToSecondView" Content="Set View"></Button>
<ContentControl x:Name="MainContent"></ContentControl>
</StackPanel>
</Window>
You can then set the content in the code behind file of your main view.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void SwitchToSecondView(object sender, outedEventArgs e)
{
var view = new SecondView();
var model = new SecondViewModel(this);
view.DataContext = model;
MainContent.Content = view;
}
public void SwitchToThirdView(object sender, outedEventArgs e)
{
var view = new ThirdView();
var model = new ThirdViewModel(this);
view.DataContext = model;
MainContent.Content = view;
}
}
Another solution would be to use an MVVM Framework light Caliburn.Micro, Prism etc, which essential do the same thing as the code snippet above, but hide the boilerplate code.
EDIT: I realized i didn't explicitly get to the second part of your question.
Usally one would need some kind of router which is able to control the navigation. For the sake of simplicity we use the main view as router. To access the main view, you need to inject it in each component.
This allows each of your submodels to access the main view.
This could be improved by using some kind of DI-Container or by a Mediator. A mediator would allow each component to send requests, which then are dispatched to the MainView, eliminating the direct dependency.
I have an interface called
ISquare
There is a usercontrol MatrixGrid which has a public property
public List<List<ISquare>> Squares
{
get { return (List<List<ISquare>>)GetValue(SquaresProperty); }
set { SetValue(SquaresProperty);}
}
and I am able to bind to it in the designer for this usercontrol without any issue.
If I place a MatrixGrid in a window and try to bind to the Squares property, the designer throws an error
The member "Squares" is not recognized or accessible
and the Gui part of the designer shows
Invalid Markup, check the error list for more information
Running the program, all the bindings work properly and everything behaves without problems.
Is this a bug in the designer or is there a rule written down somewhere about this?
I'm running Visual Studio Express 2012 For Windows Desktop
I was able to duplicate it with the following code
TestControl.xaml.cs
public partial class TestControl : UserControl
{
public TestControl()
{
InitializeComponent();
}
public static DependencyProperty
TestProperty = DependencyProperty.Register("MyTest", typeof(List<List<Test>>), typeof(TestControl));
public List<List<Test>> MyTest { get { return (List<List<Test>>)GetValue(TestProperty); } set { SetValue(TestProperty, value); } }
}
public interface Test {}
TestWindow.xaml
<Window x:Class="simthing.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:simthing"
Title="TestWindow" Height="300" Width="300">
<Grid>
<local:TestControl MyTest="{Binding Tests}"/>
</Grid>
</Window>
Im having problem sharing a windows viewmodel to the windows hosted frame.
Therefore I made a static viewmodel for the mainwindow, so any class can edit it´s properties:
class GUICollection
{
public static MainWindowViewModel MainWindowViewModel = new MainWindowViewModel();
}
This is then set into the MainWindows datacontext:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = GUICollection.MainWindowViewModel;
}
}
This is the windows xaml:
<Window x:Class="MVVMFrameQuestiontoStackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Frame NavigationUIVisibility="Hidden" Source="{Binding MainWindow.FrameURI}"/>
</Grid>
It´s view model:
class MainWindowViewModel
{
private string startUpURI;
private object startUpDataContext;
private MainWindowModel mainWindow;
public MainWindowViewModel()
{
startUpURI = "pack://application:,,,/MVVMFrameQuestiontoStackOverflow;component/Page1.xaml";
mainWindow = new MainWindowModel(startUpURI);
}
/// <summary>
/// Gets the MainWindow instance
/// </summary>
public MainWindowModel MainWindow
{
get
{
return mainWindow;
}
}
}
So from here I can choose the frame Source, which means I can choose which view to show. However Im wondering if I could avoid the static initiliazing and still being able to access the mainwindows FrameURI property (Here is my current logic):
public Page1()
{
InitializeComponent();
DataContext = new MainMenuViewModel();
//Statement below causes an exception, but the whole issue is about accesing this without using a static instance.
GUICollection.MainWindowViewModel.MainWindow.FrameURI = "Change MainWindows FrameURI property!";
}
Is the same behaviour able to produce without using a static class? If so an example would be warmly appreciated.
Thanks on advance!
I think in fact you problem is that you haven't understood MVVM and use a mix between MVC and MVVM : You create the view (mainWindows) in the ViewModel wich is forbidden.
Your App must load the main view.
In the constructor of the main view you should create the view-model as a private field.
When you will create new windows (and you should only do that from view, never from viewmodel) you will give the viewmodel datacontext object as a parameter for the new view, wich will give it to it's own viewmodel via parameters.
If you have Model object(s) wich is(are) shared throught all the application, create it in the App launch method, and pass it throught views via their constructors as a parameter.
I'm very new to WPF and a beginner in C#.NET. I'm currently making an application where there will be many pages and the trigger to change the page is hand gesture using Kinect SDK (the trigger method is not relevant for this question). Normally when a WPF file is created, there will be a similarly named .cs file attached to it, which acts somewhat like a controller. However, I need multiple WPF files/pages to be controlled only by a single controller .cs file. How do I achieve that? Thanks for viewing my question and your answer will be very appreciated :)
You probably want to write a class that contains your 'controller' code and reference it from your WPF UserControls / Pages.
In a new file:
public class MyController
{
public void DoThings(object parameter)
{
// stuff you want to do
}
}
and then inside your UserControl code-behind class:
public partial class MyWpfControl : UserControl
{
private MyController controller;
public MyWpfControl
{
this.controller = new MyController();
}
}
and finally, tie your events back to the controller's method:
private void OnGesture(object sender, EventArgs e)
{
// call the method on the controller, and pass whatever parameters you need...
this.controller.DoThings(e);
}
The code behind is really part of the view and isn't really analogous to a controller and generally there shouldn't be much code in them. Typically you would want most of your logic between your "View Model" which serves as an abstraction of the view and "Model" which serves as an abstraction of the business logic that your UI is interacting with.
In this light what I think you really want is a View Model(VM) that controls multiple views. This is a fairly typical scenario and the preferred method (IMO anyway) is to have a hierarchical view model that has a top level the application model and a number of sub VMs that represent different components within your UI, though you can bind everything to your top level VM if you really want to.
To do this we would first define our view model like so
public interface IGestureSink
{
void DoGesture();
}
public class MyControlVM : INotifyPropertyChanged, IGestureSink
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ApplicationVM parent;
public MyControlVM(ApplicationVM parent)
{
this.Name = "my user control";
this.parent = parent;
parent.PropertyChanged += (s, o) => PropertyChanged(this, new PropertyChangedEventArgs("Visible"));
}
public String Name { get; set; }
public bool Visible { get { return parent.ControlVisible; } }
public void DoGesture()
{
parent.DoGesture();
}
}
public class ApplicationVM : INotifyPropertyChanged, IGestureSink
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public ApplicationVM()
{
this.ControlVM = new MyControlVM(this);
this.ControlVisible = false;
}
public MyControlVM ControlVM { get; private set; }
public bool ControlVisible {get; set;}
public void DoGesture()
{
this.ControlVisible = !this.ControlVisible;
PropertyChanged(this, new PropertyChangedEventArgs("ControlVisible"));
}
}
and then all we need to do is to build a user control
<UserControl x:Class="WpfApplication2.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="LightBlue">
<Label Content="{Binding Name}"/>
</Grid>
</UserControl>
and page
<Window xmlns:my="clr-namespace:WpfApplication2" x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<my:MyControl Width="200" Height="200" x:Name="myUserControl" DataContext="{Binding ControlVM}" Visibility="{Binding Visible,Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="222,262,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</Window>
That use it. The only thing that we need in our code behind is a constructor that sets up the page VM and wiring from our button to the view model.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ApplicationVM();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
((IGestureSink)(this.DataContext)).DoGesture();
}
}
If you wanted to use a monolithic view model instead you would use this Instead of binding the DataContext to ControlVM:
<my:MyControl Width="200" Height="200" x:Name="myUserControl" DataContext="{Binding DataContext}" Visibility="{Binding ControlVisible,Converter={StaticResource BooleanToVisibilityConverter}}"/>