Setting <Window.DataContext> in XAML - c#

I followed a very simple MVVM example as a basis for my program. The author had one code behind instruction he used in the main page to set the DataContext. I'm thinking I should be able to do this in the XAML instead. The MainWindowViewModel is in a directory ViewModels. The code behind works.
namespace RDLfromSP
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModels.MainWindowViewModel();
}
}
}
I can't seem to find the right combo to set it instead in the XAML
<Window x:Class="RDLfromSP.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300" >
<Window.DataContext>
<local:ViewModels.MainWindowViewModel />
</Window.DataContext>
Thanks in advance for your help

You'll need an xml namespace mapping to the ViewModels namespace. Once you add that, it would be:
<Window.DataContext>
<vms:MainWindowViewModel />
</Window.DataContext>
(This is assuming you map vms to the appropriate namespace.)
This should look just like your current namespace mapping for local:, but called vms: with the appropriate namespace specified.

Related

WPF XAML Designer 'Exception Cannot read Page properties because it is not in a tree with Window as its root.'

I am creating a WPF page that inherits from a base page class and has a type argument of the view model. The code compiles and runs fine however the designer states there is an exception throw.
The code:
<local:BasePage x:TypeArguments="local:SplashScreenViewModel" x:Class="CommentatorScreen.SplashScreen"
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:local="clr-namespace:CommentatorScreen"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="SplashScreen">
<Grid>
<Button Command="{Binding TestCommand}" Content="I am a button" />
</Grid>
</local:BasePage>
Exception:
I'm not sure if this is related to the problem but if I remove the Type Argument and just have it inherit from the base page, it still complies but I get the error:
The name "BasePage" doesn't not exist in the namespace 'clr-namescape:CommentatorScreen'.
And reverting back to a normal page allows the designer works fine.
<Page x:Class="CommentatorScreen.SplashScreen"
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:local="clr-namespace:CommentatorScreen"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="SplashScreen">
<Grid Background="White">
<Button Command="{Binding TestCommand}" Content="I am a button" />
</Grid>
I have tried the usual clean the solution, rebuilding the solution, restarting Visual Studio, updating Visual Studio.
Any help would be appreciated.
BasePage:
namespace CommentatorScreen
{
/// <summary>
/// Class for basic page functions
/// </summary>
public class BasePage<VM>: Page
where VM : BaseViewModel, new()
{
#region Privates
/// <summary>
/// Pages view model
/// </summary>
private VM viewModel;
#endregion
/// <summary>
/// View model for the page
/// </summary>
public VM ViewModel
{
get { return viewModel; }
set
{
// If no change
if (viewModel == value)
{
return;
}
// Update the view model
viewModel = value;
// Set data context
DataContext = viewModel;
}
}
#endregion
#region Constuctor
/// <summary>
/// Constructor
/// </summary>
public BasePage()
{
//Create a default view model
ViewModel = new VM();
}
#endregion
}
}
Splash screen:
namespace CommentatorScreen
{
/// <summary>
/// Interaction logic for SplashScreen.xaml
/// </summary>
public partial class SplashScreen : BasePage<SplashScreenViewModel>
{
public SplashScreen()
{
InitializeComponent();
}
}
}
XAML designer in the VS is not WYSIWYG. Other words, the XAML markup that you enter in XAML code editor is not exactly what is displayed in XAML graphical editor.
When you open XAML file in the editor, VS generates all the environment to support displaying graphically the markup you enter. You may try to search for exact files generated (including cached XAML files), see:
C:\Users\[UserName]\AppData\Local\Microsoft\VisualStudio\16.0_7fec07a1\Designer\Cache\**
VS supports live preview feature, meaning it can display the markup you enter even if your project is not compiled yet. If you’re using custom base classes – while generating preview, base class may not be available. The designer removes from your code some parts to allow visualizing your markup.
I think then you’ll have a look what VS generates (the designer cache files), you will catch the idea.

Cannot create an instance of Usercontrol when subscribing an event

