Multiple instances of a Viewmodel - wpf

Im very new to the WPF and MVVM (this is my 1st project in WPF). The project I'm working on is supposed to accept some search criteria, and display the results in the grid. To construct the query I'm using Dynamic LINQ queries. I seem to have issues managing instances of my ProjectSearchViewModel which corresponds to the view that's responsible for collecting the search criteria and executing the query. One instance is created when I create MainWindowViewModel. This creates all other viewmodel instances. This is what I expect. But when time comes to Show the MainWindow, I get another ProjectSearchViewModel, I guess from the binding.
The general idea is this:
The search criteria are filled in the ProjectSearchView.
When Load Command is pressed, I send SearchResultMessage using Reactive Extensions method.
The message is picked up by MainWindowViewModel
MainWindowViewModel is querying the ProjectSearchViewModel.SearchResult and assigning the IObservable List to AllProjectsViewModel.AllProjects which is bound to a datagrid to show the results (AllProjectView is responsible to show the grid with resulting projects list)
The problem is that the parameter filling and sending of the SearchResultMessage happens in one instance of ProjectSearchViewModel and the actual querying of SearchResult from MainWindowViewModel happens in another instance, where all the search criteria are empty.
I guess I have no choice but to post my code: Here it the abridged version of it, omitting some iDisposable plumbing and such. For my model I use Entity Framework 4.
As I mentioned, I'm a total newbie so If anyone sees any blatant disregard for common sense, please set me straight.
Imports Cheminator.ViewModel
Partial Public Class App
Inherits Application
Private viewModel As MainWindowViewModel
Private window As MainWindow
Protected Overrides Sub OnStartup(ByVal e As StartupEventArgs)
MyBase.OnStartup(e)
window = New MainWindow
viewModel = New MainWindowViewModel '1st instance of ProjectSearchViewModel created Here
window.DataContext = viewModel
window.Show() '2nd instance of ProjectSearchViewModel created Here
End Sub
End Class
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cheminator"
xmlns:vm="clr-namespace:Cheminator.ViewModel"
xmlns:vw="clr-namespace:Cheminator.Views"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxd="http://schemas.devexpress.com/winfx/2008/xaml/docking"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxnb="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="DXWpfApplication" Height="600" Width="800"
dx:ThemeManager.ThemeName="Office2007Blue"
>
<Window.Resources>
<ResourceDictionary Source="MainWindowResources.xaml" />
</Window.Resources>
<dxd:DockLayoutManager>
<dxd:LayoutGroup>
<dxd:LayoutGroup Orientation="Vertical" Width="3*">
<dxd:DocumentGroup Height="3*" SelectedTabIndex="0">
<dxd:DocumentPanel Caption="Document1" Height="3*" >
<ContentControl
Content="{Binding Path=ProjectsVM}"
/>
</dxd:DocumentPanel>
</dxd:DocumentGroup>
<dxd:LayoutPanel Caption="Search Criteria" Height="*" CaptionImage="Images/Icons/DetailView.png">
<ContentControl
Content="{Binding Path=ProjectsSearchVM}"
/>
</dxd:LayoutPanel>
</dxd:LayoutGroup>
</dxd:LayoutGroup>
</dxd:DockLayoutManager>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Cheminator"
xmlns:vm="clr-namespace:Cheminator.ViewModel"
xmlns:vw="clr-namespace:Cheminator.Views" >
<DataTemplate DataType="{x:Type vm:AllProjectsViewModel}">
<vw:AllProjectsView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ProjectSearchViewModel}">
<vw:ProjectSearchView />
</DataTemplate>
<UserControl x:Class="Cheminator.Views.AllProjectsView"
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:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxd="http://schemas.devexpress.com/winfx/2008/xaml/docking"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Cheminator.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<dxg:GridControl AutoPopulateColumns="True" ShowBorder="False" >
<dxg:GridControl.DataSource>
<Binding Path="AllProjects"/>
</dxg:GridControl.DataSource>
<dxg:GridControl.View>
<dxg:TableView>
</dxg:TableView>
</dxg:GridControl.View>
</dxg:GridControl>
</UserControl>
<UserControl x:Class="Cheminator.Views.ProjectSearchView"
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:vm="clr-namespace:Cheminator.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="160" d:DesignWidth="470">
<Grid Height="160" Width="470">
<Grid.DataContext>
<vm:ProjectSearchViewModel />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="175*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="Cotation ID:" Height="28" Margin="49,12,32,0" Name="Label1" VerticalAlignment="Top" />
<TextBox Grid.Column="1" Height="23" Margin="0,14,159,0" Name="CotationIDTextBox" VerticalAlignment="Top" Text="{Binding Path=CotationID, UpdateSourceTrigger=LostFocus}"/>
<Label Grid.Row="1" Content="Cotation Name:" Height="28" Margin="49,6,6,0" Name="Label2" VerticalAlignment="Top" />
<TextBox Grid.Row="1" Grid.Column="1" Height="23" Margin="0,8,159,0" Name="CotationNameTextBox" VerticalAlignment="Top" Text="{Binding Path=ProjectSummary, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="2" Content="User:" Height="28" Margin="49,6,32,0" Name="Label3" VerticalAlignment="Top" />
<TextBox Grid.Row="2" Grid.Column="1" Height="23" Margin="0,8,159,0" Name="UserTextBox" VerticalAlignment="Top" Text="{Binding Path=UserName, UpdateSourceTrigger=PropertyChanged}"/>
<Button
Command="{Binding Path=LoadCommand}"
Content="_Load"
HorizontalAlignment="Right"
Margin="0,10,51,12"
MinWidth="60" Grid.Row="3" Width="72" Grid.Column="1" />
</Grid>
</UserControl>
Public Class MainWindowViewModel
Inherits ViewModelBase
Private _commands As ReadOnlyCollection(Of CommandViewModel)
Private _ProjectsVM As AllProjectsViewModel
Private _ProjectsSearchVM As ProjectSearchViewModel
Public Sub New()
MyBase.DisplayName = "Cheminator"
_ProjectsSearchVM = New ProjectSearchViewModel
Messenger.[Default].OfType(Of SearchResultMessage) _
.Subscribe(Sub(param As SearchResultMessage)
ProjectsVM.AllProjects = ProjectsSearchVM.SearchResult
End Sub)
_ProjectsVM = New AllProjectsViewModel
End Sub
Public ReadOnly Property ProjectsVM As AllProjectsViewModel
Get
If (_ProjectsVM IsNot Nothing) Then
Return _ProjectsVM
End If
Return Nothing
End Get
End Property
Public ReadOnly Property ProjectsSearchVM As ProjectSearchViewModel
Get
If (_ProjectsSearchVM IsNot Nothing) Then
Return _ProjectsSearchVM
End If
Return Nothing
End Get
End Property
End Class
Public Class AllProjectsViewModel
Inherits ViewModelBase
Private m_ProjectsList As ObservableCollection(Of xGMV_Cotation)
Public Sub New()
MyBase.DisplayName = "Temp AllProjectsViewModel Name" 'Strings.AllProjectsViewModel_DisplayName
End Sub
Public Property AllProjects() As ObservableCollection(Of xGMV_Cotation)
Get
Return m_ProjectsList
End Get
Set(ByVal value As ObservableCollection(Of xGMV_Cotation))
m_ProjectsList = value
OnPropertyChanged("AllProjects")
End Set
End Property
End Class
Public Class ProjectSearchViewModel
Inherits ViewModelBase
Private m_ProjectsList As ObservableCollection(Of xGMV_Cotation)
Public Sub New()
MyBase.DisplayName = "Cheminator.ProjectSearchViewModel"
End Sub
Dim _CotationID As Integer
Public Property CotationID As Integer
Get
Return _CotationID
End Get
Set(ByVal value As Integer)
_CotationID = value
MyBase.OnPropertyChanged("CotationID")
End Set
End Property
Public Property ProjectSummary As String
Public Property UserName As String
Private m_LoadCommand As RelayCommand
Public ReadOnly Property LoadCommand As ICommand
Get
If m_LoadCommand Is Nothing Then
Dim LoadAction As New Action(Of Object)(AddressOf Me.Load)
m_LoadCommand = New RelayCommand(LoadAction)
End If
Return m_LoadCommand
End Get
End Property
Public ReadOnly Property SearchResult() As ObservableCollection(Of xGMV_Cotation)
Get
Dim xWhere As String = ""
Dim i As Integer = 0
Dim parameterList As New ArrayList
If Not String.IsNullOrEmpty(CotationID) Then
xWhere = String.Format("CotationID = #{0}", i)
parameterList.Add(CotationID)
i += 1
End If
If Not String.IsNullOrEmpty(ProjectSummary) Then
If i > 0 Then
xWhere = xWhere & " AND "
End If
xWhere = xWhere & String.Format("ProjectSummary = '#{0}'", i)
i += 1
parameterList.Add(ProjectSummary)
End If
If Not String.IsNullOrEmpty(UserName) Then
If i > 0 Then
xWhere = xWhere & " AND "
End If
xWhere = xWhere & String.Format("UserName = '#{0}'", i)
i += 1
parameterList.Add(UserName)
End If
Return New ObservableCollection(Of xGMV_Cotation)(DataContext.DBEntities.xGMV_Cotations.Where(xWhere, parameterList.ToArray))
End Get
End Property
Private Sub Load()
OnPropertyChanged("SearchResult")
Messenger.Default.Send(New SearchResultMessage())
End Sub
End Class

