Implementing own ViewModelLocator - c#

I wanted to implement ViewModelLocator by my own. So I implemented the simplest app in the world. I did everything as in this tutorial. But I'm still getting an exception:
XamlParseException occured
Exception thrown: 'System.Windows.Markup.XamlParseException' in
PresentationFramework.dll
Additional information: 'Provide value on
'System.Windows.StaticResourceExtension' threw an exception.' Line
number '8' and line position '9'.
This is this line:
DataContext="{Binding MainWindowViewModel, Source={StaticResource ViewModelLocator}}">
Here is the code:
App.xaml
<Application x:Class="ViewModelLocatorDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModelLocatorDemo="clr-namespace:ViewModelLocatorDemo">
<Application.Resources>
<viewModelLocatorDemo:ViewModelLocator x:Key="ViewModelLocator"/>
</Application.Resources>
</Application>
App.xaml.cs
namespace ViewModelLocatorDemo
{
using System.Windows;
using ViewModelLocatorDemo.Views;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
}
}
ViewModelLocator.cs
namespace ViewModelLocatorDemo
{
using ViewModels;
public class ViewModelLocator
{
public MainWindowViewModel MainWindowViewModel
{
get { return new MainWindowViewModel(); }
}
}
}
MainWindow.xaml
<Window x:Class="ViewModelLocatorDemo.Views.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"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300"
DataContext="{Binding MainWindowViewModel, Source={StaticResource ViewModelLocator}}">
<Grid>
<Frame x:Name="MainFrame" Margin="50" BorderThickness="2" BorderBrush="Black" />
</Grid>
</Window>
MainWindowViewModel.cs
namespace ViewModelLocatorDemo.ViewModels
{
public class MainWindowViewModel
{
public string MainText { get; set; }
public MainWindowViewModel()
{
MainText = "The first page";
}
}
}
In this answer I found:
Make sure that the resources are defined before the usage (in Xaml
parsing order). The easiest way is to place it into App.xaml
So I have it in App.xaml. If somebody would me explain what's going on here? Why am I getting this error?

You are running into this bug WPF - App.xaml file does not get parsed if my app does not set a StartupUri?
From that page:
There's a VS code generation bug where the code necessary to connect to the rest of the program sometimes is not inserted when contains only one entry and does not have a StartupUri attribute.
From that page, there are 3 solutions (summarizing here for completeness):
Add x:Name="App"
Add more resources in App.xaml like <viewModelLocatorDemo:ViewModelLocator x:Key="ViewModelLocator"/> and <viewModelLocatorDemo:ViewModelLocator x:Key="ViewModelLocator2"/>
Rather than overriding OnStartup, try using an event instead, Startup="Application_Startup"
This was definitely not obvious, and was difficult to troubleshoot and even find an answer for in my own search. Hopefully this answer will help others find the other answer.

Related

Generic Window-Classes

I want to have a generic type of a Window.
However, if I implement the <R> into the class definition, it gives me errors, everywhere I reference on the xaml, e.g. at InitializeComponent(); or if I want to access any label or button.
The name 'InitalizeComponent' is not available in the current context
Probably, the reference/linking from the xaml to the code behind does not work properly.
Are there any suggestions, how I can achieve a correct linking to the xaml with generic window classes?
C#
namespace MyNamespace
{
public partial class Designer<R> : Window, IEventListener
where R : Region, new()
{
...
}
}
XAML
<Window
x:Class="MyNamespace.Designer"
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:local="clr-namespace:MyNamespace"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="Designer"
Width="1600"
Height="1000"
mc:Ignorable="d">
...
</Window>
You need to provide x:TypeArguments directive:
<Window
x:Class="MyNamespace.Designer"
x:TypeArguments="src:Region"
...
</Window>

WPF binding, C# Generics and nullable type result in Unhandled Exception in Visual Studio Designer

