anhldbk
anhldbk

Reputation: 4587

WPF Binding problem

I want to create a WPF user control exposing a property called InnerCaption. The code is below

XAML code

<UserControl x:Class="myControl.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <TextBlock Name="_innerCaption" > Hello</TextBlock>
    </Grid>
</UserControl>

CS code behind

namespace myControl
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        public TextBlock InnerCaption
        {
        get { return (TextBlock)this.GetValue(InnerCaptionProperty); }
        set { this.SetValue(InnerCaptionProperty, value); }
        }
        public static readonly DependencyProperty InnerCaptionProperty = DependencyProperty.Register(
        "InnerCaption", typeof(TextBlock), typeof(UserControl1),new PropertyMetadata(false));
        }
}

The question is: I want users to be able to customize the InnerCaption at design time such as: modifying its color, its font style ... But I don't know how Sigh I tried to use some kinds of Binding. But it's futile. As I know, Binding only supports for binding to a property, right? Show me a way pls! :(

Upvotes: 2

Views: 178

Answers (3)

anhldbk
anhldbk

Reputation: 4587

There're a simple solution to my problem above: how about the use of ContentPresenter control? Just a few line of code declaring your property (TextBlock, string, ...) and then bind the control to the property. I tried and it works! :D

Upvotes: 0

Christian
Christian

Reputation: 4375

Bindings look like the following:

XAML

<UserControl x:Class="myControl.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <TextBlock Name="_innerCaption" 
                   Text="{Binding MyCaption}"/>
    </Grid>
</UserControl>

Now you have to set the DataContext of the XAML file to the class you would like to bind to:

namespace myControl
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        MyClassToBindTo bindingClass;

        public UserControl1()
        {
            InitializeComponent();
            bindingClass = new MyClassToBindTo();
            DataContext = bindingClass;
        }
   }
}

And The Binding Class may look like this:

public class MyClassToBindTo : NotifyPropertyChangedBase
{
        private string myCaptionString = "hello";

        public string MyCaption
        {
        get { return myCaptionString }
        set { myCaptionString = value; OnPropertyChanged("MyCaptionString"); }
        }

        // Also implement the INotifyPropertyChanged interface here
}

Hope this helps you!

EDIT:

I usually implement the INotifyPropertyChanged stuff in a base class "NotifyPropertyChangedBase:

public abstract class PropertyChangedBase : INotifyPropertyChanged
    {
        #region Member Variables

        private static HashSet<string> alreadyCoveredProperties = new HashSet<string>();

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Calls registered handlers when a property is changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// Removes all property changed subscription from the event handler.
        /// </summary>
        protected void RemovePropertyChangedSubscription()
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged -= new PropertyChangedEventHandler(PropertyChanged);
                this.PropertyChanged = null;
            }
        }

        /// <summary>
        /// Determines whether the specified property name refers to a property defined for this object.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>
        /// Returns <c>true</c> if the provided name refers to a defined property; otherwise, <c>false</c>.
        /// </returns>
        /// <remarks>Uses reflection.</remarks>
        protected bool IsDefinedPropertyName(string propertyName)
        {
            string propertyFullName = GetType().FullName + "." + propertyName;
            if (alreadyCoveredProperties.Contains(propertyFullName))
            {
                return true;
            }
            else
            {
                PropertyDescriptorCollection col = TypeDescriptor.GetProperties(this);
                PropertyDescriptor propertyDescriptor = col[propertyName];

                // A property exists, if the propertyDescriptor is != null.
                if (propertyDescriptor != null)
                {
                    alreadyCoveredProperties.Add(propertyFullName);
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
        #endregion
    }

Upvotes: 1

Almund
Almund

Reputation: 6226

I think what you might be after is probably more something in the line of sub-classing the TextBlock which´ll allow you to easily make use of the existing properties on that control.

public class MyCaptionControl : TextBlock
{
 ....
}

This enables you to bind as you would normally:

<MyCaptionControl FontSize="{Binding MyModelSize}" Text="{Binding MyModelCaption}" />

You´re definatly in the right direction creating a dependency property but if you want to bind to these properties you´ll need one property per bindable value. Like

public static readonly DependencyProperty ForegroundColor = ....
public static readonly DependencyProperty BackgroundColor = ....

These could then be directly mapped to any inner textblock-control in your usercontrol but as I mentioned I think you would be better of subclassing TextBlock.

Upvotes: 1

Related Questions