everytime you create a ProjectSearchView usercontrol, you also create a ProjectSearchViewModel too. you should remove the following from your usercontrl xaml.
<Grid.DataContext>
<vm:ProjectSearchViewModel />
</Grid.DataContext>
because of your datatemplate you do not need to set the datacontext for ProjectSearchView, it already has the right datacontext.

Related

Binding an ObservableCollection Dependency Property in a User Control

I have a UserControl that hosts a TreeView that has a custom collection which inherits from ObservableCollection of a custom class. I am trying to bind to a property using a dependency property from a ViewModel, whilst I have proven the process works for another property it does not work here.
I'm sure it is something silly I have missed but I've been going round in circles, can anyone spot my mistake?
The UserControl XAML:
<UserControl
x:Class="FileExplorer.TreeViewUserControl"
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:fe="clr-namespace:WpfControlLibrary.FileExplorer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="FileExplorer"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<TreeView
BorderThickness="0"
ItemsSource="{Binding FileSystem, RelativeSource={RelativeSource AncestorType=UserControl}}"
TreeViewItem.Collapsed="TreeView_Collapsed"
TreeViewItem.Expanded="TreeView_Expanded"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type fe:FileSystemObject}" ItemsSource="{Binding Items}">
<StackPanel Margin="0,2" Orientation="Horizontal">
<Image
Width="14"
Margin="2"
Source="{Binding Image}"
Stretch="Fill" />
<TextBlock
VerticalAlignment="Center"
FontSize="{StaticResource FontSizeSmall}"
Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The code behind for the UserControl:
Namespace FileExplorer
Public Class TreeViewUserControl
Inherits UserControl
#Region "Constructors"
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.DataContext = Me
End Sub
#End Region
#Region "Properties"
Public Shared ReadOnly FileSystemProperty As DependencyProperty = DependencyProperty.Register("FileSystem", GetType(FileSystemObjectCollection), GetType(TreeViewUserControl))
Public Property FileSystem As FileSystemObjectCollection
Get
Return CType(GetValue(FileSystemProperty), FileSystemObjectCollection)
End Get
Set(ByVal value As FileSystemObjectCollection)
SetValue(FileSystemProperty, value)
FileExplorer.UpdateLayout()
End Set
End Property
#End Region
Private Function GetFileSystemInfo(ByVal root As String) As FileSystemObjectCollection
Dim items As New FileSystemObjectCollection
' Parse all the directories at the path
For Each dir As String In Directory.GetDirectories(root)
items.Add(New FileSystemObject(dir, root))
Next
' Parse all the file at the path
For Each file As String In Directory.GetFiles(root)
items.Add(New FileSystemObject(file, root))
Next
Return items
End Function
Private Sub TreeView_Collapsed(sender As Object, e As RoutedEventArgs)
Dim node As TreeViewItem = CType(e.OriginalSource, TreeViewItem)
Dim fs As FileSystemObject = CType(node.DataContext, FileSystemObject)
fs.Clear()
End Sub
Private Sub TreeView_Expanded(sender As Object, e As RoutedEventArgs)
Dim node As TreeViewItem = CType(e.OriginalSource, TreeViewItem)
Dim fs As FileSystemObject = CType(node.DataContext, FileSystemObject)
If Not fs.HasChildren Then Exit Sub
fs.Items = GetFileSystemInfo(fs.FullName)
End Sub
End Class
End Namespace
The MainWindow XAML:
<Window
x:Class="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:fe="clr-namespace:WpfControlLibrary.FileExplorer;assembly=WpfControlLibrary"
xmlns:local="clr-namespace:ModStudio.Client"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:ModStudio.Client.ViewModels"
Title="MainWindow"
d:DesignHeight="600"
d:DesignWidth="800"
WindowStartupLocation="CenterOwner"
WindowState="Maximized"
mc:Ignorable="d">
<Window.Resources>
<vm:MainWindowViewModel x:Key="MainWindowViewModel" />
</Window.Resources>
<Grid>
<fe:TreeViewUserControl DataContext="{StaticResource MainWindowViewModel}" FileSystem="{Binding ApplicationExplorer}" />
</Grid>
</Window>
The MainWindow has its DataContext set to a new instance of the ViewModel in code behind. The MainWindowViewModel has a property called ApplicationExplorer which is an instance of FileSystemObjectCollection. As mentioned FileSystemObjectCollection inherits from ObservableCollection(Of FileSystemObject). A FileSystemObject implements INotifyPropertyChanged. If I change ApplicationExplorer property, the control remains blank.
I have intentionally omitted some code here, but can add them if necessary.
Don't set the DataContext in the UserControl, i.e. remove this line:
Me.DataContext = Me
When you explcitly set the DataContext like this, you break the inheritance chain which means that the binding to the ApplicationExplorer property in your window no longer works:
<fe:TreeViewUserControl DataContext="{StaticResource MainWindowViewModel}"
FileSystem="{Binding ApplicationExplorer}" />
I made 3 changes and got this working!
Added OnPropertyChanged to ApplicationExplorer:
Public Property ApplicationExplorer As FileSystemObjectCollection
Get
Return _applicationExplorer
End Get
Set(value As FileSystemObjectCollection)
_applicationExplorer = value
OnPropertyChanged(NameOf(ApplicationExplorer))
End Set
End Property
Updated the binding in the UserControl:
<UserControl
x:Class="FileExplorer.TreeViewUserControl"
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:fe="clr-namespace:WpfControlLibrary.FileExplorer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="FileExplorer"
d:DesignHeight="300"
d:DesignWidth="400"
mc:Ignorable="d">
<TreeView
BorderThickness="0"
ItemsSource="{Binding FileSystem, RelativeSource={RelativeSource AncestorType=fe:TreeViewUserControl}}"
TreeViewItem.Collapsed="TreeView_Collapsed"
TreeViewItem.Expanded="TreeView_Expanded"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type fe:FileSystemObject}" ItemsSource="{Binding Items}">
<StackPanel Margin="0,2" Orientation="Horizontal">
<Image
Width="14"
Margin="2"
Source="{Binding Image}"
Stretch="Fill" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</UserControl>
Removed the Me.DataContext = Me from the UserControl code behind as you suggested