I am having a problem with the Visual Studio designer for a WPF project and the combination of binding to a type using a generic and specifying a nullable type as the generic type.
I have tried to construct a minimal example of the problem:
XAML:
<Window x:Class="TestWpfApp.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:TestWpfApp"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:TestViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding TestText.Value}"/>
<!--<TextBlock Text="{Binding TestTextValue}"/>-->
</Grid>
</Window>
Code behind:
using System.Windows;
namespace TestWpfApp
{
public class TestGeneric<T>
{
public TestGeneric(T value)
{
Value = value;
}
public T Value { get; }
}
public class TestViewModel
{
public TestGeneric<double?> TestText { get; } = new TestGeneric<double?>(123.456);
public double? TestTextValue => TestText.Value;
}
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = new TestViewModel();
InitializeComponent();
}
}
}
The designer fails with this code with the following error message:
System.Runtime.Remoting.RemotingException
[16040] Designer process terminated unexpectedly!
The commented out line in the XAML code does not give the error in the designer window.
Both versions actually work when running the project. It is only the designer that fails.
Does anyone have any idea about what the problem could be?
The d:DataContext design time expression is a very practical trick, and IDE related, it has no impact on the runtime, only affect the design time. Applicable in Visual Studio 2010 and later.
The default constructor is required for a type to be instantiated in XAML.
The option IsDesignTimeCreatable=True tells the designer that it can create the specified class via default constructor. This way it is possible to provide sample data for the UI.
d:DataContext="{d:DesignInstance local:TestViewModel , IsDesignTimeCreatable=True }"
Yong Lu

DataContext class not found in XAML Designer (while everything looks fine)

I am trying to set my datacontext in the XAML file via
<Window x:Class="LocationScout.SettingsDeleteWindow"
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:ViewModel="clr-namespace:LocationScout.ViewModel"
mc:Ignorable="d"
Title="Delete" Height="315" Width="350"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<ViewModel:SettingsDeleteDisplayItem/>
</Window.DataContext>
The XAML editor, however, shows the error "The name "SettingsDeleteDisplayItem" does not exist in the namespace "clr-namespace:LocationScout.ViewModel".
The view model class looks fine for me:
namespace LocationScout.ViewModel
{
public class SettingsDeleteDisplayItem : BaseObservableObject
{
private long _countryAreaCountToDelete;
public long CountryAreaCountToDelete
{
get => _countryAreaCountToDelete;
set
{
_countryAreaCountToDelete = value;
OnPropertyChanged();
}
}
}
}
Building the solution works fine without error. Any idea? Many thanks.
As advised by #RobertHarvey in a comment, I changed to lowercase and restarted Visual Studio:
try changing ViewModel to lower case, as in xmlns:viewModel.
The reason this might have been an issue is because ViewModel (in
upper case) appears in your namespace declarations.
Now it works.
Just rebuilding the solution did not help (I tried that earlier).

Add a New ViewModel to ViewModelLocator in MVVM Light Toolkit

