Window.Content with DataTemplate - wpf

I have a DataTemplate it's DataType is the MyViewModel, if I do something like:
<ContentPresenter Content="{Binding SomeViewModel}"/>
if I set the "SomeViewModel" to be MyViewModel (the VM that defined in the DataTemplate), I can see the DataTemplate render on the view, which is expected.
What I want to do is this:
Window host = new Window()
host.Content = new MyViewModel();
host.Show();
I expect this to show a window with the DataTemplate that associated with the MyViewModel render on it, instead I get a window with a single line, the path to my ViewModel.
what am I doing wrong ?

Probably a resource location issue. Where was the DataTemplate defined previously? Was it in App.xamls ResourceDictionary? Try adding the DataTemplate there.
<Application ...>
<Application.Resources>
<DataTemplate DataType="{x:Type MyViewModel}">
<!-- View -->
</DataTemplate>
</Application.Resources>
</Application>
In a better-case scenario you would place this in a ResourceDictionary that is merged with others in App.xaml.
Edit: tiny working example.
<Application x:Class="DataTemplateTest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Startup="Application_Startup">
<Application.Resources>
<DataTemplate DataType="{x:Type sys:Int32}">
<Border Background="Red">
<TextBlock Text="{Binding}" />
</Border>
</DataTemplate>
</Application.Resources>
</Application>
Appropriate code-behind:
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
var window = new Window();
window.Content = 42;
window.Show();
}
}
Edit 2: Since you said this code is in a WPF AddIn
If the DataTemplate is in the Host application, this will not work. Host and AddIn UIs do not talk to one another in that manner as the AddIn is merely an HwndSource.
If the DataTemplate is in a ResourceDictionary in the AddIn, you can load it like so:
var window = new Window();
window.Resources.MergedDictionaries.Add(
new ResourceDictionary
{
Source =
new Uri("pack://application:,,,/AddInAssembly;component/Resources.xaml",
UriKind.Relative)
});
window.Content = ...;
window.Show();

you should add the following information to your question: where is the datatemplate/usercontrol defined (addin.dll)? where is "window" defined (mainproject)?
if your viewmodel and datatemplate are in your addin.dll and your "window" is in your mainproject, you would have to add your datatemplate as a resource to the window at least.
if your viewmodel and datatemplate and your "window" are in your addin.dll and you just call in your main project "window.show" then you have to add the datatemplate as a resource to the "window" in your addin.dll.
i use mef fo building wpf plugin applications and i use the following syntax in my addin.dlls:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourAddinDllName;Component/ResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
btw if you expose just your viewmodel and call it in your mainproject. you have to expose and register your datatemplate too. let me know how your plugins work.

Related

Cannot find resource named 'ViewModelLocator' exception

I am trying to use ViewModelLocator by declaring it as a resource in App.xaml. Its a very simple class as follows:
public class ViewModelLocator
{
public ShellViewModel ShellPage
{
get
{
return new ShellViewModel();
}
}
}
App.xaml file is as below:
<Application x:Class="SomeNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SomeNamespace.ViewModels">
<Application.Resources>
<vm:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
</Application>
App.xaml.cs is as below:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var view = new ShellView();
Current.MainWindow = view;
Current.MainWindow.Show();
}
}
ShellView.xaml is a below:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SomeNamespace.ShellView"
Title="MainWindow"
Height="350"
Width="525"
ResizeMode="NoResize"
MinWidth="700"
MinHeight="700"
DataContext="{Binding ShellPage, Source={StaticResource ViewModelLocator}}"
>
<Grid>
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
</Window>
I can see the correct title in Visual Studio designer but when i run the app, get XamlParseException:
'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '11' and line position '9'.
The innerexception has {"Cannot find resource named 'ViewModelLocator'. Resource names are case sensitive."}
Am i missing something ?
Try putting it inside a ResourceDictionary
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="ViewModelLocator" />
</ResourceDictionary>
</Application.Resources>
Edit:
I solved the problem by using the Startup event in the App, instead of overriding OnStartup.
<Application x:Class="TestWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TestWPF.ViewModels"
Startup="App_Startup">
<Application.Resources>
<vm:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
</Application>
Code
public partial class App : Application
{
void App_Startup(object sender, StartupEventArgs e)
{
var view = new ShellView();
Current.MainWindow = view;
Current.MainWindow.Show();
}
}
At startup time you're loading the window, this is not a good practice in my opinion because the resources are not yet entirely resolved in both App in witch your Window depends. Even if you're seeing properties loading at design time this is not always a good argument, though design is following another logic to execute the code.
You have to let App and Windows load and then fill the context property in the Window's grid
Remove the resource XAML from App.xaml (it looks really out of the scope there) and do this instead :
<Window.Resources>
<wpfApplication1:ViewModelLocator x:Key="ViewModelLocator" />
</Window.Resources>
<Grid DataContext="{Binding ShellPage, Source={StaticResource ViewModelLocator}}">
<TextBlock Text="{Binding Title}"></TextBlock>
</Grid>
It seems i found a solution.The reason maybe the StartUrl in the App.xaml.
1.Remove the default StartUrl in the App.xaml.Such like this:
<Application x:Class="BidingAccessories.App" 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" d1p1:Ignorable="d"
xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" xmlns:vm="clr-namespace:BidingAccessories.ViewModel" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/BidingCommon;component/CommonStyleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
</Application.Resources>
</Application>
2. override the OnstartUp event in App.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
}
Remove this part of code and run again. I am not sure about what's happening internally but that is what throwing the exception.