How do I add, save and load items to a listview in WPF

About two weeks ago I started to develop in WPF and, since I only developed in WinForms, I ran into common problems but managed to find the solution for them. However, currently I'm stuck at something: adding items with multiple columns (via visual basic code, not xaml) into a listview.
I'm not sure if it's best to use a Listview or a DataGrid control for this but basically I want the user to be able to add favourite songs to a list, save them to a file and load the file whenever the app is opened. The listview currently has three columns: Artist, Song and Status.
When I was programming in WinForms, I used to do this:
Dim Song As New ListViewItem
Form1.ListView1.Items.Add(Song)
Song.Text = TextBox1.Text
Song.SubItems.Add(TextBox2.Text)
Song.SubItems.Add(TextBox3.Text)
Then, to save:
Dim Song As New ListViewItem
Form1.ListView1.Items.Add(Song)
Song.Text = TextBox1.Text
Song.SubItems.Add(TextBox2.Text)
Song.SubItems.Add(TextBox3.Text)
Try
Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
For Each myItem As ListViewItem In Form1.ListView1.Items
myWriter.WriteLine(myItem.Text & "|" & myItem.SubItems(1).Text & "|" & myItem.SubItems(2).Text & "|" & myItem.SubItems(3).Text
Next
myWriter.Close()
Catch ex As Exception
MsgBox("Error: " & ex.Message, vbCritical, "Error")
End Try
I've been searching and I've found that I need to use binding so I've tried this:
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Windows
Public Structure Song
Public _artist As String
Public _title As String
Public _status As String
Property Artist() As String
Get
Return _artist
End Get
Set(ByVal Value As String)
_artist = Value
End Set
End Property
Property Title() As String
Get
Return _title
End Get
Set(ByVal Value As String)
_title = Value
End Set
End Property
Property Status() As String
Get
Return _status
End Get
Set(ByVal Value As String)
_status = Value
End Set
End Property
End Structure
Public Class WINDOW_AddSong
Dim songs As New ObservableCollection(Of Song)
Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
Dim Song As New ListViewItem
For Each wnd As Window In Application.Current.Windows
If wnd.GetType Is GetType(MainWindow) Then
DirectCast(wnd, MainWindow).Listview1.Items.Add(Alimento)
Alimento.Content = New Song() With {._artist = "Lol", ._title = "Lol2", ._status = "Lol3"}
End If
Next
End Sub
End Class
And in the XAML listview:
<GridViewColumn Header="Artist" DisplayMemberBinding="{Binding Artist}"/>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Title}"/>
<GridViewColumn Header="Status" DisplayMemberBinding="{Binding Status}"/>
This works however I'm not sure if this is the WPF-way of doing things.
However, I'm stuck at the saving process:
Try
Dim myWriter As New IO.StreamWriter(PATH_DATABASE)
For Each wnd As Window In Application.Current.Windows
If wnd.GetType Is GetType(MainWindow) Then
For Each myItem As ListViewItem In DirectCast(wnd, MainWindow).Listview1.Items
myWriter.WriteLine(Song, Artist, Status)
Next
End If
Next
myWriter.Close()
Catch ex As Exception
MsgBox("Error: " & ex.Message, vbCritical, "Error")
End Try
This doesn't work.
Also, PATH_DATABASE is just a directory.
To summarise, could an expert review my code and check if I'm doing things "right" and, if possible, could you help me with the saving and loading process?
Thanks.
I just updated your structure (I used a class, just force of habit) to use automatic property values which are available in recent versions of VS. The default implementation of properties is built in along with the backer fields. It is used just as before.
You are on the right track with ObservableCollection but you you need to implement INotifyPropertyChanged by adding the Event PropertyChanged and providing the Sub OnPropertyChanged to raise the event.
Set up the bindings on the controls and set the ItemsSource ot the ListView to your ObservableList.
To save Import System.Text so you can use a StringBuilder. The loop through the list to build the string to save to a text file.
I didn't spend any time trying to make the XAML Windows look nice. Pardon the ugliness.
Class MainWindow
Public Songs As New ObservableCollection(Of Song)
Protected Sub OnLoad(sender As Object, e As RoutedEventArgs)
Songs.Add(New Song("Nirvana", "Smells Like Teen Spirit", "Open"))
Songs.Add(New Song("John Lennon", "Imagine", "In Stock"))
Songs.Add(New Song("U2", "One", "Unknown"))
Songs.Add(New Song("Michael Jackson", "Billie Jean", "Open"))
lvSongs.ItemsSource = Songs
End Sub
Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
Dim frm As New frmAdd
frm.mainForm = Me 'send the instance of the current form to the new form
frm.ShowDialog()
End Sub
Private Sub SaveList()
Dim sb As New StringBuilder
For Each item As Song In Songs
sb.AppendLine($"{item.Artist}|{item.Title}|{item.Status}")
Next
'call sb.ToString and write it to a .txt file
End Sub
End Class
MainWindow XAML
<Window x:Class="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:WPF_BindComboBox"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Loaded="OnLoad"
Name="root">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView Margin="10" Name="lvSongs" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBox x:Name="txtArtist"
Text="{Binding Artist, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontWeight="Bold"/>
<TextBlock Text=", " />
<TextBlock Text="Age: " />
<TextBox Text="{Binding Title}"
FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBox Text="{Binding Status}" />
<TextBlock Text=")" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button x:Name="btnAdd" Grid.Row="1" Height="25" Width="100" Content="Add a Song" Click="BtnAdd_Click"/>
</Grid>
</Window>
The Song class
Public Class Song
Implements INotifyPropertyChanged
Public Property Artist() As String
Public Property Title() As String
Public Property Status() As String
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Sub New(art As String, Ttl As String, Stat As String)
Artist = art
Title = Ttl
Status = Stat
End Sub
End Class
The Add Window
Public Class frmAdd
Public mainForm As MainWindow
Private Sub BtnAdd_Click(sender As Object, e As RoutedEventArgs)
mainForm.Songs.Add(New Song(txtArtist.Text, txtTitle.Text, txtStatus.Text))
ClearForm()
End Sub
Private Sub ClearForm()
txtArtist.Clear()
txtTitle.Clear()
txtStatus.Clear()
End Sub
End Class
Add Window XAML
<Window x:Class="frmAdd"
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:WPF_BindComboBox"
mc:Ignorable="d"
Title="frmAdd" Height="172.641" Width="307.547">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="Artist" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtArtist" Grid.Column="1" Grid.Row="0" FontSize="16" Width="150" />
<TextBlock Grid.Column="0" Grid.Row="1" Text="Title" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtTitle" Grid.Column="1" Grid.Row="1" FontSize="16" Width="150"/>
<TextBlock Grid.Column="0" Grid.Row="2" Text="Status" FontSize="16" FontWeight="Bold"/>
<TextBox x:Name="txtStatus" Grid.Column="1" Grid.Row="2" FontSize="16" Width="150"/>
<Button x:Name="btnAdd" Grid.Column="1" Grid.Row="4" Content="Add Song" Click="BtnAdd_Click"/>
</Grid>
</Window>
EDIT
Required Imports
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Text