There are other threads on the topic but I did not find any that are related to my specific problem.
In Visual Studio 2017, I have a situation when the XAML designer prompt an error even though nothing strange seems to be present.
Basically, to reproduce the problem, consider those two files,
MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TabControl Background="White">
<TabItem Header="TEST" Width="60">
<local:UserControl1/>
</TabItem>
</TabControl>
</Grid>
</Window>
Usercontrol1.xaml.cs
namespace WpfApp1
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
App.Current.MainWindow.Closing += window_Closing;
}
void window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
}
}
}
Note that I did not include the content of Usercontrol1.xaml because it is not relevant here. It can be an empty UserControl.
Now, the designer shows the Error
Cannot create an instance of "Usercontrol1".
It is strange since when starting the application, everything is working fine. This is easily reproducible by creating a new WPF app and creating the same pattern shown above.
NOTE
I tried removing the subscription
App.Current.MainWindow.Closing += window_Closing;
And it removes the error. So, it is the cause, but why ?
App.Current will be null while in DesignMode.
You can check for IsInDesignMode to prevent running this code. Like,
public UserControl1()
{
InitializeComponent();
if(!DesignerProperties.GetIsInDesignMode(this))
App.Current.MainWindow.Closing += window_Closing;
}
Related post in MSDN blog.

WPF UserControl using MVVMLight can't find ViewModelLocator

I've got two problems. I have a WPF UserControl that is a .dll plugin to another WPF application.
The first is, unless I install MVVMLight in the WPF application that is using my Usercontrol dll, it complains it can't find any MVVMLight libraries. Is there anyway I don't have to install MVVMLight on the Main WPF application using my UserControl dll?
Second is, it can't find the ViewModelLocator in my UserControl. I've tried making it a StaticResource of my UserControl but it can't find the ViewModelLocator.
Please help.
Here is an example on how to use a View Model Locator:
Start with a simple ViewModel:
public class MainViewModel
{
public string TestProperty { get; set; } = "ViewModelLocator works fine!";
}
Define the ViewModelLocator:
public class ViewModelLocator
{
private static readonly MainViewModel mainViewModel;
static ViewModelLocator()
{
mainViewModel = new MainViewModel();
}
public static MainViewModel MainViewModel => mainViewModel;
}
As you see, your ViewModel's instance is created only once in the static constructor, and after that, the same instance is returned.
And here is the View:
<Window x:Class="SetViewModelLocator.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:SetViewModelLocator"
xmlns:vm="clr-namespace:SetViewModelLocator.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:ViewModelLocator x:Key="ViewModelLocator"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModelLocator}, Path=MainViewModel}">
<TextBlock Text="{Binding TestProperty}"/>
</Grid>
Set the Locator as a Resource and use it as the DataContext of your main container which in this case is a Grid.

Partial declarations of 'Triangle.MainWindow' must not specify different base classes

I've been facing an error that tells me that the partial declarations must not specify a different base class.
public partial class MainWindow : Shape
{
public MainWindow()
{
InitializeComponent();
this.Stretch = System.Windows.Media.Stretch.Fill;
this.StrokeLineJoin = PenLineJoin.Round;
}
I get error from :
public partial class MainWindow : Shape
The 'MainWindow' gives me error about the specifying of a different base. How do i go about rectifying this error?
My XAML currently, is the default one:
<Window x:Class="Triangle.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>
</Grid>
</Window>
I have yet to edot anything from the XAML as this codes are codes i found somewhere from online and is using it to try out whether or not it work.
MainWindow normally extends Window.
So in your code-behind you'll see public partial class MainWindow : Window, and in your associated XAML you'll see something like:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
... />
...
</Window>
To extend another class (not sure what Shape is, but I'm assuming it's appropriate in this case), you'll have to correct your XAML in addition to the code-behind... something like this:
<Shape x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
... />
...
</Shape>
MainWindow : Shape? I suppose it to be MainWindow : Window
do please verify the base class (root element) from the designer of the MainWindow.xaml and use the same base class here.
typically top level window classes like default MainWindow class derives from Window. whereas in your case I can see it is being derived from Shape
if you are trying to create a shape class then there is no InitializeComponent() in shape class and it does not need a designer hence a partial class is not required. last but not the least the class name MainWindow does not sounds a nice name for the same. you may perhaps revise it.
your main page should be in the format
public sealed partial class MainPage : Page
inheriting only Page Class.

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