Custom property of own UserControl needs weird binding on usage

We have a TickerUserControl with a simple Text property which stands for the displayed text of the ticker.
Do we really have to use these DependencyProperty pattern thing inside the UserControl (see below) or is there a simpler way to achieve this?
When we want to use our UserControl and BIND the text field to a property of a ViewModel we have to use the following weird binding syntax. Why can't we just use 'Text="{Binding Text}"' like all the other controls? Is there something wrong with the property implementation of the UserControl or something?
Usage of the UserControl
<userControls:TickerUserControl Text="{Binding Path=Parent.DataContext.TickerText, RelativeSource={RelativeSource Self}, Mode=OneWay}"/>
Property implementation of the UserControl (code behind)
public partial class TickerUserControl : UserControl
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TickerUserControl), new PropertyMetadata(""));
// ...
}
XAML snippet of the UserControl
<UserControl x:Class="Project.UserControls.TickerUserControl"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<TextBlock Text="{Binding Text}">
<!-- ... -->
The solution
The problem was the setting of the DataContext inside the UserControl.
I deleted the DataContext binding added a name to the UserControl and modified the TextBox binding inside the UserControl. After that I was able to bind "as usual" from outside.
<userControls:TickerUserControl Text="{Binding TickerText}"/>
<UserControl x:Class="Project.UserControls.TickerUserControl"
Name="TickerUserControl"
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">
<TextBlock Text="{Binding Text, ElementName=TickerUserControl}">
<!-- ... -->
If you want to bind your property, you'll need a dependency property.
To solve the weird binding you can do the following changes:
In your usercontrol
<UserControl Name="control"...
<TextBlock Text="{Binding Text, ElementName=control}">
And then you can bind it like that
<userControls:TickerUserControl Text="{Binding TickerText}"/>
If you want to create bindable properties in the code behind of your UserControl, then yes, you do have to use DependencyProperty objects. However, you don't have to create your bindable properties there... you could use an MVVM type pattern and create your bindable properties in a separate class as long as you set that class as the DataContext of you UserControl.
No, you don't have to use that 'weird' binding syntax... the problem is that you have hard coded setting the DataContext of your UserControl to its code behind. Instead of doing that, you can bind your Text DependencyProperty in the control like this:
<TextBlock Text="{Binding Text, RelativeSource={RelativeSource FindAncestor,
{AncestorType={x:Type Local:TickerUserControl}}}" />
Local is the XML namespace of your local project... something like this:
xmlns:Local="clr-namespace:Project.UserControls.TickerUserControl"
Then after removing the DataContext binding, you should be able to bind to the control from outside normally.

Accessing dependency property in the same control xaml

In a Wpf Application i have a main window.
I have added a user control to the same project.
In the user control's .xaml.cs file a Dependency property ( "Value" name of the property ) is added.
I would like to access the defined dependency property in the usercontrol.xaml.
I know i can do the same while creating the control instance either in window.xaml or some other user control.
But is it possible to access the dependency property defined in .xaml.cs in .xaml?
Question updated based on Vivs answer
Ok. I mentioned my question wrongly. Nevertheless even i was not aware of accessing. But my actual intended question is it possible to set the dependency property from .xaml. some thing like from the example given above,
<Grid CustomBackground ="{Binding Path= BackgroundColor}" />
Or
<Grid CustomBackground ="Blue" />
Is it possible to set the custom dependency properties like this in the same .xaml?
Yes it is possible.
something like:
.xaml
<UserControl x:Class="MvvmLight26.UserControl1"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=CustomBackground}" />
</UserControl>
and .xaml.cs:
public partial class UserControl1 : UserControl {
public static readonly DependencyProperty CustomBackgroundProperty =
DependencyProperty.Register(
"CustomBackground",
typeof(Brush),
typeof(UserControl1),
new FrameworkPropertyMetadata(Brushes.Tomato));
public UserControl1() {
InitializeComponent();
}
public Brush CustomBackground {
get {
return (Brush)GetValue(CustomBackgroundProperty);
}
set {
SetValue(CustomBackgroundProperty, value);
}
}
}
Alternate:
If you say have the DataContext of the UserControl as itself like:
public UserControl1() {
InitializeComponent();
DataContext = this;
}
then in your xaml you could just go with:
<Grid Background="{Binding Path=DataContext.CustomBackground}" />
Update:
For the new question,
Not quite directly.
You can "set" the value if the custom DP is registered as an attached property(Do remember an attached property is not the same as a normal DP in it's behavior and scope.)
If you want to keep it as a normal DP, then you can keep UserControl1 from the original answer same as it is(just the DP part. You need to remove the xaml part of it and make it a non-partial class in the code-behind) and then derive it to a new UserControl.
something like:
<local:UserControl1 x:Class="MvvmLight26.UserControl2"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
CustomBackground="Blue"
mc:Ignorable="d">
<Grid />
</local:UserControl1>
You can ofc name UserControl1 as something like "BaseUserControl" or so to make it obvious that it's not intended for direct usage.
You can set the value from the UserControl.Style in the same xaml as well.
xaml:
<UserControl x:Class="MvvmLight26.UserControl1"
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:MvvmLight26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.Style>
<Style>
<Setter Property="local:UserControl1.CustomBackground"
Value="Blue" />
</Style>
</UserControl.Style>
<Grid Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:UserControl1}}, Path=CustomBackground}" />
</UserControl>