I know this question is somewhat basic, but at the current moment, I am completely lost as to how I should add a new ViewModel to my ViewModelLocator class in MVVM Light Toolkit.
My current implementation looks like so:
First assume that I have a Window named Settings, a ViewModel named SettingsViewModel and a ViewModelLocator ViewModelLocator.
First I call CreateSettings() in the VieModelLocator constructor:
public ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
CreateSettings();
}
CreateMain();
}
Note that this will always run as I'm not using blend and build the application each time I try to run it. Now for the `CreateSettings() method.
I had no idea what I was doing so I tried to play it safe and model everything after the methods used for creating and managing the MainViewModel.
public static void CreateSettings()
{
if (_settings == null)
{
_settings = new SettingsViewModel();
}
}
Then another few methods modeled after those used for the MainViewModel:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SettingsViewModel Settings
{
get
{
return SettingsStatic;
}
}
public static SettingsViewModel SettingsStatic
{
get
{
if (_settings == null)
{
CreateSettings();
}
return _settings;
}
}
And in my Settings Window Xaml:
<Window x:Class="_5500A_Auto_Calibrator.Settings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Settings" Height="300" Width="300"
DataContext="{Binding Source={StaticResource Locator}, Path=Settings}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
The window is then opened from my MainViewModel like so:
Settings settings = new Settings();
settings.Show();
If I try this, I receive an exception:
"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '4' and line position '39'."
And an inner exception of:
"Cannot find resource named 'Locator'. Resource names are case sensitive."
I've read up on errors involving a Window's inability to find the Locator resource, but most have to do with blend.
My current take is that I'm doing something wrong, but there's so little documentation as to adding new ViewModels that I'm unsure what I'm doing wrong.
Edit:
My App.Xaml:
<Application x:Class="_5500A_Auto_Calibrator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:_5500A_Auto_Calibrator.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
StartupUri="MainWindow.xaml"
mc:Ignorable="d">
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
</Application>
This is how it usually looks:
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
...
}
the ViewModelLocator is static, yours doesn't seem to be. It usually sits in the ViewModel folder (assuming you installed mvvmlight with the nuget and then added a new wvvm project.
it then proceeds to have the 2 cases for design and for runtime. (if you don't use it, you can skip the if (IsInDesignMode) ... bit, and just put your logic. (though it's a shame, since it's nice to have a preview of some fake data in VS designer ...)
Adding new ViewModels usually involves creating a property of that type, and registering them with the locator, so you can then retrieve them ... but this differs and can be done differently I believe ...
Hope this help, and if there's anything else I can help with, do let me know.
Your data context binding tries to apply earlier, than resource is declared. Try to declare binding this way (of course, this should help only if either MainSkin.xaml or application resources contain Locator resource):
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource Locator}" Path="Settings"/>
</Window.DataContext>

Adding .cs in a ResourceDictionary?

I have DataTemplate in a ressource dictionnary, and in some, I need button and i don't know how i can use code behind for manage events.
I tried to put a class in my resource dictionnary like that :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SLProject.Templates"
x:Class="TVTemplate">
And I definied the class in the cs file like that :
namespace SLProject.Templates
{
partial class TVTemplate
{
}
}
The build is OK but when the application started, I obtains XAML error following :
AG_E_PARSER_BAD_TYPE
I tried all I know like change the class kind to a ClassModifier, make the class to an inherited class of RessourceDictionnary ... no way.
Someone have an idee ...
Thanks.
Using the x:Class attribute allows you to define a codebehind for a ResourceDictionary.
You must specify the complete namespace of the class (i.e. x:Class="WpfApplication.MyClass"), and such class has to be defined as partial (at least VS 2010 complains and does not compile without such modifier).
I mocked-up a simple example:
1. Create a new WPF application project (WpfApplication)
2. Add a new class file (TestClass.cs) and paste the following code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows;
namespace WpfApplication
{
public partial class TestClass
{
private void OnDoubleClick(object obj, MouseButtonEventArgs args)
{
MessageBox.Show("Double clicked!");
}
}
}
3. Add a new ResourceDictionary (Resources.xaml), open the file and paste the following code
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication.TestClass">
<Style TargetType="{x:Type Label}">
<EventSetter Event="Label.MouseDoubleClick" Handler="OnDoubleClick"/>
</Style>
</ResourceDictionary>
4. Finally, open the MainWindow.xaml and past the following code
<Window x:Class="WpfApplication.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>
<ResourceDictionary Source="Resources.xaml"/>
</Window.Resources>
<Grid>
<Label Content="Double click here..." HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="Red"/>
</Grid>
</Window>
In the example I wire-up a double-click event from a Style, since it is a scenario requiring you to call some code from a ResourceDictionary.
You have the x:Class attribute defined twice, which is why you're getting the parser error. Change your declaration to this and it should work:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SLProject.Templates.TVTemplate">
I Checked, and it's just an error of copy-past. I have well the class definied one time.
Best thing would be to make your own usercontrol and add your events in it . and later put this entire usercontrol in resource dictionary.

Categories