0

コントロールへのバインドに問題があります。コントロールの label(lblLabel) に、Field プロパティにバインドされているものからメタデータを表示したいと思います。現在、「フィールド」をラベルとして表示しています。プロパティ CustomerName のビュー モデルの名前である "Customer Name :" を表示するにはどうすればよいですか?

マイ コントロール 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>

マイ コントロール コード ビハインド

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

    }
}

モデルを見る

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; }
    }
}

My Control を呼び出す XAML

このページのデータ コンテキストは、Customers (ビュー モデル) 型のプロパティに設定されます。

<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>
4

1 に答える 1

0

バインドされたプロパティの表示名を取得する方法はありますが、残念ながらそれは簡単ではなく、使用されるプロパティ パスについて推測する必要があります。

Silverlight Toolkit ValidationSummaryがバインディングのプロパティ名を自動的に検出できることは知っていますが、ソース コードを調べたところ、バインディング パスを独自に評価することでこれを行っていることがわかりました。

だから、それが私がここでとるアプローチです。

RowItemユーザー コントロールのコード ビハインドを変更したところ、次のようになりました。

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

public partial class RowItem : UserControl
{
    public RowItem()
    {
        InitializeComponent();
        Dispatcher.BeginInvoke(SetFieldLabel);
    }

    public string Field
    {
        get { return (string)GetValue(FieldProperty); }
        set { SetValue(FieldProperty, value); }
    }

    public static readonly DependencyProperty FieldProperty =
        DependencyProperty.Register("Field", typeof(string), typeof(RowItem),
                                    null);

    /// <summary>
    /// Return the display name of the property at the end of the given binding
    /// path from the given source object.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The display name of the property is the name of the property according
    /// to a <see cref="DisplayAttribute"/> set on the property, if such an
    /// attribute is found, otherwise the name of the property.
    /// </para>
    /// <para>
    /// This method supports dot-separated binding paths only.  Binding
    /// expressions such <c>[0]</c> or <c>(...)</c> are not supported and will
    /// cause this method to return null.
    /// </para>
    /// <para>
    /// If no suitable property could be found (due to an intermediate value
    /// of the property-path evaluating to <c>null</c>, or no property with a
    /// given name being found), <c>null</c> is returned.  The final property
    /// in the path can have a <c>null</c> value, as that value is never used.
    /// </para>
    /// </remarks>
    /// <param name="binding">The binding expression.</param>
    /// <param name="source">
    /// The source object at which to start the evaluation.
    /// </param>
    /// <returns>
    /// The display name of the property at the end of the binding, or
    /// <c>null</c> if this could not be determined.
    /// </returns>
    private string GetBindingPropertyDisplayName(BindingExpression binding,
                                                 object source)
    {
        if (binding == null)
        {
            throw new ArgumentNullException("binding");
        }

        string bindingPath = binding.ParentBinding.Path.Path;
        object obj = source;
        PropertyInfo propInfo = null;
        foreach (string propertyName in bindingPath.Split('.'))
        {
            if (obj == null)
            {
                // Null object not at the end of the path.
                return null;
            }

            Type type = obj.GetType();
            propInfo = type.GetProperty(propertyName);
            if (propInfo == null)
            {
                // No property with the given name.
                return null;
            }

            obj = propInfo.GetValue(obj, null);
        }

        DisplayAttribute displayAttr = 
            propInfo.GetCustomAttributes(typeof(DisplayAttribute), false)
            .OfType<DisplayAttribute>()
            .FirstOrDefault();

        if (displayAttr != null)
        {
            return displayAttr.GetName();
        }
        else
        {
            return propInfo.Name;
        }
    }

    private void SetFieldLabel()
    {
        BindingExpression binding = this.GetBindingExpression(FieldProperty);
        string displayName = GetBindingPropertyDisplayName(binding,
                                                           DataContext);
        if (lblLabel != null)
        {
            lblLabel.Content = displayName;
        }
    }
}

注意すべき点がいくつかあります。

  • このコードを使用するには、プロジェクトに への参照が必要System.ComponentModel.DataAnnotationsです。Displayただし、これは属性を使用するために必要な参照と同じであるため、問題にはなりません。

  • この関数SetFieldLabelは、フィールドのラベルを設定するために呼び出されます。電話をかけるのに最も信頼できる場所は からであることがわかりましたDispatcher.BeginInvoke。コンストラクター内またはイベント ハンドラー内からこのメソッドを直接呼び出すことLoadedは機能しませんでした。これは、バインディングがそれまでにセットアップされていなかったためです。

  • プロパティ名のドット区切りリストで構成されるバインディング パスのみがサポートされます。のようなものSomeProp.SomeOtherProp.YetAnotherPropは問題SomeProp.SomeList[0]ありませんが、サポートされておらず、機能しません。バインディング プロパティの表示名を特定できない場合は、何も表示されません。

  • 依存関係プロパティに PropertyChangedCallback がなくなりましたField。ユーザーがコントロール内のテキストを変更するたびに何が起こるかについては、あまり関心がありません。バインドされたプロパティの表示名は変更されません。

テスト目的で、次のビューモデル クラスを作成しました。

public class ViewModel
{
    // INotifyPropertyChanged implementation omitted.

    [Display(Name = "This value is in a Display attribute")]
    public string WithDisplay { get; set; }

    public string WithoutDisplay { get; set; }

    [Display(Name = "ExampleFieldNameKey", ResourceType = typeof(Strings))]
    public string Localised { get; set; }

    public object This { get { return this; } }

    public object TheVerySame { get { return this; } }
}

( Resources コレクションには、 nameと valueStrings.resxを持つ単一のキーが含まれています。このコレクションには、 Access Modifier も Public に設定されています。) 次の XAML を使用して、ビューモデルのインスタンスに設定して、コントロールへの変更をテストしました。上記のクラス:ExampleFieldNameKeyThis value is in a Resources.resxDataContext

<StackPanel>
    <local:RowItem Field="{Binding Path=WithDisplay, Mode=TwoWay}" />
    <local:RowItem Field="{Binding Path=WithoutDisplay, Mode=TwoWay}" />
    <local:RowItem Field="{Binding Path=Localised, Mode=TwoWay}" />
    <local:RowItem Field="{Binding Path=This.This.TheVerySame.This.WithDisplay, Mode=TwoWay}" />
</StackPanel>

これによりRowItems、次のラベルが付いた4 つが得られました。

この値は表示属性にあります
表示なし
この値は Resources.resx にあります
この値は表示属性にあります
于 2012-07-19T22:24:54.650 に答える