Binding UserControl to ViewModel (Caliburn Micro WPF) - wpf

I am creating a login form that will be used by many different applications. The login will always have the same logic, so I'd like to bind a viewmodel and do all logic there (Retrieving login info from database, etc). I created a new UserControl, MainView and a ViewModel, MainViewModel both of which are in a Login namespace.
The form continues to run everything in the code-behind, but nothing in the VM.
Is there another way of binding that I am not aware of?
Code-Behind MainView.Xaml.vb
Imports Caliburn.Micro
Namespace Login
Public Class MainView
Public Sub New()
MsgBox("TEST code-behind")
End Sub
End Class
End Namespace
VM MainViewModel.vb
Imports Caliburn.Micro
Namespace Login
Public Class MainViewModel
Inherits PropertyChangedBase
Public Sub New()
MsgBox("TEST ViewModel")
End Sub
End Class
End Namespace
Xaml
<UserControl x:Class="Login.MainView"
xmlns:cal="http://www.caliburnproject.org"
xmlns:local="clr-namespace:cLogin.Login"
cal:Bind.Model="cLogin.Login.MainViewModel" (not sure if needed due to naming)
... >
EDIT
This is how I have added the UserControl as a separate window before the user is logged in, I can see the content, but none of the properties inside the ViewModel bind
Dim login As New Window
With login
.WindowStyle = WindowStyle.None
.ResizeMode = ResizeMode.NoResize
.SizeToContent = SizeToContent.WidthAndHeight
.Content = New MainView()
End With
login.ShowDialog()

Since you are creating the window explicitly, you also need to explicitly set its DataContext:
Dim login As New Window
With login
.WindowStyle = WindowStyle.None
.ResizeMode = ResizeMode.NoResize
.SizeToContent = SizeToContent.WidthAndHeight
.Content = New MainView()
.DataContext = New MainViewModel()
End With
You should also bind the attached Bind.Model property to the DataContext in the view:
cal:Bind.Model="{Binding}"

Related

Caliburn's Screen can not be inherited by Window or UserControl

I discovered that Window or UserControl can not inherit Caliburn's Screen base class. Am I forced to implement IScreen interface to every window/user control in my WPF application?
Imports Caliburn.Micro
Namespace Views
Public Class CustomView
Inherits Screen
End Class
End Namespace
Base class 'Screen' specified for class 'CustomView' cannot be
different from the base class 'UserControl' of one of its other
partial types.
Caliburn's Screen base class is meant to be used on view models.
Imports Caliburn.Micro
Namespace ViewModels
Public Class CustomViewModel
Inherits Screen
'...'
End Class
End Namespace
And then used to bind derived view models to the views
Imports ViewModels
Namespace Views
Public Class CustomView
Inherits Window
Public Sub New()
'...'
Dim viewModel As New CustomViewModel()
DataContext = viewModel
End Sub
End Class
End Namespace
Reference Caliburn.Micro Documentation: Screens, Conductors and Composition

Caliburn.Micro WindowManager and Window constructor parameter

It looks like Caliburn.Micro's WindowManager have problem with WPF Windows, that have constructor parameter although that parameter's type is registered in Caliburn.Micro IoC. Is there better way to put that needed parameter into Window except using stinking service locator IoC.Get(Of T) in constructor?
Namespace Views
Class MainWindowView
Private _eventAggregator As IEventAggregator
Public Sub New(eventAggregator As IEventAggregator)
_eventAggregator = eventAggregator
End Sub
End Class
End Namespace
Sub ShowMainWindowView()
'Everything is correctly registered in Ioc...
Dim windowManager As New WindowManager
Dim viewModel As New MainWindowViewModel
windowManager.ShowDialog(viewModel)
'Exception is thrown about absence of parameterless constructor of MainWindowView
End Sub
I tried to reproduce this problem again in new, clear project and it works - it is only needed to have type of view (Window) registered in IoC container and WindowManager automatically injects required dependencies in view's constructor.

how I can get direct access of control placed in User control?

