It is possible to do 'simple' binding with a UserControl? - wpf

I have a user control in which I have some textblocks. I want to include this usercontrol into a listBox (or listview in case it causes any problems).
When I check the output windows, I see no binding exception, but I don't see anything in the textblock either.
Is there anyway to make this work ?
Thanks :
Here is the listBox I use for now :
<ListBox AllowDrop="True" Grid.Row="1"
Style="{StaticResource BaseListBox}" x:Name="LstEquipeDefaut">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<my:ucEquipe x:Name="ucEquipe" Grid.Row="1" Margin="5,0,5,2"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is the Usercontrol :
<UserControl x:Class="ucEquipe"
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:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="180" MinWidth="180" >
<Border Style="{StaticResource UControlBorder}">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="25" />
<RowDefinition Height="35" />
<RowDefinition Height="100" />
<RowDefinition Height="35" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TextBox AllowDrop="True" x:Name="TxtChiefEquipe"
Style="{StaticResource BaseTextBox}"
Text="{Binding Mode=OneWay,Path=chefEquipe.NomComplet}"
Grid.Row="1" />
</Grid>
</Border>
Here is the objets I use :
Public Class Equipe
Public Property ID As Long = 0
Public Property Couleur As String = ""
Public Property Semaine As New Date(1900, 1, 1)
Public Property chefEquipe As Employe = Nothing
Public Property ListEquipeEmploye As New List(Of EquipeEmploye)
Public Property ListEquipeEquipement As New List(Of EquipeEquipement)
End Class
The objet Employe have a property called NomComplet. For now I manually added new objects in the listbox for testing.

Your Equipe class needs to implement INotifyPropertyChanged
private Employe _chefEquipe;
public Employe ChefEquipe
{
get { retun _chefEquipe; }
set
{
_chefEquipe = value;
NotifyPropertyChanged("ChefEquipe");
}
}
Sorry about the C#, I don't remember the VB syntax anymore =)

Related

WPF Core 3.1 bound TextBox not visible at runtime

