Reputation: 4587
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
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
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
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