I have a label under my user control. Below is the XAML code of my User Control.
<UserControl x:Class="Class_CalcProject"
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:Class_WPFCalc_Project.WPF_Calculator.ViewModelCalculator"
xmlns:local="clr-namespace:Class_WPFCalc_Project"
mc:Ignorable="d"
d:DesignHeight="315" d:DesignWidth="650"
Loaded="SetTextBoxFocus"
KeyUp="KeyBoardHelper">
<Grid>
<Label x:Name="lblHello" />
</Grid>
</User Control>
Below is the Code behind file of User Control in VB.NET:
Import System.Data
Import System.ComponentModel
Import System.Collections
Import System.Collections.ObjectModel
Class Class_CalcProject
Public Sub New()
InitializeComponent()
End Sub
End Class
I have another Class File with Name Testing.vb
Import System.Data
Import System.ComponentModel
Import System.Collections
Import System.Collections.ObjectModel
Class Class_Testing
End Class
My question is Is there any way to access my Label1 (Control) which is placed inside User Control in Testing Class? Where I can directly Change /set any property of Label1. Something similar like:
Label1.IsEnabled = True
or
UserControl.Current.Windows.OfType(Of Class_CalcProject)().First().Label1.IsEnabled = True
In norwal WPF application I can able to access my label control in Testing Class by writing and change / set its property:
Application.Current.Windows.OfType(Of Class_CalcProject)().First().Label1.IsEnabled = True
In windows WPF application the above code is working fine for me. But when I try to convert from Windows to User Control it doesn't work. As I am newbie in WPF and vb.net. Any help in this regard will be highly appreciated. Thanks in advance for posting your reply.
This is what Properties are for. You shouldn't be able to access those controls outside the UserControl directly. But you can.
Any Control in .Net has a property called Controls, which lists the controls immediately inside the parent. You can get them all with some recursion in an extension method:
<Extension()> _
Public Function ChildControls(Of T As Control)(ByVal parent As Control) As List(Of T)
Dim result As New List(Of Control)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is T Then result.Add(ctrl)
result.AddRange(ctrl.ChildControls(Of T)())
Next
Return result.ToArray().Select(Of T)(Function(arg1) CType(arg1, T)).ToList()
End Function
This method returns all children controls from the parent. From here, you would need to get the control you are looking for by name. Usage:
Dim myLabel = CType(TeradiodeTestPanel1.ChildControls.Where(Function(c) c.Name = "Label1").Single(), Label)
The Single method will throw an exception if the control isn't found, so Try...Catch accordingly.
You need to set the DataContext class for your UserControl and bind it's various properties to properties in that DataContext class.
It is considered best practice to observe the MVVM pattern and make your ViewModel class the DataContext of your UserControl. Your Testing Class could be that VM it would just need some modifications...
It is also best practice to use Data Binding instead of accessing the control properties in code behind so instead of Label1.IsEnabled = True you'd have <Label x:Name="lblHello" IsEnabled={Binding HelloLabelEnabled}/> in your XAML and HelloLabelEnabled property in your ViewModel which implements some form of change notification...
That's how things are supposed to work in WPF... you could, in theory, continue to code all this in your code-behind but that is highly discouraged and will make your life miserable in the long run...

How can I select tabitem in usercontrol from MainWindow

I would like to ask you, how can I access my usercontrol from MainWindow?
For accessing to MainWindow I'm using :
Dim mw As MainWindow = DirectCast(Application.Current.MainWindow, MainWindow)
I need this because I would like to select particular TabItem from my UserControl. (tabitem.IsSelected= true). UserControl is already placed in Grid on MainWindow as a child.
Thank you in advance
simple way is to create a ReadOnly property in MainWindow returning the user control instance
eg
Public ReadOnly Property MyEmployeeMenu As UserControl
Get
Return Me.employeemenu
End Get
End Property
usage
Dim mw As MainWindow = DirectCast(Application.Current.MainWindow, MainWindow)
Dim empMenu as UserControl = mw.MyEmployeeMenu 'this way you can access the control

Passing data to user control in MVVM pattern

Background:
I have WPF application with a main window containing a user control. I want to pass a value from the main window to the user control.
In the Main window's constructor I have code:
public MainWindow()
{
InitializeComponent();
_vm = new MainWindowViewModel();
this.DataContext = _vm;
ucControl = new UserControl1("NameSet");
}
(ucControl is my user control)
User control has two constructors:
public UserControl1()
{
InitializeComponent();
this.ID = ID.GetNewID;
}
public UserControl1(string name)
{
InitializeComponent();
_vm = new UCViewModel(name);
this.DataContext = _vm;
this.ID = ID.GetNewID;
}
The problem is: although the second constructor (with parameter) is called, it is not loaded in the main window. I checked the ID (this.ID) in the user control's loaded event and I see the ID set in the default constructor and its DataContext is null. Because of this reason, I do not get the "name" string in my user control.
Any help please? Since I am using MVVM pattern I do not want to expose properties in user control (view) to be set from main window just for this.
You are instantiating the UserControl1 object twice:
Once within the XAML. The <uc:UserControl1> element instantiates a UserControl1 object, using the default constructor, and assigns it to the member ucControl.
You instantiate it again within the constructor of the MainWindow object
If you put a break point in the constructor of UserControl, you'll notice it is called twice. I assume WPF instantiate and initialize the XAML's UserControl (#1 from above) after you assign the dynamic UserControl (#2 from above), and this is why you see the former in the logical tree of MainWindow.
You should have only one instance. If you want to parameterized a user control, the canonical paradigm is what you mention that you don't want to do (why??). If you had such a property, you could set it in the XAML: <uc:UserControl1 x:Name="..." YourProperty="NameSet>
exposing such a property is a single line in the UserControl:
public YourProperty { get; set; }
If you insist of not having this line, you should do the following:
Remove the XAML's user control.
In main window, subscribe to the Loaded event
In the handler of the Loaded event, instantiate a new UserControl1 - with whatever constructor parameter that you want.
Manually add it to the Children array of the parent Grid element
Clearly this isn't my recommendation. In addition to the complexity, with the former method you'll also work very well with the Visual Studio designer.

Resources