I have a custom control that derives from TextBox. And I cannot find a way to override the default Text value. So, in short - I want MyTextBox to have some specific default text in it.
The code:
public class MyTextBox : TextBox
{
static MyTextBox()
{
TextBox.TextProperty.OverrideMetadata(
typeof(MyTextBox),
new FrameworkPropertyMetadata("DEFAULT TEXT", OnTextChanged));
}
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
The problem is that the property (strangely) contains the "DEFAULT TEXT" value, yet it does not show up in the UI (text box is empty).
What am I doing wrong?
The TextBox uses an inner class to provide most of it's functionality. This same class is shared with RichTextBox. I believe the TextBox assumes Text will be empty when constructed, so the UI is not updated.
One thing to keep in mind is that you have effectively short-circuited the TextBox.OnTextPropertyChanged method from being called. If you want to override the PropertyChangedCallback, then you'd probably need to manually call the TextBox version to ensure everything works as expected.
You can get to the base class's PropertyChangedCallback using TextProperty.GetMetadata(typeof(TextBox)).PropertyChangedCallback.
You may be able to call the TextBox's PropertyChangedCallback in an instance constructor to force the UI to update. Otherwise, you'd need to set Text directly.
Related
I need to add some logic to user control with DependencyProperty.
My logic is supposed to change properties on controls inside my UserControl.
I want to avoid building huge "dependency tree" because I have a lot of user controls. I just want to use binding in my windows (not in nested user controls).
This is my control:
public partial class BucketElevatorControl : UserControl
{
public BucketElevatorControl()
{
InitializeComponent();
}
public bool On
{
get
{
return (bool)GetValue(OnProperty);
}
set
{
SetValue(OnProperty, value);
}
}
// Using a DependencyProperty as the backing store for IsOn. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnProperty = DependencyProperty.Register(
"On",
typeof(bool),
typeof(BucketElevatorControl),
new PropertyMetadata(
false, PropertyChangedCallback
));
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
// I want to do something with my UserControl child controls
}
}
The question is: how can I do some logic in contol code behind and take advantage of data binding?
My logic is complicated (drawing graphics, animations etc.).
You should create CoerceValueCallbacks for the properties you want to change. Those callbacks set the new values. When this property changes, you then coerce the others, like so:
private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
dependencyObject.CoerceValue(MinReadingProperty);
dependencyObject.CoerceValue(MaxReadingProperty);
}
I have no idea what you mean by "dependency tree", but if you want to alter the state of stuff in your template according to changes in your control's dependency properties, you can do that with TemplateBinding and/or triggers in your control template. Write value converters if you need to. Most of what you need to do can probably be done that way.
If you need more complicated logic, you can also override OnApplyTemplate() in your control, and call GetTemplateChild() to get named controls within the control's template. For example, you might require the template to have a TextBox somewhere in it called PART_FooText; throw an exception if you get null from GetTemplateChild("PART_FooText") as TextBox. If the TextBox is there, do anything you like to it: Handle events, set properties, etc. If you like, keep a private field TextBox _PART_FooText; to fiddle with it later on, in your property-changed callbacks, other events, or whatever.
I just discovered than I can do the following:
var button = new Button();
button.SetValue(TextBlock.TextProperty, "text");
var text = (string)button.GetValue(TextBlock.TextProperty); // text is "text"
While the above example is a bit unrealistic, it does show that I can attach a regular dependency property onto another object. It doesn't have to be a an attached property (TextBlock.TextProperty is not registerd with DependencyProperty.RegisterAttached().
This bares the questions why are there attached properties in the first place? The only difference I can see for now ist that I can't attach regular dependency properties in XAML. But that's about it. Are there any other differences?
Update:
To make it more clear, the below code works and looks pretty close to an attached property from the end users perspective:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(Button),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
...
button.SetValue(AttachedPropertyDeclarer.TextProperty, "text");
var text = (string)button.GetValue(AttachedPropertyDeclarer.TextProperty);
Compare this to the attached property way:
public static class AttachedPropertyDeclarer
{
public static readonly DependencyProperty TextProperty = DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(AttachedPropertyDeclarer),
new PropertyMetadata(default(string),OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// do something when text changed
}
}
The only effective differnce to an attached property here is that I have to declare the owner of type Button whereas in a attached property it would usually be AttachedPropertyDeclarer. But this only needs to be done if I need a changed event handler (i.e. OnTextChanged).
Regarding your example, you have not as you say, attached a regular dependency property onto another object. All your code has achieved is to store a string value in a Dictionary along with a reference to your object. That does not make it an Attached Property - importantly, you cannot access that string value from the Button directly, as there is no Text property on a Button.
What your code does is actually very similar to this:
Dictionary<object, object> values2 = new Dictionary<object, object>();
var button = new Button();
values2.Add(button, "text");
string text = values2[button].ToString();
Now to answer your question:
The main reason to declare an Attached Property is in order to add a property to a type that you didn't declare, thereby extending its functionality.
A great example of this would be to add a SelectedItems property to the ItemsControl or ListBox class. In doing so, we extend the current, or default functionality of the class. Another good example would be declaring an Attached Property that automatically brings added items into view (again in an ItemsControl or ListBox class).
UPDATE >>>
According to your comments, you seem to be refusing to accept the differences that I have outlined... you said:
There is literally no difference from the end users perspective except that I can't use it in XAML.
Firstly, do you not think that this is a huge difference?.. you won't be able to use it for data binding for a start. Furthermore, you keep saying that you can attach a property to a type that you haven't declared using a DependencyProperty, but you are 100% incorrect. You can reference an Attached Property directly in both code and XAML, while you can't reference what you are calling your attached property directly in either XAML or code.
All you are doing is storing a value in a Dictionary and you certainly don't need the overhead of a DependencyProperty to do that. There really is no comparison between doing that and declaring an Attached Property. From the Attached Properties Overview page on MSDN:
You might create an attached property when there is a reason to have a property setting mechanism available for classes other than the defining class.
Note the following part: a property setting mechanism
Adding values into a Dictionary is not a property setting mechanism. So again, you lose the ability to use your pretend Attached Property in Styles, Animations, Triggers, etc.
To clarify this situation for once and for all, you can develop a simple test project. Implement the IList SelectedItems Attached Property for a ListBox that I mentioned (you can find online tutorials for this) and then do the same using your pretend Attached Property (if it is even possible). The difference in the simplicity of development bewteen the two will clearly show you why you should use an Attached Property instead of a regular DependencyProperty.
If you look closely at dependency property identifier, all DP's are registered with class DependencyProperty and we pass the Owner class type and property name at time of registration.
Sample:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
"IsSpinning", typeof(Boolean), typeof(OwnerClass));
At time of registration it creates some unique hash code combining property name and owner class type to represent each DP uniquely.
So, when you set value for that DP on some object like in your case on Button, code flow is like this:
First it will get the unique value generated at time of registration of property and add the key value pair in private dictionary named _effectiveValues declared in class Dependency Object with Key set to unique hashcode at time of registration and value being the value set by user.
Note - No written documentation for this on MSDN but verified this by peeking into source code using reflector.
So, when you set the value from code behind it will work like I mentioned above because it does not validate before adding value in the dictionary if it belongs to that type or not and fetching value will get you the value from dictionary.
Not sure but might be constraint is there in XAML only where WPF guys enforced the type check. Sadly there is no written documentation for this on MSDN.
Attached properties are discovered, when you want to have control over an existing control, but dont want to extend it. A pretty good example is, there is no way to bind BlackOutDates property in XAML for WPF DatePicker. In that case you can use an Attached Property to attach a custom functionality to map the BlackOutDates. This suits good in MVVM, since attached properties provided way for binding in XAML.
public class BlackOutDatesAdapter
{
public static List<DateTime> GetBlackOutDates(DependencyObject obj)
{
return (List<DateTime>)obj.GetValue(BlackOutDatesProperty);
}
public static void SetBlackOutDates(DependencyObject obj, List<DateTime> value)
{
obj.SetValue(BlackOutDatesProperty, value);
}
// Using a DependencyProperty as the backing store for BlackOutDates. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BlackOutDatesProperty =
DependencyProperty.RegisterAttached("BlackOutDates", typeof(List<DateTime>), typeof(BlackOutDatesAdapter), new PropertyMetadata(null, OnBlackOutDatesChanged));
private static void OnBlackOutDatesChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as DatePicker;
var list = (List<DateTime>)e.NewValue;
foreach(var date in list)
{
control.BlackoutDates.Add(new CalendarDateRange(date));
}
}
}
Binding in XAML will look like this,
<DatePicker VerticalAlignment="Center"
Width="200"
local:BlackOutDatesAdapter.BlackOutDates="{Binding BlackOutDates}"
DisplayDate="{Binding DisplayDate}" />
In the callback of property, you can do your own mapping of adding the dates to DatePicker. For more information, please read this post.
I would like to know when is actually what happening inside initalization process of controls when I start a WPF application?
When are DP initalized? When Binding? When does DataContext get set? Is DataContext avaialbe in constructor of a control? Is there any kind of order?
I realized I ran into a trap that once I set a value on getter/setter of a DP inside constructor of a control the DP value gets updated but immediately also the values gets rolled back to default value which was null.
So my guess is that contructors get initalized first and then dependency properties.
Can somebody help me out with this?
Edit: Just for Rachel. The dp receives the value 234 and immedialty rolls back to null. I think its because constructor gets called first and then subsequently the initalizing of dps happens which sets dp back to null because null is default value. Am i thinking wrong about this? What is the order of initalization steps of a control or dependency object.
class MySuperDuperCoolClass : ContentControl
{
public MySuperDuperCoolClass()
{
InitalizeComponents();
this.MySuperDuperProperty = "234";
}
public string MySuperDuperProperty
{
get { return (string)GetValue(MySuperDuperPropertyProperty);}
set { SetValue(MySuperDuperPropertyProperty, value);}
}
public static DependencyProperty MySuperDuperPropertyProperty =
DependencyProperty.Register("MySuperDuperProperty", typeof(string), typeof(MySuperDuperCoolClass),
new PropertyMetadata(null));
}
I find the DispatcherPriority Enum useful for recalling the exact event order:
Send
Normal - Constructors run here
DataBind
Render
Loaded
Background
ContextIdle
ApplicationIdle
SystemIdle
Inactive
Invalid
Input
As you can see, Constructors get run first, followed by data bindings.
DependencyProperties get initialized when the object gets created, just like any other property, so that would occur prior to the constructor being run so the property exists in the constructor.
Setting the DataContext property or other DependencyProperties works just like any other property you are setting. If you set them with a binding, they'll get evaluated after the constructor. If you set them in the XAML, they'll get set in the Constructor. If you set them in the Loaded event, they'll get set after everything has been constructed, bound, and rendered.
You also might find this SO answer useful:
Sequence of events when a Window is created and shown
As requested, here is the sequence of major events in WPF when a
window is created and shown:
Constructors and getters/setters are called as objects are created, including PropertyChangedCallback, ValidationCallback, etc on the
objects being updated and any objects that inherit from them
As each element gets added to a visual or logical tree its Intialized event is fired, which causes Styles and Triggers to be
found applied in addition to any element-specific initialization you
may define [note: Initialized event not fired for leaves in a logical
tree if there is no PresentationSource (eg Window) at its root]
The window and all non-collapsed Visuals on it are Measured, which causes an ApplyTemplate at each Control, which causes additional
object tree construction including more constructors and
getters/setters
The window and all non-collapsed Visuals on it are Arranged
The window and its descendants (both logical and visual) receive a Loaded event
Any data bindings that failed when they were first set are retried
The window and its descendants are given an opportunity to render their content visually
Steps 1-2 are done when the Window is created, whether or not it is
shown. The other steps generally don't happen until a Window is
shown, but they can happen earlier if triggered manually.
Edit based on code added to question
Your DependencyProperty.Register method looks funny to me. The signature of the method doesn't match any of the overloads for that method, and you're using what appears to be a custom UIProperty class to set the default value instead of the normal PropertyMetadata.
I can confirm that if your code runs as expected with a normal DependencyProperty.Register signature, so the likely cause of your problem is either somewhere within your custom code, or its with how you are using/setting the property.
The code I used for a quick sample test is this:
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
this.TestDependencyProperty = "234";
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata(null));
}
and the XAML is
<ContentControl x:Class="WpfApplication1.UserControl1"
x:Name="TestPanel" ...>
<Label Content="{Binding ElementName=TestPanel, Path=TestDependencyProperty}"/>
</ContentControl>
In WPF you are setting default values for DP with PropertyMetaData not via constructor.
public partial class UserControl1 : ContentControl
{
public UserControl1()
{
InitializeComponent();
}
public string TestDependencyProperty
{
get { return (string)GetValue(TestDependencyPropertyProperty); }
set { SetValue(TestDependencyPropertyProperty, value); }
}
public static DependencyProperty TestDependencyPropertyProperty =
DependencyProperty.Register("TestDependencyProperty", typeof(string), typeof(UserControl1),
new PropertyMetadata("234"));
}
I have a problem concerning properties that are set in xaml.
I've made a user control with a dependency property 'MidiChanel'.
I set the value of this property to 10 in xaml.
In the constructor of the user control, I need this value to add it to a dictionary and to pass the value to a child class of my user control.
The problem is, that in the constructor, even after calling initializecomponents, the property stil has its default value, and not the value, set in xaml.
In fact, it does't gets set at all.
If I change the 'MidiChanel' proprty to a normal property, the value gets set, but it's not initializecomponents of the userControl that sets the value, but initializecomponents of the main window.
Call stack = Main.InitializeComponents, Constructor of userControl (values are not yet available), Setter of 'MidiChanel' gets set. (by who?, call stack says Main.InitializeComponents).
I'm a winforms developer and find all this pretty strange.
After Main.InitializeComponents, I could loop over all userControls in the main page, and do everything here, but that seems a strange thing to do.
Any suggestions here?
you can set a callback method that will be raised when your dependenyProperty changed
public int SomeProp
{
get { return (int)GetValue(SomePropProperty); }
set { SetValue(SomePropProperty, value); }
}
// Using a DependencyProperty as the backing store for SomeProp. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SomePropProperty =
DependencyProperty.Register("SomeProp", typeof(int), typeof(yourOwnerclass), new PropertyMetadata(new PropertyChangedCallback(OnSomePropertyChnaged)));
public static void OnSomePropertyChnaged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as yourOwnerclass).SomeFunction();
}
My application's building a UI dynamically in which each ItemsControl in the view is a focus scope. The items displayed by each ItemsControl are controls in its focus scope. The user can tab through all of the controls in the view from start to end (i.e. the keyboard navigation mode is Continue). These controls are all bound to data source properties.
What seems to be happening is that when I'm on the last control in the first focus scope and press TAB, the keyboard focus moves to the second focus scope, but the previous control doesn't lose logical focus. So the bound property doesn't get updated.
I can make this problem go away (in theory, at least) by not making each ItemsControl a focus scope. But I didn't decide to implement logical focus capriciously: there are things the application needs to do when each ItemsControl loses logical focus, and if I get rid of the focus scopes it's going to be hard to make that happen.
This seems like a problem that should have a straightforward solution, but nothing in the documentation seems to suggest a way around it. Any ideas?
The problem is that you're trying to make logical focus in-line with keyboard focus which as the documentation shows is really not how it is supposed to be used. Logical focus provides a way to maintain what the previous control that had focus in a given focus scope so you can refocus again on it when you regain keyboard focus.
Looking at your question I think what you really want to do is pick up the event when your item contol, or one of the visual child elements, loses keyboard focus. This can
be achieved using IsKeyboardFocusedWithin property and you can trigger actions based on the associated event.
If you need this to be a routed event, then you'll need a custom control like follows which exposes a routing event for gaining and losing focus.
public partial class FocusManagingControl : UserControl
{
public static readonly RoutedEvent KeyboardLostFocusWithinEvent = EventManager.RegisterRoutedEvent("KeyboardLostFocusWithin",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FocusManagingControl));
public static readonly RoutedEvent KeyboardGotFocusWithinEvent = EventManager.RegisterRoutedEvent("KeyboardGotFocusWithin",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FocusManagingControl));
public event RoutedEventHandler KeyboardLostFocusWithin
{
add { AddHandler(KeyboardLostFocusWithinEvent, value); }
remove { RemoveHandler(KeyboardLostFocusWithinEvent, value); }
}
public event RoutedEventHandler KeyboardGotFocusWithin
{
add { AddHandler(KeyboardGotFocusWithinEvent, value); }
remove { RemoveHandler(KeyboardGotFocusWithinEvent, value); }
}
public FocusManagingControl()
{
this.InitializeComponent();
this.IsKeyboardFocusWithinChanged += FocusManagingControl_IsKeyboardFocusWithinChanged;
}
private void FocusManagingControl_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if((bool)e.OldValue && !(bool)e.NewValue)
RaiseEvent(new RoutedEventArgs(KeyboardLostFocusWithinEvent, this));
if(!(bool)e.OldValue && (bool)e.NewValue)
RaiseEvent(new RoutedEventArgs(KeyboardGotFocusWithinEvent, this));
}
}
Which you can use in your XAML with the entry
<local:FocusManagingControl>
<local:FocusManagingControl.Triggers>
<EventTrigger RoutedEvent="local:FocusManagingControl.KeyboardLostFocusWithin">
....