I am having problems binding to my control. I would like the label(lblLabel) in my control to display the metadata from whatever is bound to the Field Property. It currently displays “Field” as a label. How do I get it to display “Customer Name :” which is the Name on the view model for property, CustomerName?
My Controls XAML
<UserControl x:Name="ctlRowItem" x:Class="ApplicationShell.Controls.RowItem"
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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:my="clr-namespace:SilverlightApplicationCore.Controls;assembly=SilverlightApplicationCore"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="g_required" Width="15" />
<ColumnDefinition x:Name="g_label" Width="200" />
<ColumnDefinition x:Name="g_control" Width="auto" />
<ColumnDefinition x:Name="g_fieldEnd" Width="*" />
</Grid.ColumnDefinitions>
<sdk:Label x:Name="lblRequired" Grid.Column="0" Grid.Row="0" />
<sdk:Label x:Name="lblLabel" Grid.Column="1" Grid.Row="3" Target="{Binding ElementName=txtControl}" PropertyPath="Field" />
<TextBox x:Name="txtControl" Grid.Column="2" Grid.Row="3" MaxLength="10" Width="150" Text="{Binding Field, Mode=TwoWay, ElementName=ctlRowItem}" />
</Grid>
</UserControl>
My Controls CODE BEHIND
using System.Windows;<BR>
using System.Windows.Controls;<BR>
using System.Windows.Data;<BR>
using ApplicationShell.Resources;<BR>
namespace ApplicationShell.Controls
{
public partial class RowItem : UserControl
{
#region Properties
public object Field
{
get { return (string)GetValue(FieldProperty); }
set { SetValue(FieldProperty, value); }
}
#region Dependency Properties
public static readonly DependencyProperty FieldProperty = DependencyProperty.Register("Field", typeof(object), typeof(RowItem), new PropertyMetadata(null, Field_PropertyChangedCallback));
#endregion
#endregion
#region Events
#region Dependency Properties
private static void Field_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != e.NewValue)
return;
var control = (RowItem)d;
control.Field = (object)e.NewValue;
}
#endregion
#endregion
#region Constructor
public RowItem()
{
InitializeComponent();
}
#endregion
}
}
View Model
namespace ApplicationShell.Web.ViewModel
{
[Serializable]
public class Customers
{
[Display(AutoGenerateField = false, ShortName="CustomerName_Short", Name="CustomerName_Long", ResourceType = typeof(LocaleLibrary))]
public override string CustomerName { get; set; }
}
}
XAML which calls the My Control
This pages datacontext is set to a property of type Customers (View Model).
<controls:ChildWindow x:Class="ApplicationShell.CustomerWindow"
xmlns:my="clr-namespace:SilverlightApplicationCore.Controls;assembly=SilverlightApplicationCore"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Title="Customer View">
<my:RowItem x:name="test" Field="{Binding CustomerName,Mode=TwoWay}" />
</controls:ChildWindow>
There is a way of getting at the display names of the properties bound to, but sadly it is not trivial and we have to make assumptions about the property-paths used.
I’m aware that the Silverlight Toolkit ValidationSummary is able to find out property names of bindings automatically, but when I looked through its source code, I found that it does this by doing its own evaluation of the binding path.
So, that’s the approach I’ll take here.
I modified the code-behind of your
RowItemuser-control, and this is what I came up with:There are a few things to note:
To use this code, your project will need a reference to
System.ComponentModel.DataAnnotations. However, that shouldn’t be a problem as that’s the same reference you need in order to use theDisplayattribute.The function
SetFieldLabelis called to set the label of the field. I found that the most reliable place to call it was fromDispatcher.BeginInvoke. Calling this method directly from within the constructor or from within aLoadedevent handler did not work, as the binding had not been set up by then.Only binding paths consisting of a dot-separated list of property names are supported. Something like
SomeProp.SomeOtherProp.YetAnotherPropare fine, butSomeProp.SomeList[0]is not supported and will not work. If the display name of the binding property cannot be determined, nothing will be displayed.There’s no longer a PropertyChangedCallback on the
Fielddependency property. We’re not really interested in what happens whenever the user changes the text in the control. It’s not going to change the display name of the property bound to.For test purposes, I knocked up the following view-model class:
(The Resources collection
Strings.resxcontains a single key, with nameExampleFieldNameKeyand valueThis value is in a Resources.resx. This collection also has its Access Modifier set to Public.) I tested out my modifications to your control using the following XAML, with theDataContextset to an instance of the view-model class presented above:This gave me four
RowItems, with the following labels: