WPF UserControl using MVVMLight can't find ViewModelLocator - c#

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.

Related

Why does binding to elementName in usercontrol work even though Name is changed by user of usercontrol?

I have the following very simple WPF application:
A User control:
XAML:
<UserControl x:Class="WPFUserControlTest.TestControl"
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:WPFUserControlTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
x:Name="root">
<Grid>
<TextBlock Text="{Binding ElementName=root, Path=InputString}"/>
</Grid>
Code Behind:
namespace WPFUserControlTest
{
public partial class TestControl : UserControl
{
public TestControl()
{
InitializeComponent();
}
public string InputString
{
get { return (string)GetValue(InputStringProperty); }
set { SetValue(InputStringProperty, value); }
}
public static readonly DependencyProperty InputStringProperty =
DependencyProperty.Register("InputString", typeof(string), typeof(TestControl), new PropertyMetadata(""));
}
}
My Main Window:
<Window x:Class="WPFUserControlTest.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:WPFUserControlTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<local:TestControl x:Name="Mercedes" InputString="Mercedes"/>
<local:TestControl InputString="Volvo"/>
</StackPanel>
</Grid>
</Window>
What i wonder is how it can be that the binding inside the user control that uses ElementName seems to work even though the main window changes the name of the control.
Is this binding done internally in the control somehow at compile time?
When i look at this is in the Live Visual Tree I see that one of my control instances has name "root" and one is named "mercedes". Still both of them works as expected...
You should read up on XAML namescopes. The name "Mercedes" is only applicable in the namescope of the window and "root" in the namescope of the UserControl.
The window cannot refer to the UserControl as "root" because it doesn't belong to the same namescope.

Cannot display binding on usercontrol?

I'm not able to display a property value on the usercontrol.
I set up the datacontext in this way:
public MainController cm;
public static MainWindow AppWindow;
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
cm = new MainController();
DataContext = cm;
AppWindow = this;
}
}
inside MainController I've all the controller with all the properties like this:
public class MainController: MainControllerVM
{
private ClubController _clubController = new ClubController();
public ClubController ClubController
{
get { return _clubController ; }
}
}
Now I've splitted my user interface in different controls to have more xaml organization. I need to access to the main datacontext that's cm from all user controls, I tried in this way:
public partial class Club : UserControl
{
public Club ()
{
InitializeComponent();
DataContext = MainWindow.AppWindow.cm;
}
but I get:
NullReferenceException
on AppWindow. My main problem's that I can't get to display the value of the property on a label available on the user control:
<Label Content="{Binding ClubController.Club.Name}" />
this binding working in the main window but not working on usercontrol, why??
Suppose you have a window like this:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
And you set the DataContext in the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
Now remember that the DataContext is an inheritable dependency property, i.e. it flows down. (In general, dependency properties are not inheritable by default, unless you explicitly state it)
So, you set the DataContext once on the root of the logical tree (the window) and all of its children will "see" it. (the UniformGrid and the custom controls in our case)
Yes, that means you can directly bind to the view model in your user control's XAML:
<UserControl x:Class="Example.MyUserControlA"
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>
<TextBlock Text="{Binding PropertyFromMainViewModel}"/>
</Grid>
</UserControl>
Now, this approach works well, until your control gets so complicated that it needs to have its own ViewModel and DataContext reespectively.
Usually this happens when the control is not a passive, but maintains a state (validates input, button state, etc.)
1.Declare all properties that you want to bind to the main view model as dependency properties and pay attention to the default value you specify.
2.Locate the main panel of your UserControl and name it, for example "LayoutRoot":
<UserControl x:Class="Example.MyUserControlA"
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 x:Name="LayoutRoot">
<TextBlock Text="{Binding MyDependencyProperty}"/>
</Grid>
</UserControl>
3.Now, you set the DataContext on the LayoutRoot
public MyUserControlA()
{
InitializeComponent();
LayoutRoot.DataContext = new MyUserControlViewModel();
}
4.You bind to the main view model in this way
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Example"
Title="MainWindow" Height="350" Width="525">
<UniformGrid Rows="2" Columns="2">
<local:MyUserControlA MyDependencyProperty="{Binding MainViewModelProperty}"/>
<local:MyUserControlB/>
<local:MyUserControlC/>
<local:MyUserControlD/>
</UniformGrid>
</Window>
The other way around is to bind using RelativeSource, but this would break the encapsulation and reusability of your UserControl.
WPF has a steep learning curve, I hope my tips were helpful...

Creating a child Window without an MVVM framework

I'm writing a small application whilst learning MVVM in WPF.
As long as I keep on using one Window, everything is pretty easy.
Now I want to open a new Window with a specific ViewModel.
I have a main ViewModel, which contains a Command that should open a new Window / ViewModel, along with a Parameter.
To do this in an MVVM way, I've created a NavigationService, which I'd like to call like this:
public MainWindowViewModel()
{
DetailsCommand = new DelegateCommand(Details);
}
public void Details()
{
SessionsViewModel sessions = new SessionsViewModel();
_NavigationService.CreateWindow(sessions);
}
I've noticed that it's possible to "bind" Views and ViewModels in XAML, like this:
<Application x:Class="TimeTracker.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TimeTracker"
xmlns:vm="clr-namespace:TimeTracker.ViewModels"
xmlns:vw="clr-namespace:TimeTracker.Views"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:MainWindowViewModel}">
<vw:MainWindow />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SessionsViewModel}">
<vw:Sessions />
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
<Window x:Class="TimeTracker.Views.Sessions"
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:TimeTracker.Views"
xmlns:vm="clr-namespace:TimeTracker.ViewModels"
mc:Ignorable="d"
Title="Sessions" Height="300" Width="300">
<Window.DataContext>
<vm:SessionsViewModel/>
</Window.DataContext>
<Grid>
<TextBlock Text="Hallo" />
</Grid>
</Window>
The problem I'm having is that I don't know how I can use this ResourceDictionary in my NavigationService, so that I can create a new Window by only using its ViewModel.
class NavigationService
{
public void CreateWindow(IViewModel viewModel)
{
//How do I create a new Window using the ResourceDictionary?
}
}
Make sure you have a ContentControl or ContentPresenter in your new window so that the ViewModel can be presented. Next, make sure that resource dictionary is in scope. Putting it in Application.Resources will make it global and guarantee that WPF can find the DataTemplate.
Also, don't use a Window class as your view in the DataTemplate. Use your sub-window panel (e.g., Grid, StackPanel, etc).
I do this:
<blah:MyChildWindow>
<ContentControl Content={Binding DataContext}/>
</blah:MyChildWindow>
And in Application.Resources:
<DataTemplate DataType={x:Type blah:MyViewModel}>
<blah:MyChildWindow/>
</DataTemplate>
BTW - using DataTemplates the way you are trying to do is an excellent pattern.