WPF user control's datacontext to property in codebehind

Having a simple XAML user control, I'd like to set the DataContext to the code behind (xaml.cs) file.
I'd like to set DataContext and Itemssource in XAML, so I can populate the combobox with property ListOfCars
XAML
<UserControl x:Class="Sample.Controls.MyControl"
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="85" d:DesignWidth="200">
<Grid Height="85" Width="200" Background="{StaticResource MainContentBackgroundBrush}">
<StackPanel Orientation="Vertical">
<ComboBox Height="23.338" x:Name="CarList" />
</StackPanel>
</Grid>
</UserControl>
Code behind
public List<Cars> ListOfCars
{
get { return _store.ListCars(); }
}
In other words, instead of doing this in codebehind, how may I set the binding in XAML
public MyControl()
{
InitializeComponent();
_store = new Store();
CarList.ItemsSource = _store.ListCars();
CarList.DisplayMemberPath = "Name";
}
Just bind the ItemsSource.
<ComboBox ItemsSource="{Binding ListOfCars}"/>
And then for the UserControl:
<MyControl DataContext="{Binding *viewModel*}"/>
You have to bind the DataContext where your UserControl is used rather than in the definition, because in the definition you don't know to what to bind. The Combobox automatically is in the context of the control so you can just bind to the DataContext without any additional work.
Example of binding to a resource:
<Application.Resources>
...
<viewmodels:ViewModelLocator x:Key="ViewModelLocator"/>
...
</Application.Resources>
<MyControl DataContext="{Binding Source={StaticResource ViewModelLocator}}"/>
This creates an instance of the ViewModelLocator and then binds the DataContext of the control to that resource.
Do not do that, you will mess up all external bindings to the DataContext. Use UserControl.Name and ElementName bindings instead (or RelativeSource).

DataTemplate in Resource sets ViewModel to View, but then

I am trying to figure the many different ways of setting datacontext of a view to a viewmodel.
One I'm oggling at this moment goes something like this:
I have my MainWindowResource:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:DemoStuffPartII.View"
xmlns:vm="clr-namespace:DemoStuffPartII.ViewModel">
<DataTemplate DataType="{x:Type vm:PersonViewModel}">
<vw:PersonView />
</DataTemplate>
But that's also immediately where I strand. I know that I should use a ContentControl in the View. But what is the best way to configure it? How to go about this?
That is the way you can enable ViewSwitching navigation in your MVVM application.
The other missing bits are:
in the view ->
<ContentControl Content="{Binding CurrentPage}" />
in the ViewModel -> (pseudo code)
Prop ViewModelBase CurrentPage.
note however that if all u want is to connect a ViewModel to a View, you can just drop the entire DataTemplate-ContentControl thing altogether, and just do this.DataContext = new SomeViewModel(); in the codebehind.
The cleanest way I know to connect VM to Views is by using the ViewModelLocator pattern. Google ViewModelLocator.
There are a couple of simple ways to just bind a ViewModel to a view. As Elad mentioned you can add it in the code-behind:
_vm = new MarketIndexVM();
this.DataContext = _vm;
or, you can specify the ViewModel as a resource in your XAML of your view:
<UserControl.Resources>
<local:CashFlowViewModel x:Key="ViewModel"/>
<Converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
and bind the DataContext of your LayoutRoot to that resource:
<Grid x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}">
Maybe this doesn't directly answer your question, but have you looked at using an MVVM framework? For example, in Caliburn.Micro you would do (very basic example):
public class ShellViewModel : Conductor<IScreen>
{
public ShellViewModel()
{
var myViewModel = new MyViewModel();
this.ActivateItem(myViewModel);
}
}
ShellView.xaml
<Grid>
<ContentControl x:Name="ActiveItem" />
</Grid>
MyView.xaml
<Grid>
<TextBlock>Hello world</TextBlock>
</Grid>
This is a viewmodel first approach.

Resources