2

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! :(

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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
    }

7 Comments

Well I'll add the INotifyPropertyChanged interface in a minute... just arrived at work and need to sort out some things.
This is a great example of how to use simple binding with a proper model implementing INotifyPropertyChanged -pattern but it doesn't help the question of how to be able to bind the style for the caption as was requested "modifying its color, its font style"
Well you could bind the color property for fore/background just like the text caption. Using the "MyClassToBind" class you can bind any property of the control. Or did I misunderstood the question?
@Christian: Can we bind the whole object instead of binding only its properties?
Hmm .. interesting question. To be honest, I don't know. propably not via DataBindings. But if you want to bind the entire control, it would maybe make more sense to just access the control without bindings from the code-behind class. There, you could just change any property of the control. For instance, if you want to change the foreground color, just do "_innerCaption.Foreground = Color.Green;" in your code-behind file.
|
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.