How to bind to properties of a UserControl inside another UserControl?

I have a simple UserControl that displays an icon and text:
<UserControl x:Class="IconLabel"
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="26" d:DesignWidth="200" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Image x:Name="imgIcon" Source="{Binding Path=IconPath}" Stretch="UniformToFill" Width="26" Height="26" Margin="3,0" />
<Label Content="{Binding Path=LabelText}" Margin="5,0" Grid.Column="1" />
</Grid>
</UserControl>
The code-behind defines two DependencyProperties that are meant to be bound from the outside:
Public Class IconLabel
Public Property IconPath As String
Get
Return GetValue(IconPathProperty)
End Get
Set(ByVal value As String)
SetValue(IconPathProperty, value)
End Set
End Property
Public Shared ReadOnly IconPathProperty As DependencyProperty = DependencyProperty.Register("IconPath", GetType(String), GetType(IconLabel), New PropertyMetadata(""))
Public Property LabelText As String
Get
Return GetValue(LabelTextProperty)
End Get
Set(ByVal value As String)
SetValue(LabelTextProperty, value)
End Set
End Property
Public Shared ReadOnly LabelTextProperty As DependencyProperty = DependencyProperty.Register("LabelText", GetType(String), GetType(IconLabel), New PropertyMetadata("LabelText"))
End Class
That's working fine so far. I can set its properties in XAML and they are getting used properly:
<local:IconLabel LabelText="Test"/>
However, I'd now like to re-use this control in another UserControl that slightly expands its functionality by showing a progress bar next to it (I've kept this short for the sake of the example):
<UserControl x:Class="IconLabelProgress"
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:myApp"
mc:Ignorable="d"
d:DesignHeight="26" d:DesignWidth="600" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" MaxWidth="300"/>
<ColumnDefinition Width="6*"/>
</Grid.ColumnDefinitions>
<local:IconLabel IconPath="{Binding Path=IconPath}" LabelText="{Binding Path=PropName}" />
<ProgressBar Value="{Binding Path=ActualValue}" Minimum="0" Maximum="10" Margin="5" Height="16" VerticalAlignment="Top" Grid.Column="1" />
</Grid>
</UserControl>
with the following code-behind:
Public Class IconLabelProgress
'These are just meant to be passed along to the IconLabel
Public Property IconPath As String
Get
Return GetValue(IconPathProperty)
End Get
Set(ByVal value As String)
SetValue(IconPathProperty, value)
End Set
End Property
Public Shared ReadOnly IconPathProperty As DependencyProperty = DependencyProperty.Register("IconPath", GetType(String), GetType(IconLabelProgress), New PropertyMetadata(""))
Public Property PropName As String
Get
Return GetValue(PropNameProperty)
End Get
Set(ByVal value As String)
SetValue(PropNameProperty, value)
End Set
End Property
Public Shared ReadOnly PropNameProperty As DependencyProperty = DependencyProperty.Register("PropName", GetType(String), GetType(IconLabelProgress), New PropertyMetadata("PropName"))
'This one is new
Public Property ActualValue As Double
Get
Return GetValue(ActualValueProperty)
End Get
Set(ByVal value As Double)
SetValue(ActualValueProperty, value)
End Set
End Property
Public Shared ReadOnly ActualValueProperty As DependencyProperty = DependencyProperty.Register("ActualValue", GetType(Double), GetType(IconLabelProgress), New PropertyMetadata(0.0))
End Class
If I now try to instantiate this control and pass in a value for the label of the inner IconLabel control, like this:
<local:IconLabelProgress x:Name="ilp1" PropName="Test" ActualValue="5.0" />
then it won't show "Test" on its label and instead fall back to its default that was specified via PropertyMetadata("LabelText"). The ActualValue is used correctly, though.
How can I make the outer control pass the value to the nested one?
As a general rule, never explicitly set the DataContext property of a UserControl as you do with
<UserControl x:Class="IconLabel" ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
Doing so effectively prevents inheriting a DataContext from the UserControl's parent, e.g. here
<local:IconLabel LabelText="{Binding Path=PropName}" ... />
where PropName is expected to be a property in the parent DataContext.
Instead of explicitly setting a UserControl's DataContext, write its "internal" Bindings with a RelativeSource like
<Label Content="{Binding Path=LabelText,
RelativeSource={RelativeSource AncestorType=UserControl}}" ... />
By default (and you didn't specify anything else) the binding is resolved from the objects DataContext. So your IconLabel searches a property with the name IconPath on its DataContext.
To specify that the place to search for the property is the outer control, you can add ElementName to the binding and set a name property on the IconLabelProgress or you specify a RelativeSource like in the second example of the accepted answer in How do I use WPF bindings with RelativeSource.
Hope it helps.

Usercontrol within Usercontrol with Data Binding

I built a simple WPF UserControl that I want to implement several of in another UserControl while maintaining the data binding. I have a simple Debug1 window that implements (1) a bound textblock (to verify data binding only) & button; (2) a single instance of the NumericUpDownBox usercontrol with binding; and (3) an instance of the parent user control containing an instance of the child user control NumericUpDownBox.
Items (1) and (2) are working. Item (3) does not. I feel like I am missing something in the implementation in the parent user control code? I also get a couple of errors that concern me (see below).
Debug1 Window xaml:
<Window x:Class="Debug1"
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:CFIT"
mc:Ignorable="d"
Title="Debug1" Height="225" Width="600"
Closing="ClosingDebug1">
<Grid>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button Name="NextButton" Content="Next" Click="ClickNext" Width="50" />
<TextBox Text="{Binding Path=Years, Mode=TwoWay}" />
</StackPanel>
<local:NumericUpDownBox Grid.Row="1" x:Name="SingleBox" Label="TBD" Increment="2" Value="{Binding Path=Years}"/>
<local:YMDH_TimeControl Grid.Row="2" x:Name="TimeControl" YearsValue="{Binding Path=Years}" />
</Grid>
</Window>
Debug Window VB Code:
Public Class Debug1
Public Mat As New clsMaterial
Sub New() ' Removed for brevity
End Sub
Private Sub ClickNext(sender As Object, e As RoutedEventArgs)
Mat.Lives(0).MSDateOffset.Years = Mat.Lives(0).MSDateOffset.Years + 5
End Sub
End Class
Parent UserControl xaml:
<UserControl x:Class="YMDH_TimeControl"
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:CFIT"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="200" MinHeight="30" MinWidth="160"
Name="YMDH_TimeControl">
<Grid DataContext="{Binding ElementName=YMDH_TimeControl}">
<local:NumericUpDownBox Grid.Column="0" x:Name="Years"/>
</Grid>
</UserControl>
Parent UserControl VB Code:
Public Class YMDH_TimeControl
Public Shared ReadOnly YearsValueProperty As DependencyProperty = DependencyProperty.Register("YearsValue", GetType(Integer), GetType(YMDH_TimeControl), New FrameworkPropertyMetadata(0, (FrameworkPropertyMetadataOptions.BindsTwoWayByDefault), Nothing, New CoerceValueCallback(AddressOf OnValueChanged)))
Private Shared Function OnValueChanged(d As DependencyObject, value As Object) As Object
If IsNumeric(value) Then
Return CInt(value)
Else
Return Nothing
End If
End Function
Public Property YearsValue As Integer
Get
Return Years.Value
End Get
Set(value As Integer)
Years.Value = value
End Set
End Property
End Class
Child UserControl xaml:
<UserControl x:Class="NumericUpDownBox"
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:CFIT"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50" MinHeight="30" MinWidth="40"
Name="NumericUpDownBox">
<Grid DataContext="{Binding ElementName=NumericUpDownBox}">
<Grid.Resources>
... removed for brevity...
</Grid.Resources>
<!--Box Definition-->
<Border Style="{StaticResource SectionNameBorder}" Grid.Column="0">
<Grid Background="Gray" >
<Viewbox>
<TextBlock Name="LabelTextBlock" Text="{Binding Path=Label, ElementName=NumericUpDownBox}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Viewbox>
<Grid Grid.Row="2" Background="White" >
<Grid Grid.Column="0" HorizontalAlignment="Right">
<Viewbox>
<TextBox Name="ValueTextBox" BorderBrush="White" Text="{Binding Path=Value, Mode=TwoWay, ElementName=NumericUpDownBox, UpdateSourceTrigger=PropertyChanged}" />
</Viewbox>
</Grid>
<Grid Grid.Column="1">
<RepeatButton Name="UpArrow" Style="{StaticResource UpArrowStyle}" Tag="+" Click="TickValues" />
<RepeatButton Name="DownArrow" Style="{StaticResource DownArrowStyle}" Tag="-" Click="TickValues" />
</Grid>
</Grid>
</Grid>
</Border>
</Grid>
</UserControl>
Child UserControl VB Code:
Public Class NumericUpDownBox
Private Const DefaultInc As Integer = 1
Public Sub New()
InitializeComponent()
If Increment = 0 Then Increment = DefaultInc
End Sub
Private Sub TickValues(sender As Object, e As RoutedEventArgs)
Dim Sign As Char
Dim Tag As String
Tag = sender.tag.ToString
If Len(Tag) = 0 Then Exit Sub
Sign = Left(Tag, 1)
If Sign = "+" Then
Value = Value + Increment
ElseIf Sign = "-" Then
Value = Value - Increment
End If
End Sub
Public Property Value As Integer
Get
Return CInt(GetValue(ValueProperty))
End Get
Set(value As Integer)
If value < 0 Then value = 0
SetValue(ValueProperty, value)
End Set
End Property
Public Property Label As String
Get
Return GetValue(LabelProperty)
End Get
Set(value As String)
SetValue(LabelProperty, value)
End Set
End Property
Public Property Increment As Integer
Get
Return CInt(GetValue(IncProperty))
End Get
Set(value As Integer)
If value < 0 Then value = 0
SetValue(IncProperty, value)
End Set
End Property
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(Integer), GetType(NumericUpDownBox), New FrameworkPropertyMetadata(0, (FrameworkPropertyMetadataOptions.BindsTwoWayByDefault), Nothing, New CoerceValueCallback(AddressOf OnValueChanged)))
Public Shared ReadOnly LabelProperty As DependencyProperty = DependencyProperty.Register("Label", GetType(String), GetType(NumericUpDownBox), New FrameworkPropertyMetadata(""))
Public Shared ReadOnly IncProperty As DependencyProperty = DependencyProperty.Register("Increment", GetType(Integer), GetType(NumericUpDownBox), New FrameworkPropertyMetadata(0, Nothing, New CoerceValueCallback(AddressOf OnValueChanged)))
Private Shared Function OnValueChanged(d As DependencyObject, value As Object) As Object
If IsNumeric(value) Then
Return CInt(value)
Else
Return Nothing
End If
End Function
End Class
Example Information Error in the Output Window:
System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=Years; DataItem=null; target element is 'YMDH_TimeControl' (Name='TimeControl'); target property is 'YearsValue' (type 'Int32')
How do I get my data binding to pass through my parent user control?

