Binding WPF xaml to a ViewModel without constructing it - c#

My WPF app doesn't use an app.xaml. I'm using MVVM, and construct the view and the viewmodel separately. I then pass the ViewModel to the View constructor and set the datacontext there.
I'd like to have Visual Studio be able to understand the viewmodel's properties for context clicking and such, but not have it construct its own DataContext when the view is set.
When I do this, it forces me to have a default constructor for MainViewModel, and then it calls that VM constructor when I construct the View.
<Window x:Class="Kraken.CopFeed.Windows.MainFeedView"
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:Windows="clr-namespace:MyProgram.MainApp.WpfWindows"
mc:Ignorable="d"
Title="Main App" Height="321.557" Width="652.922">
<Window.DataContext>
<Windows:MainViewModel />
</Window.DataContext>
How can I keep my existing implementation of constructing the V and VM separately, but get the XAML editor in Visual STudio to know of my ViewModel that will (eventually) be set as the data context?

I think you should be able to do this with d:DesignInstance, as in this question.
<Window
...etc...
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=MyViewModelNamespace:MyViewModel}"
>

Related

Rider Code Completion for Autowired ViewModel (Prism)

I'm working on a project that uses Prism for its client software. I have a UserControl XAML file that looks something like this:
<UserControl x:Class="UserModule.Frontend.UserListView"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ListView ItemsSource="{Binding Users}"/>
</Grid>
</UserControl>
The actual XAML is a bit longer, but my question is:
Rider shows me a warning at the binding of the list view's ItemsSource property. I have a ViewModel that Prism injects correctly and I can see that the list has been populated. However, at design time, I can't see if the property exists, if I don't check for myself. At the same time, I get a warning in the ViewModel class, that the public getter of Users could be removed.
Is there a way to get code completion to recognize the autowired ViewModel with Prism?
You need to define the d:DataContext for your view. And while you're at it, vote for the feature...

UserControl DataContext defined at XAML page level