Learning WPF Core 3.1 WPF MVVM pattern, using Visual Studio 2019 4.8.04084.
I have a MainWindow with the following (boilerplate excluded)
<Window>
<Grid
Width="1024"
Height="768"
Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<usercontrols:CustomerMaintenanceControl
x:Name="CustomerMaintenance"
Grid.Row="0"
Height="109"
VerticalAlignment="Top" />
</Grid>
</Window>
The CustomerMaintenanceControl user control looks like this:
<UserControl
x:Class="Redacted.UserControls.CustomerMaintenanceControl"
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:usercontrols="clr-namespace:Redacted.UserControls"
xmlns:vm="clr-namespace:Redacted.ViewModels"
d:DesignHeight="450"
d:DesignWidth="800"
Loaded="UserControl_Loaded"
mc:Ignorable="d">
<UserControl.Resources>
<vm:CustomerMaintenanceViewModel x:Key="viewModel" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<usercontrols:CustomerMaintenanceGridControl
x:Name="gridControl"
Grid.Row="0"
Height="200"
DataContext="{StaticResource viewModel}" />
<usercontrols:CustomerMaintenanceDetailControl
x:Name="detailControl"
Grid.Row="1"
Width="800"
Height="200"
HorizontalAlignment="Left"
VerticalAlignment="Center"
DataContext="{StaticResource viewModel}" />
</Grid>
</UserControl>
Here's the CustomerMaintenanceDetailControl, where the issue is:
<UserControl
x:Class="Redacted.UserControls.CustomerMaintenanceDetailControl"
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:Redacted"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d">
<Grid Margin="0,0,0,325">
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TabControl
x:Name="tabControl"
Grid.Row="0"
Margin="0,0,0,0">
<TabItem>
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock>General</TextBlock>
</StackPanel>
</TabItem.Header>
<StackPanel>
<TextBox
x:Name="companynmTextbox"
Width="322"
Margin="0,32,228,0"
HorizontalAlignment="Left"
IsReadOnly="True"
Text="{Binding Path=Entity.Companynm}" />
</StackPanel>
</TabItem>
</TabControl>
</Grid>
</UserControl>
So the CustomerMaintenanceControl has two user controls in it, a CustomerMaintenanceGridControl that has a DataGrid and then a CustomerMaintenanceDetailControl which has a tab control and a bound TextBox. The grid is pulling data in fine, so the model and data layer are working. However the 'companyNmTextBox' control on CustomerMaintenanceGridControl is displaying nothing.
There are no binding errors in VS. In addition, when I run the application and open the Live Visual Tree, I can open the properties for 'companyNmTextBox' and under Inherited\DataContext I have my 'Customers' ObservableCollection there and populated, and my 'Entity' property on the model populated with the data for the first Customer. Yet nothing showing onscreen.
Since people are voting to close because of 'debugging details' (?) - the debugging details are in the last paragraph - there are no binding or errors in Visual Studio, and everything appears bound OK in the Live Visual Tree/ The 'desired behaviour' is that the textbox displays the value of the thing that it is bound to. If there are further debugging steps I could investigate I'd be delighted to hear them.
Your Entity has to implement INotifyPropertyChanged interface
public class Entity : INotifyPropertyChanged
{
private string companynm;
public string Companynm
{
get
{
return companynm;
}
set
{
this.companynm = value;
NotifyPropertyChanged();
}
}
...
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Also TextBox companynmTextbox has the property IsReadOnly set to true, so in that case I would modify the binding.
Text="{Binding Path=Entity.Companynm, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
It was a typo to the call to RaisePropertyChanged() in the ViewModel the CustomerMaintenanceDetailControl is using.
I had:
private Customer _entity = new Customer();
public Customer Entity
{
get { return _entity; }
set
{
_entity = value;
RaisePropertyChanged("Customer"); // <- problem here
}
}
I should have had:
private Customer _entity = new Customer();
public Customer Entity
{
get { return _entity; }
set
{
_entity = value;
RaisePropertyChanged("Entity"); // <- doh
}
}

WPF multiple Instance of ViewModel in Usercontrol and Window

This is my first question on stackoverflow.
I have a Usercontrol that contains a viewModel as a staticresource. Than I have a Window that contains this UserModel. I want to send commands from the Window to the viewmodel that is used by the usercontrol. So I bound the Viewmodel class to the Usercontrol and to the Window as a static Resource. But the viewmodell ist instanciated 2 times. Once from the Window and once from the usercontrol. What am I doing wrong?
Here is the xaml of the usercontrol:
<UserControl x:Class="ScanVM.ScanView"
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:t="clr-namespace:ScanVM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="501.305">
<UserControl.Resources>
<t:ScanVM x:Key="ABC"/>
</UserControl.Resources>
<DockPanel DataContext="{Binding Source={StaticResource ABC}}">
<Grid ShowGridLines="True" Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="10*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Label Content="Geräte:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="0,10,10,0"/>
<DataGrid ItemsSource="{Binding Path=ScanIDs, Mode=OneWay}" Grid.Column="1" HorizontalAlignment="Left" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" AutoGenerateColumns="false" Height="152" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ScanIds}" ClipboardContentBinding="{x:Null}" Header="Bezeichnung" MinWidth="200"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</DockPanel>
This is the code of my window:
<Window x:Class="ScanViewer.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:ribbon="clr-namespace:System.Windows.Controls.Ribbon;assembly=System.Windows.Controls.Ribbon"
xmlns:self="clr-namespace:ScanViewer"
xmlns:t="clr-namespace:ScanVM;assembly=ScanVM"
mc:Ignorable="d"
Title="Scan-Viewer" Height="682.225" Width="694">
<Window.Resources>
<t:ScanVM x:Key="ABC"/>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding
Command="self:MainWindow.ExitCommand"
CanExecute="ExitCommand_CanExecute"
Executed="ExitCommand_Execute"/>
</Window.CommandBindings>
<DockPanel>
<ribbon:Ribbon DockPanel.Dock="Top">
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu Visibility="Collapsed">
<RibbonApplicationMenuItem Header="Beenden"/>
<RibbonApplicationMenu.FooterPaneContent>
<RibbonButton Label="Exit"/>
</RibbonApplicationMenu.FooterPaneContent>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
<RibbonTab Header="Start">
<RibbonGroup Header="Programm">
<RibbonButton Label="Beenden" Margin="0,5,0,0" Command="self:MainWindow.ExitCommand" />
</RibbonGroup>
<RibbonGroup Header="Scannen">
<RibbonButton Label="Daten Scannen" Margin="0,5,0,0" Command="{Binding ScanCmd, Source={StaticResource ABC}}"/>
</RibbonGroup>
</RibbonTab>
</ribbon:Ribbon>
<Grid DockPanel.Dock="Top" Height="464">
<t:ScanView DataContext="{Binding Source={StaticResource ABC}}"/>
</Grid>
</DockPanel>
And this is the code of my Viewmodel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Windows.Input;
using System.Threading;
using MVVMBase;
namespace ScanVM
{
public class ScanVM : ViewModelBase
{
private ICommand _doWorkCmd;
private ICommand _scanCmd;
private bool scanAllowed = false;
public List<int> ScanIDs
{
get;
set;
}
public ScanVM()
{
_doWorkCmd = new DelegateCommand((param) => DoWork());
_scanCmd = new DelegateCommand((param) => Scan());
}
public bool NWScanAllowed(object executeAllowed)
{
return scanAllowed;
}
public ICommand WorkCmd
{
get { return _doWorkCmd; }
}
public ICommand ScanCmd
{
get
{
return _scanCmd;
}
}
private void Scan()
{
scanAllowed = true;
}
private void DoWork()
{
}
}
}
The answer depends on what you want to do with the shared object.
You can either move ABC to App.xaml inside <Application.Resources> tag to share it throughout your project.
Or you can remove the one in user control and use the ABC in window's resources.
You will need a valid reference path to address the ABC in window resource from user control in this case.
which brings us to these two situations:
The valid path is possible to find:
So let's say if your user control is a part of the mentioned window (and if the window's DataContext is set to ABC) then inside the user control you will reference it like this:
<DockPanel DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext}">
The valid path is impossible to find and you just want the static resource. If you insist on doing it this way it is better to just move the resource to App.xaml than to try to tweak this out.