Binding Silverlight 3 description attribute of dataInput:DescriptionViewer

Silverlight's dataInput:Label and dataInput:DescriptionViewer controls provide a useful function through their ability to bind to a TextBox. Rather than hard-coding the text content of my labels and descriptions with System.ComponentModel.DataAnnotations.DisplayAttribute, I'd prefer to bind these attributes to properties of a class that holds admin-editable text. This works for Label, but not for DescriptionViewer.
Specifically, this Label works:
<dataInput:Label Grid.Row="00" Grid.Column="0"
Target="{Binding ElementName=inpStatus}"
Content="{Binding StatusLabel}" IsRequired="true" />
but this DescriptionViewer is invisible:
<dataInput:DescriptionViewer Grid.Row="00" Grid.Column="3"
Target="{Binding ElementName=inpStatus}"
Name="dvBound2" Description="{Binding Path=StatusDescription}" />
When I remove the binding to my TextBox, the DescriptionViewer is shown:
<dataInput:DescriptionViewer Grid.Row="00" Grid.Column="2"
Name="dvBound1" Description="{Binding Path=StatusDescription}" />
Should it be possible to bind a DescriptionViewer to both a TextBox and an alternate Description source?
Thanks in advance!
MainPage.xaml:
<UserControl x:Class="DataInputDemo.MainPage"
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:controlsToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="142"></ColumnDefinition>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="24"></ColumnDefinition>
<ColumnDefinition Width="24"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<dataInput:Label Grid.Row="00" Grid.Column="0"
Target="{Binding ElementName=inpStatus}"
Content="{Binding StatusLabel}" IsRequired="true" />
<TextBox Grid.Row="00" Grid.Column="1"
Text="{Binding Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True, Path=Status}"
Name="inpStatus" MaxLength="025"
BindingValidationError="_BindingValidationError" />
<dataInput:DescriptionViewer Grid.Row="00" Grid.Column="2"
Name="dvBound1" Description="{Binding Path=StatusDescription}" />
<!-- Following DescriptionViewer is not shown. -->
<dataInput:DescriptionViewer Grid.Row="00" Grid.Column="3"
Target="{Binding ElementName=inpStatus}"
Name="dvBound2" Description="{Binding Path=StatusDescription}" />
<dataInput:ValidationSummary Grid.Row="01" Grid.Column="0"
Grid.ColumnSpan="4" Name="VS1" />
</Grid>
</UserControl>
MainPage.xaml.vb:
Partial Public Class MainPage
Inherits UserControl
Public myDataClass = New DataClass
Public Sub New()
InitializeComponent()
myDataClass.status = "Default Status"
myDataClass.StatusLabel = "Status Label"
myDataClass.StatusDescription = "Status Description"
LayoutRoot.DataContext = myDataClass
End Sub
Private Sub _BindingValidationError(ByVal sender As Object, ByVal e As ValidationErrorEventArgs)
End Sub
End Class
DataClass.vb:
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Runtime.Serialization
Imports System.ComponentModel.DataAnnotations
Imports System.Windows.Controls
Public Class DataClass
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Function ValidateEntry(ByVal fieldname As String, ByRef value As Object) As Object
Dim ctx As New ValidationContext(Me, Nothing, Nothing)
ctx.MemberName = fieldname
Validator.ValidateProperty(value, ctx)
Return value
End Function
<DataMember()> _
<Required()> _
<StringLength(100)> _
Public Property Status() As String
Get
Return _Status
End Get
Set(ByVal value As String)
_Status = ValidateEntry("Status", value)
NotifyPropertyChanged("")
End Set
End Property
Private _Status As String
<DataMember()> _
Public Property StatusLabel() As String
Get
Return _StatusLabel
End Get
Set(ByVal value As String)
_StatusLabel = value
NotifyPropertyChanged("")
End Set
End Property
Private _StatusLabel As String
<DataMember()> _
Public Property StatusDescription() As String
Get
Return _StatusDescription
End Get
Set(ByVal value As String)
_StatusDescription = value
NotifyPropertyChanged("")
End Set
End Property
Private _StatusDescription As String
End Class
I had the exact same problem with the DescriptionViewer using this markup:
<TextBox x:Name="UserNameTextBox" Text="{Binding UserName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1"></TextBox>
<dataInput:DescriptionViewer Target="{Binding ElementName=UserNameTextBox}" Description="{Binding Path=CreateUserResources.UserNameDescription, Source={StaticResource GlobalResources}}" Grid.Column="2" Grid.Row="1"></dataInput:DescriptionViewer>
Before binding the Description to its resource it was a plain string in the markup and it did work.
To resolve the problem I have set the PropertyPath of the DescriptionViewer with the property name where the binding is located on the Target control. It is now working correctly.
<TextBox x:Name="UserNameTextBox" Text="{Binding UserName, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true, UpdateSourceTrigger=Explicit}" Grid.Column="1" Grid.Row="1"></TextBox>
<dataInput:DescriptionViewer Target="{Binding ElementName=UserNameTextBox}" PropertyPath="Text" Description="{Binding Path=CreateUserResources.UserNameDescription, Source={StaticResource GlobalResources}}" Grid.Column="2" Grid.Row="1"></dataInput:DescriptionViewer>
If you are using MVVM make sure the PropertyPath does not match a property of your presenter model. I did not investigate but I had an instance where I was forced to change the property name of my presenter to make it work. Seems like the control was trying to parse the metadata from the property of my presenter instead of the target control.
Hope this help.

Resources