通过xaml中的dependancy属性公开初始化对象的内部属性

时间:2016-02-05 12:12:10

标签: c# wpf xaml

我有一个派生自Control的课程,它位于UserControl内。所以我的UserControl是绑定的连接层,以及xaml中发生的所有事情。

代码看起来像这样:

public partial class CombinedControl: UserControl{}
public class DerivedControl:   MainControl
{
    public int ExampleProp{get; set;}
}

我想要做的是访问xaml中的MainControl属性。我在CombinedControl中有它的实例,我可以通过DependancyProperty公开对象本身。

public DerivedControl Instance
{
    get
    {
        return (DerivedControl)GetValue(InstanceProperty); 
    }
    set
    { 
       SetValue(InstanceProperty, value);   
    }
 }

public static readonly DependencyProperty InstanceProperty=
        DependencyProperty.Register("Instance", typeof(DerivedControl), typeof(CombinedControl));

我的目标<NameSpaceName:CombinedControl Instance.ExampleProp = "10"/>

问题:如何在xaml中访问和更改初始化的对象属性?

1 个答案:

答案 0 :(得分:1)

由于您无法使用普通的元素/属性级语法,因此可以使用“混合”行为来定位Instance上的CombinedControl属性,并将其ExampleProp属性设置为无论你想要什么价值。这需要添加对System.Windows.Interactivity的引用,它是Blend SDK(Visual Studio附带)的一部分。首先是主要行为:

using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Interactivity;

// Sets properties on targeted items via XAML.
public class SetPropertyBehavior : Behavior<FrameworkElement>
{
    // Name of the property we want to set on our target.
    public static DependencyProperty PropertyNameProperty =
        DependencyProperty.Register( "PropertyName", typeof( string ), typeof( SetPropertyBehavior ),
        new PropertyMetadata( OnTargetPropertyOrValueChanged ) );

    public string PropertyName
    {
        get { return (string)GetValue( PropertyNameProperty ); }
        set { SetValue( PropertyNameProperty, value ); }
    }

    // Value of the property we want to set.
    public static DependencyProperty PropertyValueProperty =
        DependencyProperty.Register( "PropertyValue", typeof( object ), typeof( SetPropertyBehavior ),
        new PropertyMetadata( OnTargetPropertyOrValueChanged ) );

    public object PropertyValue
    {
        get { return GetValue( PropertyValueProperty ); }
        set { SetValue( PropertyValueProperty, value ); }
    }

    // Target object that has the property we want to set. If this is null, the behavior's
    // associated object will be the target instead.
    public static DependencyProperty TargetProperty =
        DependencyProperty.Register( "Target", typeof( object ), typeof( SetPropertyBehavior ),
        new PropertyMetadata( OnTargetPropertyOrValueChanged ) );

    public object Target
    {
        get { return GetValue( TargetProperty ); }
        set { SetValue( TargetProperty, value ); }
    }

    protected override void OnAttached()
    {
        UpdateTargetProperty();
    }

    private static void OnTargetPropertyOrValueChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var behavior = d as SetPropertyBehavior;
        if( behavior != null )
            behavior.UpdateTargetProperty();
    }

    private void UpdateTargetProperty()
    {
        // Ensure we have a property name and target to work with.
        if( string.IsNullOrEmpty( this.PropertyName ) )
            return;

        var target = this.Target ?? this.AssociatedObject;
        if( target == null )
            return;

        // Make sure our property is actually on our target.
        var targetType = target.GetType();
        PropertyInfo propInfo = targetType.GetProperty( this.PropertyName,
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );

        if( propInfo == null )
            return;

        // Try to convert the string from the XAML to a value the target property can store.
        TypeConverter converter = TypeDescriptor.GetConverter( propInfo.PropertyType );
        object propValue = null;
        try
        {
            if( converter.CanConvertFrom( this.PropertyValue.GetType() ) )
                propValue = converter.ConvertFrom( this.PropertyValue );
            else
                propValue = converter.ConvertFrom( this.PropertyValue.ToString() );
        }
        catch( Exception )
        {
            // Do whatever is appropriate in your case.
            propValue = null;
        }

        propInfo.SetValue( target, propValue );
    }
}

然后,根据您为ExampleProp设置值最有意义的位置,您可以通过XAML添加行为。例如,如果您在CombinedControl的XAML中添加了行为,则可能如下所示:

<UserControl x:Class="NameSpaceName.CombinedControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:NameSpaceName"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             x:Name="Root">

    <i:Interaction.Behaviors>
        <local:SetPropertyBehavior Target="{Binding Instance, ElementName=Root}" PropertyName="ExampleProp" PropertyValue="10"/>
    </i:Interaction.Behaviors>

    <!-- Rest of control here -->
</UserControl>

如果您想从托管CombinedControl的任何父级的XAML中执行此操作,您可能会这样(使用基本WPF Window作为示例):

<Window x:Class="NameSpaceName.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:NameSpaceName"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow" Height="350" Width="525">

    <i:Interaction.Behaviors>
        <local:SetPropertyBehavior Target="{Binding Instance, ElementName=myCombinedControl}" PropertyName="ExampleProp" PropertyValue="10"/>
    </i:Interaction.Behaviors>

    <Grid>
        <local:CombinedControl x:Name="myCombinedControl"/>
    </Grid>
</Window>