Fill textbox with a property with Binding MVVM

I try to figure out how MVVM works and want to start simple.
I made a Model, a View and a ViewModel. I hooked up the view to a window. this works more or less.
in the view i have a textbox which i want to fill with the value of a property. The textbox keeps empty!?
This is what i have:
Model:
namespace Qbox_0001.Model
{
public class RegistratieModel
{
}
public class Registratie : INotifyPropertyChanged
{
//Fields
private string programmaNaam;
//eventhandler die kijkt of een property wijzigt
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
//Properies
public string ProgrammaNaam
{
get { return programmaNaam; }
set
{
if (programmaNaam != value)
{
programmaNaam = value;
RaisePropertyChanged("ProgrammaNaam");
}
}
}
}
}
The View:
<UserControl x:Name="UserControlRegistratie" x:Class="Qbox_0001.Views.RegistratieView"
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:Qbox_0001.Views"
mc:Ignorable="d" Height="1000" Width="860" MaxWidth="860" HorizontalContentAlignment="Left" VerticalContentAlignment="Top">
<Grid x:Name="GridRegistratie">
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDef_Menu" Height="21*" MaxHeight="21" MinHeight="21"/>
<RowDefinition x:Name="RowDef_TabControl" Height="500*" MinHeight="500"/>
<RowDefinition x:Name="Rowdef_Bottom" Height="40*" MaxHeight="40" MinHeight="40"/>
</Grid.RowDefinitions>
<Grid x:Name="Grid_Registratie_WorkArea" Grid.Row="1">
<TabControl x:Name="TabControl_Registratie">
<TabItem Header="Registratie">
<Grid x:Name="Grid_Tab_Registratie">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="GridRowDef_Algemeen" MinHeight="68" Height="68*" MaxHeight="68"/>
<RowDefinition x:Name="GridRowDef_Locatie" MinHeight="120" Height="120*" MaxHeight="120"/>
<RowDefinition x:Name="GridRowDef_AantalDagen" MinHeight="105" Height="105*" MaxHeight="105"/>
<RowDefinition x:Name="GridRowDef_MaxDagen" MinHeight="105" Height="105*" MaxHeight="105"/>
<RowDefinition x:Name="GridRowDef_Lokaal" MinHeight="100" Height="100*" MaxHeight="100"/>
<RowDefinition x:Name="GridRowDef_LicBestand" Height="150*" MaxHeight="150" MinHeight="150"/>
</Grid.RowDefinitions>
<GroupBox x:Name="GroupBox_algemeen" Header="Algemeen" Margin="10,4,10,3">
<Grid>
<Label x:Name="Label_Klant" Content="Klant:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Padding="0,5,5,5"/>
<Label x:Name="Label_Programma" Content="Programma:" HorizontalAlignment="Left" Margin="356,10,0,0" VerticalAlignment="Top"/>
<Label x:Name="Label_Versie" Content="Versie:" HorizontalAlignment="Left" Margin="645,10,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="textBox_KlantNaam" HorizontalAlignment="Left" Height="23" Margin="49,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="288"/>
<!-- the textbox keeps empty -->
<TextBox x:Name="TextBox_ProgrammaNaam" Text="{Binding ElementName=RegistratieViewModel, Path=ProgrammaNaam, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="431,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="190" IsEnabled="False" />
<TextBox x:Name="TextBox_Versie" HorizontalAlignment="Left" Height="23" Margin="695,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" IsEnabled="False" />
</Grid>
</GroupBox>
</Grid>
</Border>
</Grid>
</TabItem>
<TabItem Header="Licentie(s)">
<Grid x:Name="Grid_Tab_Licentie" Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Grid>
</Grid>
</UserControl>
In the View.cs:
namespace Qbox_0001.Views
{
/// <summary>
/// Interaction logic for RegistratieView.xaml
/// </summary>
public partial class RegistratieView : UserControl
{
public RegistratieView()
{
InitializeComponent();
this.DataContext = new Qbox_0001.ViewModel.RegistratieViewModel();
}
}
}
The ViewModel
using Qbox_0001.Model; //
using System.Collections.ObjectModel; //
namespace Qbox_0001.ViewModel
{
public class RegistratieViewModel
{
public RegistratieViewModel()
{
loadRegistratieOnderdelen();
}
public ObservableCollection<Registratie> RegistratieOnderdelen //Registratie = "public class Registratie : INotifyPropertyChanged" van de Model
{
get;
set;
}
public void loadRegistratieOnderdelen()
{
ObservableCollection<Registratie> regOnderdelen = new ObservableCollection<Registratie>();
regOnderdelen.Add(new Registratie { ProgrammaNaam = "Test" });
}
}
}
I can see a couple of problems with your code.
You are populating a local ObservableCollection (inside your loadRegistratieOnderdelen() method) with data but since its local, it is not a member of the DataContext and hence unavailable to the View. You have to use public properties like RegistratieOnderdelen which you already declared in your RegistratieViewModel.
Next you are using an ObservableCollection whereas you might just want to use a property of type String. Collections are used when you want to represent lists, for example inside a ListView or an ItemsControl. Your view indicates that you want to bind a single text value so a public property of type String makes more sense.
Best, Nico
The DataContext is a RegistratieViewModel. This class has a RegistratieOnderdelen property that returns a collection of Registratie objects.
You could bind to the ProgrammaNaam property of one such item but you need to specify which item to bind to, for example the first one:
<TextBox x:Name="TextBox_ProgrammaNaam" Text="{Binding Path=RegistratieOnderdelen[0].ProgrammaNaam, UpdateSourceTrigger=PropertyChanged}" ... />

