views:

611

answers:

1

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
A: 

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.

Spasas