I defined a DataContext for a UserControl at XAML page level as follows (the last line being the relevant one):
<UserControl
x:Class="Sample.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="using:Sample.Models"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="768"
d:DesignWidth="1024"
DataContext="data:TestDataCollection">
The aim is to be able to access the DataContext object in the page hosting the UserControl as follows, myUserControl being the x:Name for the UserControl.
TestDataCollection tdc = myUserControl.DataContext as TestDataCollection;
Everything is working fine with data binding and UI displaying and updating as expected on the UWP platform.
The only one problem is that the above code line is not returning the expected DataContext object. In fact, myUserControl.DataContext during debugging shows a string with a value "data:TestDataCollection" (same as in the above XAML code) rather than an object of type TestDataCollection.
Here is another strange thing: If I set the DataContext in codebehind as:
this.DataContext = new TestDataCollection();
the problem with be gone, i.e., (myUserControl.DataContext as TestDataCollection) returns the DataContext object as expected.
What am I doing wrong in setting the page DataContext in XAML?
By using DataContext="data:TestDataCollection", you're doing nothing more than setting a string value. If you want a viewmodel object to be set, you have to use following syntax:
<UserControl
x:Class="Sample.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="using:Sample.Models"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="768"
d:DesignWidth="1024">
<UserControl.DataContext>
<data:TestDataCollection />
</UserControl.DataContext>
</UserControl>
Note that your usercontrol will also inherit a DataContext from the page it's used on. So in most scenarios it's not necessary to set it explicitly in your control, while you will still be able to access it in code behind (as it's set by the page implicitly).

WPF XAML: Difference between DataContext as attribute or property for XAML element?

I'm currently getting started with XAML and I have a question regarding how to define the DataContext of an element.
I've created a View that includes a Page with the following markup:
<Page x:Class="View.MainView"
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:ViewModel="clr-namespace:ViewModel"
mc:Ignorable="d"
Title="MainView">
...
</Page>
When I want to give this Page a DataContext to be used by child elements, this works:
<Page x:Class="View.MainView"
...
mc:Ignorable="d"
Title="MainView">
<Page.DataContext>
<ViewModel:MainViewModel />
</Page.DataContext>
...
</Page>
And this doesn't:
<Page x:Class="View.MainView"
...
mc:Ignorable="d"
Title="MainView" DataContext="ViewModel:MainViewModel">
...
</Page>
For me, it looks like the Page element expects the DataSource to be defined as a XAML property and not as an attribute. However, the IntelliSense in Visual Studio offers me a DataContext attribute for the Page, so I guess I'm just using a wrong syntax here. Can you point that out to me?
Thanks!
You can use the attribute to specify the DataContext, but you should consider how does your viewmodel get instantiated.
Using a property in this way
<Page.DataContext>
<ViewModel:MainViewModel />
</Page.DataContext>
you tell WPF to instantiate the MainViewModel and to assign the created object to the DataContext property of the Page.
With an attribute, you just specify a string in that case:
DataContext="ViewModel:MainViewModel"
But you want WPF to create an instance for you.
So you can use e.g. a Binding or a StaticResource / DynamicResource to assign a created instance to the DataContext property:
DataContext="{Binding ViewModel}"
or
<Page DataContext="{StaticResource ViewModel}">
<Page.Resources>
<ViewModel:MainViewModel x:Key = "ViewModel"/>
</Page.Resources>
</Page>

How to implement converter class in usercontrol

I want to make a converter class , I implemented it and i want to use it in another xaml class
So i write this code
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PoliceApp"
xmlns:common="using:PoliceApp.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Resources>
<local:TitleToImgConverter x:Key="BoolOrConverter"/>
</UserControl.Resources>
</UserControl>
It tells me that there is a missing attribute for user control
and my first code was
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PoliceApp"
xmlns:common="using:PoliceApp.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Resources>
<local:TitleToImgConverter x:Key="BoolOrConverter"/>
</UserControl.Resources>
And the error was "The name titletoimgconverter doesnt exist in the namespace using:policeApp"
This is normal (at least, I have never seen it otherwise) when you have just created a new converter and added it as a resource in your XAML code. XAML code often lags behind when something is added to the namespace.
The solution for this is to rebuild your entire project. The XAML should now be able to locate your converter, and the error should disappear.
Update
If your converter exists in some folder called Converter, you should use your first example, and replace xmlns:local="using:PoliceApp" with xmlns:local="clr-namespace:PoliceApp.Converter". If it just resides in your main folder, you can leave out the .Converter. Note that I've replaced the using: with clr-namespace:.

Setting the DataContext in "MainView" filters down to all "ChildView's"

I would like to know if this is a standard feature of .NET: when setting the DataContext in the ParentView, it filters down to all child views.
Say you have ParentView, ChildView1 and ChildView2:
<UserControl x:Class="DXWPFApplication1.ParentView"
xmlns:view="clr-namespace:DXWPFApplication1"
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>
<view:ChildView1 x:Name="childView1"/>
</Grid>
</UserControl>
<UserControl x:Class="DXWPFApplication1.ChildView1"
xmlns:view="clr-namespace:DXWPFApplication1"
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>
<view:ChildView2 x:Name="childView2"/>
</Grid>
</UserControl>
Code behind of ParentView:
public ParentView()
{
InitializeComponent();
DataContext = "ViewModel"; //BreakPoint here
//
//When the first DataContext is set, all the DataContext's below are set as well
//
childView1.DataContext = DataContext;
childView1.childView2.DataContext = DataContext;
}
NOTE: Breakpoint when setting first DataContext
Why are all the DataContexts set when I have only set the ParentView's DataContext?
What can I do to prevent this from happening?
This is standard behaviour, and normally desired. To prevent it, set DataContext to {x:Null} in your markup
A component in the visual tree inherts the data context from its parent. Your child view resides in the visual tree of the parent view so it will get the parent context assigned. You need to explicitly set it to something different if you want to change it (either inside the child view constructor or in the xaml, for example <view:ChildView2 DataContext="{x:Null}" x:Name="childView2"/>).
Why was that done: Because it is almost always what you want.
Willem if you are using separate ViewModels for each View, what you should do is nest your ViewModels.
So say you have MainViewModel with properties exposed ChildViewModel1 and ChildViewModel2. Then you would set the binding of the child usercontrols to
DataContext="{Binding ChildViewModel1}" and
DataContext="{Binding ChildViewModel2}" respectively
all the while the main view maintains its DataContext as MainViewModel

Categories