Binding different properties to different sources

I have a Window with a ListBox which has a DataTemplate, bound to an ObservableCollection of LogItems. The ItemsSource of the ListBox is set in code to the collection; the bindings on the TextBox and TextBlock which make up the DataTemplate are set in XAML. So far, so conventional. However, I need to set the font size/family for the TextBlock at runtime. Currently this information is held in a static cGlobals class. So I need to be able to bind the TextBlock.Text to the LogItems collection, but the TextBlock.FontSize property to the cGlobals.LogFontSize property. How can I do this, either via binding as sketched out in the XAML below, or in code?
<ListBox . . . . >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" . . . . >
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" MinHeight="40" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Background="Honeydew" Text="{Binding Mode=OneWay, Path=Header, . . . . />
<TextBlock FontSize="{Binding ??????}" Grid.Row="1" Text="{Binding Path=BodyText}" />
</Grid>
</DataTemplate >
</ListBox.ItemTemplate >
</ListBox>
xaml
<Window x:Class="WpfApplication6.StaticBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication6"
Title="StaticBinding" Height="300" Width="300">
<Grid>
<TextBlock FontSize="{Binding Source={x:Static local:Global.FontSize}}" Text="abc"/>
</Grid>
Global
public class Global
{
public static double FontSize
{
get { return 20.0; }
}
}
You will need to declare a public property of Type cGlobals but the class cannot be static because you will need to use it as a return type. It does not look like you are following the Model-View-ViewModel pattern, since you are assigning the ItemsSource in the code-behind instead of XAML, so you will need to declare the property in the code-behind. In your Code-Behind(your .xaml.cs file)
private CGlobals _cGlobals;
public CGlobals CGlobals{get{return _cGlobals;}}
public CodeBehindConstructor(){
_cGlobals = new CGlobal{FontSize = 12, FontFamily="Times New Roman"};
}
xaml:
<Window Name="TheWindow">
<TextBlock FontSize="{Binding CGlobals.FontSize, ElementName=TheWindow}" Grid.Row="1" Text="{Binding Path=BodyText}" />
</Window>

How to propagate silverlight usercontrol properties to parent?

I would like to have some of the properties in the custom user control to be available to the parent page. I created a small sample to illustrate what I am looking for.
I am trying to use MVVM pattern and all the binding mechanisms to achieve it.
USERCONTROL XAML
<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
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:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
<Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
</Grid>
</StackPanel>
</Grid>
the above Usercontrol is binded to the following VIEWMODEL
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class UserNameViewModel : BaseViewModel
{
private String _firstName;
public String FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyPropertyChanged("FirstName");
OnNameChange();
}
}
private String _lastName;
public String LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyPropertyChanged("LastName");
OnNameChange();
}
}
private void OnNameChange()
{
FullName = String.Format("{0} {1}", FirstName, LastName);
}
public String _fullName;
public String FullName
{
get { return _fullName; }
set {
_fullName = value;
NotifyPropertyChanged("FullName");
}
}
}
Consumer Page that uses the above USERCONTROL
<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls" x:Class="TestCustomUserControl.Views.ConsumeName"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="ConsumeName Page">
<Grid x:Name="LayoutRoot">
<StackPanel>
<my:UserNameControl x:Name="MyNameControl"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding FullName, ElementName=MyNameControl}"/>
</Grid>
</StackPanel>
</Grid>
Here is my question now, If you look at the view model associated with user control, it has a property called FullName and I would like that to be exposed via Usercontrol, so that I can access it from the consuming page. Its like consuming page want to access some of the properties of usercontrol. I am not quite sure as to how that can be acheived. I would like to stick with MVVM pattern.
you have already decalred a StaticResource, so you can use it in both views.
Do this
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
in the "ConsumeName Page". If yous simply add the DataContext to your Grid
<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheViewModel}">
this should work. (You don't need ElementName=MyNameControl any longer).
The my:UserNameControl should inherit the DataContext. If not, you have to add it here.
<my:UserNameControl DataContext="{StaticResource TheViewModel}"/>
This should work.
Right now the local:UserNameViewModel with the key TheViewModel is only achievable where you defined it. If you define it in your app.xaml you can access it from everywhere in the app.
Hope this hels.
BR,
TJ
I am answering my own question. But before answering my question, just want to clarify somethings. I was trying to create a fully encapsulated UserControl with its own ViewModel. And where ever the usercontrol is consumed, the consumer should know nothing about the usercontrol's internal viewmodel. Only communication option I want the consumer to have is by setting some properties and using binding mechanism.
So the way I resolved my problem is, I created dependency property inside the UserControl and setting it whenever something changes in the usercontrol's viewmodel.
UserNameControl.xaml (usercontrol)
<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
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:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
<Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
</Grid>
</StackPanel>
</Grid>
BaseViewModel.cs
public class BaseViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
UserNameViewModel.cs
public class UserNameViewModel : BaseViewModel
{
private String _firstName;
public String FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyPropertyChanged("FirstName");
OnNameChange();
}
}
private String _lastName;
public String LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyPropertyChanged("LastName");
OnNameChange();
}
}
public Action NameChanged { get; set; }
private void OnNameChange()
{
FullName = String.Format("{0} {1}", FirstName, LastName);
if(NameChanged != null) NameChanged.Invoke();
}
private String _fullName;
public String FullName
{
get { return _fullName; }
set {
_fullName = value;
NotifyPropertyChanged("FullName");
}
}
}
UserNameControl.xaml.cs (Usercontrol code behind with DependencyProperty declaration)
public partial class UserNameControl : UserControl
{
private UserNameViewModel _TheViewModel;
public UserNameControl()
{
InitializeComponent();
_TheViewModel = Resources["TheViewModel"] as UserNameViewModel;
_TheViewModel.NameChanged = OnNameChanged;
}
public String SelectedFullName
{
get { return (String) GetValue(SelectedFullNameProperty); }
set { SetValue(SelectedFullNameProperty, value); }
}
public static readonly DependencyProperty SelectedFullNameProperty =
DependencyProperty.Register("SelectedFullName", typeof (String), typeof (UserNameControl), null);
private void OnNameChanged()
{
SelectedFullName = _TheViewModel.FullName;
}
}
ConsumeName.xaml (Consumer navigation Page, user above usercontrol and pulls SelectedFullName into UserFullName)
<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls" x:Class="TestCustomUserControl.Views.ConsumeName"
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"
mc:Ignorable="d"
xmlns:local="clr-namespace:TestCustomUserControl.ViewModels"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="ConsumeName Page">
<navigation:Page.Resources>
<local:ConsumeNameViewModel x:Key="TheConsumeNameViewModel"/>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheConsumeNameViewModel}">
<StackPanel>
<my:UserNameControl x:Name="MyNameControl" SelectedFullName="{Binding UserFullName, Mode=TwoWay}" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding UserFullName}"/>
</Grid>
</StackPanel>
</Grid>
ConsumeNameViewModel.cs
public class ConsumeNameViewModel : BaseViewModel
{
private string _UserFullName;
public string UserFullName
{
get { return _UserFullName; }
set
{
if (value != _UserFullName)
{
_UserFullName = value;
NotifyPropertyChanged("UserFullName");
}
}
}
}

Resources