简体   繁体   中英

Expose inner properties of initialized object via dependancy property in xaml

I have class which derived from Control and it is inside of the UserControl . So my UserControl is connection layer for bindings and everything going on in xaml .

Code looks as such:

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

What I want to do is to access MainControl properties in xaml. I have its instance in CombinedControl and I can expose the object itself via 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));

My goal : <NameSpaceName:CombinedControl Instance.ExampleProp = "10"/>

Question : How to access and alter initialized object properties in xaml?

Since you can't use normal element-/property-level syntax, you could use a Blend behavior to target the Instance property on your CombinedControl and set its ExampleProp property to whatever value you want. This requires adding a reference to System.Windows.Interactivity, which is part of the Blend SDK (which comes with Visual Studio). First is the main behavior:

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

Then depending on where it makes most sense for you to set the value for ExampleProp , you would add the behavior via XAML. For example, if you added the behavior inside the XAML for your CombinedControl , it might look something like this:

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

If you wanted to do it from the XAML of whatever parent is hosting your CombinedControl , you could something like this (using a basic WPF Window as an example):

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM