How do I expose the ActualWidth property of one of the components of my user control to users?
I have found plenty of examples of how to expose a normal property by creating a new dependency property and binding, but none on how to expose a read-only property like ActualWidth.
What you need is a ReadOnly dependency property. The first thing you need to do is to tap into the change notification of the ActualWidthProperty dependency on the control that you need to expose. You can do that by using the DependencyPropertyDescriptor like this:
// Need to tap into change notification of the FrameworkElement.ActualWidthProperty
Public MyUserControl()
{
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty
(FrameworkElement.ActualWidthProperty, typeof(FrameworkElement));
descriptor.AddValueChanged(this.MyElement, new EventHandler
OnActualWidthChanged);
}
// Dependency Property Declaration
private static DependencyPropertyKey ElementActualWidthPropertyKey =
DependencyProperty.RegisterReadOnly("ElementActualWidth", typeof(double),
new PropertyMetadata());
public static DependencyProperty ElementActualWidthProperty =
ElementActualWidthPropertyKey.DependencyProperty;
public double ElementActualWidth
{
get{return (double)GetValue(ElementActualWidthProperty); }
}
private void SetActualWidth(double value)
{
SetValue(ElementActualWidthPropertyKey, value);
}
// Dependency Property Callback
// Called when this.MyElement.ActualWidth is changed
private void OnActualWidthChanged(object sender, Eventargs e)
{
this.SetActualWidth(this.MyElement.ActualWidth);
}
ActualWidth is a public readonly property (coming from FrameworkElement) and is exposed by default. What is the case that you are trying to achieve?
Related
I have a custom text box defined as follows:
public class CustomTextBox : TextBox
{
public static DependencyProperty CustomTextProperty =
DependencyProperty.Register("CustomText", typeof(string),
typeof(CustomTextBox));
static CustomTextBox()
{
TextProperty.OverrideMetadata(typeof(SMSTextBox),
new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.Journal |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(CustomTextBox_OnTextPropertyChanged));
}
public string CustomText
{
get { return (string)GetValue(CustomTextProperty); }
set { SetValue(CustomTextProperty, value); }
}
private static void CustomTextBox_OnTextPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
CustomTextBox customTextBox = d as CustomTextBox;
customTextBox.SetValue(CustomTextProperty, e.NewValue);
}
}
I'm binding the Custom Text property in the XAML -
<local:CustomTextBox CustomText="{Binding ViewModelProperty}" />
The problem I'm facing is that when I enter anything in the CustomTextBox, the changes are not reflected in the ViewModelProperty i.e. the ViewModelProperty is not getting updated. The CustomTextProperty is getting updated but I suppose I need to do something extra to make the binding work as well.
What am I not doing? I would appreciate any help regarding this.
Thank you
I guess the binding needs to be two-way.
<local:CustomTextBox
CustomText="{Binding ViewModelProperty, Mode=TwoWay}" />
You wouldn't need to specify the Mode if you made the CustomText property bind two-way by default:
public static readonly DependencyProperty CustomTextProperty =
DependencyProperty.Register(
"CustomText", typeof(string), typeof(CustomTextBox),
new FrameworkPropertyMetadata(
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
You may also have to define a PropertyChangedCallback for the CustomText property that updates the Text property (i.e. the other direction of what you have implemented now). Otherwise the TextBox won't display anything that is initially contained in the ViewModel property and of course woudln't be updated when the ViewModel property changes.
I have a UserControl that I am trying to hook up event handlers from a parent control to. I want to do as little in code as I can and am running into some problems. Here is my setup:
in NewUserControl code behind I have:
public RoutedEventHandler PrintClickHandler { get; set; }
public DependencyProperty PrintClickHandlerProperty =
DependencyProperty.Register("PrintClickHandler", typeof(RoutedEventHandler),
typeof(NewUserControl), new PropertyMetadata(null));
In MyParentControl I have:
public RoutedEventHandler PrintClickHandler
{
get { return btnPrintCall_Click; }
}
private void btnPrintCall_Click(object sender, RoutedEventArgs e)
{
// some code
}
And finally in the MyParentControls xaml I have the binding for NewUserControl.PrintClickHandler:
<NewUserControl PrintClickHandler="{Binding Path=PrintClickHandler,
RelativeSource={RelativeSource AncestorType=local:MyParentControl, AncestorLevel=1}}" />
Now in the debugger I can implement all the getters and setters the classic way to hit breakpoints when they are called. I see the getter for MyParentControl.PrintClickHandler in the being hit, but the setter for NewUserControl.PrintClickHandler is never hit. I also have no errors or warnings related to this binding in the output.
I've never tried doing events like this, but it does look like your dependency property may by setup incorrectly, try:
public RoutedEventHandler PrintClickHandler
{
get { return (RoutedEventHandler)GetValue(PrintClickHandlerProperty); }
set { SetValue(PrintClickHandlerProperty, value); }
}
public static DependencyProperty PrintClickHandlerProperty = DependencyProperty.Register(
"PrintClickHandler",
typeof(RoutedEventHandler),
typeof(NewUserControl),
new PropertyMetadata(null));
This question is erroneous. As I am dealing with an event, using a dependency property is inappropriate. I can simple add a public Event property in my code behind which can be set in the xaml to an appropriate event handler method.
I've created a custom control with, amongst others, the following:
public partial class MyButton : UserControl
{
public bool Enabled
{
get { return (bool)GetValue(EnabledProperty); }
set {
SetValue(EnabledProperty, value);
SomeOtherStuff();
}
}
}
public static readonly DependencyProperty EnabledProperty =
DependencyProperty.Register("Enabled", typeof(bool), typeof(MyButton), new PropertyMetadata(true));
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(EnabledProperty, value);
}
public static bool GetEnabled(DependencyObject obj)
{
return (bool) obj.GetValue(EnabledProperty);
}
}
In my XAML, I (try to) use binding to set the Enabled property:
<MyButton x:Name="myButtom1" Enabled="{Binding CanEnableButton}"/>
I know the bind between my control and the underlying data model is valid and working as I can bind 'IsEnabled' (a native property of the underlying UserControl) and it works as expected. However, my Enabled property is never set via the above binding. I've put breakpoints on my property set/get and they never get hit at all.
I can only imaging I've missed something relating to binding in my custom control. Can anyone see what?
I've tried implementing INotifyPropertyChanged on my control (and calling the PropertyChanged event from my Enabled setter) ... but that didn't fix it.
[ BTW: In case you are wondering "Why?": I can't intercept changes to the IsEnabled state of the base control, so I decided to implement and use my own version of a Enable/disable property (which I called Enabled) - one where I could plug my own code into the property setter ]
First of all drop the SetEnabled and GetEnabled pair, these only make sense for an attached property which is not what you are doing.
Now your main problem is that you are under the false assumption that the get/set members of your propery get called during binding, they don't.
What you need is to pass a call back method in the property meta data, it's here that you intercept changes and take other actions like so:-
public bool IsEnabled
{
get { return (bool)GetValue(IsEnabledProperty); }
set { SetValue(IsEnabledProperty, value); }
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.Register(
"IsEnabled",
typeof(bool),
typeof(MyButton),
new PropertyMetadata(true, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyButton source = d as MyButton;
source.SomeOtherStuff();
}
private void SomeOtherStuff()
{
// Your other stuff here
}
With this in place regardless of how the propery is changed the SomeOtherStuff procedure will execute.
I'd suggest using the IsEnabledChanged event which is part of every Control/UserControl.
That would allow you to hook up to the event and do whatever actions you want to take.
public MainPage()
{
InitializeComponent();
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(MainPage_IsEnabledChanged);
}
void MainPage_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Do SomeStuff
}
Before I start, I have this code inside of a Custom Usercontrol:
private DependencyProperty _rotation = DependencyProperty.Register("Rotation", typeof(double), typeof(MyControl),
new PropertyMetadata(new PropertyChangedCallback(RotationPropertyChanged)));
public double Rotation
{
get { return (double)GetValue(_rotation); }
set { SetValue(_rotation, value); }
}
public static void RotationPropertyChanged(DependencyObject obj, System.Windows.DependencyPropertyChangedEventArgs e)
{
//How can I start Animation, as I'm in a Static method?
}
The Properties are getting set correctly, and my RotationPropertyChanged Function is being called correctly as well. As you can see, my comment inside that method is my question. Since this handler NEEDS to be static (VS Told me so), How do I access Non-Static things, such as a storyboard so I can start animation?
To elaborate on the databinding:
My Viewmodel is updating a property(located in that same viewmodel) which is databound to this dependency property via Xaml. I wish I didn't have to use this callback, but the property wont be changed without it.
Thanks
You can just cast the DependencyObject passed into the static event handler to your control type and then call an instance method on it. I think this is a pretty common pattern with dependency properties in Silverlight/WPF:
private DependencyProperty _rotation = DependencyProperty.Register(
"Rotation",
typeof(double),
typeof(MyControl),
new PropertyMetadata(new PropertyChangedCallback(RotationPropertyChanged)));
public double Rotation
{
get { return (double)GetValue(_rotation); }
set { SetValue(_rotation, value); }
}
public static void RotationPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((MyControl)obj).RotationPropertyChanged(e);
}
private void RotationPropertyChanged(DependencyPropertyChangedEventArgs e)
{
// Start your animation, this is now an instance method
}
Two way binding does not work on my custom control with the following internals:
public partial class ColorInputControl
{
public ColorInputControl()
{
InitializeComponent();
colorPicker.AddHandler(ColorPicker.SelectedColorChangedEvent,
new RoutedPropertyChangedEventHandler<Color>( SelectedColorChanged));;
colorPicker.AddHandler(ColorPicker.CancelEvent,
new RoutedPropertyChangedEventHandler<Color>(OnCancel));
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register
("SelectedColor", typeof(Color), typeof(ColorInputControl),
new PropertyMetadata(Colors.Transparent, null));
public Color SelectedColor
{
get
{
return (Color)GetValue(SelectedColorProperty);
//return colorPicker.SelectedColor;
}
set
{
SetValue(SelectedColorProperty, value);
colorPicker.SelectedColor = value;
}
}
private void SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<Color> e)
{
SetValue(SelectedColorProperty, colorPicker.SelectedColor);
}
}
SelectedColor is being bound to a property that fires INotifyPropertyChanged event control when it changes. However, I cannot get two-way binding to work. Changes from the UI are pesisted to the data source. However, changes originating from the data source are not reflected on the UI.
What did I miss? TIA.
Never do any work (updating the color picker) in the SelectColor helpers. Those are convinence wrappers and are not guarranted to be called. (As you can see in your two way binding.) Add a PropertyChangedCallback to your SelectedColorProperty metadata. Do your work in there.