Window Binding to UserControl ViewModel

I need help with binding a window to a user control view model. Here's my user control, nice and simple.
<UserControl x:Class="WindowBindTest.UserControlTest"
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"
xmlns:ViewModel="clr-namespace:WindowBindTest.UserControlViewModel"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<ViewModel:UserControlViewModel></ViewModel:UserControlViewModel>
</UserControl.DataContext>
<Grid>
</Grid>
</UserControl>
Here's my view model
namespace WindowBindTest
{
public class UserControlViewModel
{
public Window hostWindow { get; set; }|
public UserControlViewModel()
{
// I want to set the host window
// If something isn't defined then close the host window.
}
}
}
I could use a button to close the window but what if I didn't want to use a button. Is there a way to do that?
Could I pass in the parent window into the constructor?
You can do this in MVVM but you will need to use a service. In fact, this is where MVVM is weak (without using a framework such as Prism et al.). The following is a link to disore's DialogService class on CodeProject. it is awesome, but it will take time to get to grips with how it works.
The above library will enable you to close a View from a ViewModel.
I hope this helps.

How to move the DataContext definition from code-behind to XAML?

I defined an object which contains properties in code-behind and, want to set the object to datacontext in xaml.
namespace WpfApplication4
{
public partial class MainWindow : Window
{
public EquipInfo equip1;
public MainWindow()
{
InitializeComponent();
equip1 = new EquipInfo();
// setting here works fine but i want to do in xaml
//textBox1.DataContext = equip1;
}
}
}
here's xaml code..
<Window x:Class="WpfApplication4.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>
<TextBox Text="{Binding Path=PIN}" Height="23"
HorizontalAlignment="Left" Margin="172,208,0,0"
Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
You can set the DataContext via XAML to a public property. blindmeis has given a good example on how to do this.
What you did in your Post was not setting the DataContext, but binding the Text Property of your Control to a Property of the window. This only works if a DataContext is set. Another way to do that, if you don't want to do MVVM (which I'd recommend, though), is referencing specific Elements in your Binding:
1.) Alter member to AutoProperty or similar
public EquipInfo equip1 {get;set;}
2.) Give your window a name
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="myWin"
Title="MainWindow" Height="350" Width="525">
2.) Access Window Properties by instance name
<TextBox Text="{Binding ElementName=myWin, Path=equip1.PIN}"/>
On a side note, it would really be better to use a MVVM instead.
when you create your equipinfo class in codebehind why not setting the datacontext there?
nevertheless you can create a datacontext in xaml (you did not need the codebehind stuff then)
<TextBox Text="{Binding Path=PIN}">
<TextBox.DataContext>
<local:equip1/>
</TextBox.DataContext>
</TextBox>
but please look at the MVVM pattern
When you create your equipinfo class in codebehind why not setting the datacontext there?
The whole point of MVVM is to seperate UI from code behind using a viewmodel proxy. If you are going to reference the ui element to set its datacontext you might just as well reference it to set its value and forget about mvvm.

Categories

Resources