I need to implement a Ruler and I have a CustomControl RangeSlider which is derived from Control and has several DependencyProperties (e.g. Minimum, Maximum, LowerValue, UpperValue).
public static readonly DependencyProperty LowerValueProperty =
DependencyProperty.Register("LowerValue",
typeof(double),
typeof(RangeSlider),
new FrameworkPropertyMetadata(0d, OnLowerValueChanged, CoerceLowerValue));
public double LowerValue
{
get { return (double)base.GetValue(LowerValueProperty); }
set { base.SetValue(LowerValueProperty, value); }
}
In Xaml, the Ruler has a sub control Scale (derived from Canvas), which needs the same Properties to draw the Scaling. If LowerValue or UpperValue changed, the Scale should repaint. So I add
public static readonly DependencyProperty LowerValueProperty =
RangeSlider.LowerValueProperty.AddOwner(typeof(Scale), new FrameworkPropertyMetadata(1d));
When I move the thumb, something should raised in Scale. First I implemented OnRender(DrawingContext) and added to the LowerValueProperty in RangeSlider FrameworkPropertyMetadataOptions. But it doesn't matter, nothing happens. Moreover I do not want to use OnRender(DrawingContext), because it's not available in Silverlight.
Furthermore I wonder why the OnLowerValueChanged-Method in RangeSlider is raised but not the setter of LowerValue (at least the debugger does not break in there).
So, is there a way for Silverlight & WPF to raise a method automatically in Scale, when LowerValue in RangeSlider changed (except raising from OnLowerValueChanged)?
Note: The above code is the WPF-Code. In Silverlight there are of course some difference, because Silverlight does not support among other things Coercion.
Because the code should run in Silverlight and WPF, I do not want to use OnRender(DrawingContext).
If I understand correctly you want to be able to perform some logic in propertiers, that are just wrappers around GetValue & SetValue methods. Problem is they are only used when you create XAML and specify bindings for those properties. Behind the scenes they are never called anymore, instead GetValue & SetValue are used.
Thats why I think (still not sure I understand question clearly) you have to either use event handlers for ValueChanged event (for example) inside your control and there is no other way to intercept calls to GetValue & SetValue.
Or you can use PropertyChangedCallback with DependencyProperties that is called after DPs are changed and place your code there
Related
In my WPF application, I made my own control and want to bind to a property in it. This is what I tried so far:
public partial class BreadcrumbContainer : Grid
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register(nameof(Test), typeof(string), typeof(BreadcrumbContainer), new PropertyMetadata(string.Empty));
public string Test
{
get { return (string)GetValue(TestProperty); }
set { SetValue(TestProperty, value); Refresh(); }
}
public BreadcrumbContainer()
{
InitializeComponent();
}
private void Refresh()
{
// never gets called
}
}
And I try to bind to my Test property like this:
<controls:BreadcrumbContainer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Test="{Binding SearchMessage}"/>
And in my view model, I have a property SearchMessage. All my other bindings are working, so it must be something I'm doing wrong in my BreadcrumbContainer
Quoting from Adam Nathan's WPF Unleashed
.NET property wrappers are bypassed at runtime when setting
dependency properties in XAML. Although the XAML compiler depends on
the property wrapper at compile time, WPF calls the underlying
GetValue and SetValue methods directly at runtime!
The property wrapper in your case is your Test property. So basically, don't put any logic in there since it never gets called in runtime. The proper way to do this is to use a property changed callback. One example for you is in the answer found here. Notice the callback initialization in the DependencyProperty.Register call.
There is also the "official" documentation in Checklist for Defining a Dependency Property
- Implementing the "Wrapper":
In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue actions, respectively. The reason for this is discussed in the topic XAML Loading and Dependency Properties.
From XAML Loading and Dependency Properties:
Because the current WPF implementation of the XAML processor behavior for property setting bypasses the wrappers entirely, you should not put any additional logic into the set definitions of the wrapper for your custom dependency property. If you put such logic in the set definition, then the logic will not be executed when the property is set in XAML rather than in code.
This is one example of a baffling behavior in WPF that will drive you mad if you don't know. It's not really your fault but I suggest reading WPF Unleashed by Adam Nathan - and all the online documentation - to learn more about these types of pitfalls.
In a custom control I have a class which derives from DependencyObject and has a dependency property called MaxIdealWidth. I have several custom controls which do some pretty weird measure/arrange, and they all use this property in some way, but not in a binding.
My problem is, when everything is drawn the first time, it works well. However when the MaxIdealWidth is changed by one custom control, none of the others will do measure/arrange. I can understand why this happens, but I need to force all custom controls to measure/arrange at the same time.
FrameworkPropertyMetadata.AffectsArrange
Looking at the documentation, this looks like a promising way forward, however I have absolutely no idea how to apply it in practice. It doesn't seem to be documented from a point of view of how to use it in my situation. Can someone tell me how to apply AffectsArrange from XAML or code-behind to indicate from a custom control that a dependency property on the DataContext should cause measure/arrange?
You do that by using the FrameworkPropertyMetadata class instead of the PropertyMetadata for the last Parameter of the dependency propery Registration. There you set all the bits you like:
public static readonly DependencyProperty ArrowEndsProperty =
DependencyProperty.Register("your name",
typeof(<class>), typeof(<owner class>),
new FrameworkPropertyMetadata(<initial value>,
FrameworkPropertyMetadataOptions.AffectsMeasure));
Let me start by saying I am very new to WPF so be gentle.
It seems like this should be easy but I am just missing something. I have implemented INotifyPropertyChanged on a few classes/properties and started binding them to elements in XAML but now I have a little more complex binding to make then updating text or changing a color. What I need is when the Alarm property of my object is set to true I need to change colors, start an animation, create and display other elements in the control. I thought I could just call a function in my control when the property is changed but since WPF hides how an element is "bound" to the model's property I am not sure how to wire that up. Is there a better way to perform this type of more complex response to a property change? If not are there any samples out there? I have not been able to find anything close to what I am looking for but I may not be searching with the correct terms.
What I need is when the Alarm property of my object is set to true I
need to change colors, start an animation, create and display other
elements in the control.
Change colors: Bind the Color/Foreground of the element you want to change the color of, to the boolean that sets the alarm, and add an IValueConverter in the binding that returns a Brush based on the boolean.
Start an animation: Use a (data)trigger on whichever element needs to be animated, in that trigger, use a Storyboard to define the animation you want.
create and display other elements in the control: that really depends on how well you did your MVVM, if the elements are a visualisation of an ObservableCollection through a ListBox/ListView/ItemsControl (which it should), wire up a Command to whatever sets the alarm on/off (the Button class has a Command property built in, other UIElements may require the use of System.Windows.Interactivity) and in the method that this Command will point to, add a new item to the ObservableCollection, the ItemsControl will automatically reflect the change by adding a new control.
Of course this is just raw information, and you're probably not familiar will all these things, that's when Google comes into play ;)
HTH,
Bab.
For complex behaviour in response to a property changed event, you should use the following approach: (I'm typing this freestyle so pardon any minor syntax errors)
class MyClass : INotifyPropertyChanged
{
//Presumably you've already done this part
private object _myProperty = null;
public object MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public MyClass()
{
this.PropertyChanged += My_PropertyChanged;
}
private void My_PropertyChanged( object sender, PropertyChangedEventArgs e)
{
if( e.PropertyName == "MyProperty" )
{
//Do Something complicated
}
}
Your timer / alarm just needs to update the bound property when it elapses. The property should then raise the PropertyChanged event to notify the GUI to update.
You should take a look at DataTriggers that get fired when a property changes in the view model. The EnterActions and ExitActions will allow you to play a Storyboard when the value of the property changes. Here's an example of how it is used MSDN. You can use a content control and style the Template or ContentTemplate to add all of your elements and have it control the Visibility or Opacity of the other elements.
I don't think you are looking at this right. Your class has logic, does calculation, enforces constraints, and enforces relationships among properties. If you have an alarm hopefully there is some business logic to deal with this and should be done in the class (not the UI). A UI is not built to handle an alarm it is built to display status and actions of that alarm. You will make new control visible in an alarm situation and hide other. On the animation I think you might want to throw an event that you listen for to start the animation. The idea there is that when an alarm is thrown to can register additional handles - you want to separate the business logic from the UI (not pull the business logic into the UI).
This question already has answers here:
Is there a notification mechanism for when a dependency property has changed?
(4 answers)
Closed 3 years ago.
Our Silverlight app contains a third-party control, which contains some ScrollBars (among other things). In order to troubleshoot a problem, I want to be able to stop in the debugger whenever the third-party control modifies the Minimum or Maximum properties of any of its scrollbars. Then I'll be able to look at the stack trace and learn more about what's going on.
If I was interested in the ScrollBars' Value property, that would all be easy -- ScrollBar has a ValueChanged event, so I could just add some throwaway code that hooks that event on the ScrollBar, set a breakpoint inside my event handler, and debug away. But there are no corresponding CLR events for MinimumChanged or MaximumChanged, so it won't be that simple.
I ran across a blog post that talks about using DependencyPropertyDescriptor to get dependency property change events, but unfortunately, DependencyPropertyDescriptor doesn't exist in Silverlight.
How can I get to the point where I can set a breakpoint that fires whenever the ScrollBar's Minimum and Maximum properties change?
The following idea springs to my mind:
Create a user control with a dependency property. (The XAML within the user control won't actually be used, we just need the dependency property.)
Bind the user control's dependency property to the Minimum or Maximum property of the third party control (assuming they are also dependency properties).
In the code-behind of the user control, add a PropertyChangedCallback to the dependency property and drop a breakpoint in that.
This approach should allow you to set a breakpoint that fires whenever the Minimum or Maximum property changes. However, I can't guarantee that you'll get a stacktrace that will help you.
The code-behind of the user control could look something like this:
public partial class DPContainer : UserControl
{
public static readonly DependencyProperty DebugValueProperty =
DependencyProperty.Register("DebugValue", typeof(object), typeof(DPContainer), new PropertyMetadata(DebugValue_Changed));
public DPContainer()
{
InitializeComponent();
}
public object DebugValue
{
get { return GetValue(DebugValueProperty); }
set { SetValue(DebugValueProperty, value); }
}
private static void DebugValue_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Drop a breakpoint in this method.
}
Assuming that you have a ScrollBar with x:Name="someScrollBar", you could then add something like the following to your XAML:
<local:DPContainer DebugValue="{Binding Path=Minimum, ElementName=someScrollBar}" />
Is there a way to know the first time a Dependency Property is accessed through XAML binding so I can actually "render" the value of the property when needed?
I have an object (class derived from Control) that has several PointCollection Dependency Properties that may contain 100's or 1000's of points. Each property may arrange the points differently for use in different types shapes (Polyline, Polygon, etc - its more complicated then this, but you get the idea). Via a Template different XAML objects use TemplateBinding to access these properties. Since my object uses a Template I never know what XAML shapes may be in use for my object - so I never know what Properties they may or may not bind to. I'd like to only fill-in these PointCollections when they are actually needed.
Normally in .NET I'd but some logic in the Property's getter, but these are bypassed by XAML data binding.
I need a WPF AND Silverlight compatible solution.
I'd love a solution that avoids any additional complexities for the users of my object.
Update
One way that I've found to do this is using Value Converters. In my situation I had multiple point collections. There was a main dep. property that contained the usual shape of the data. Two alternate shapes were needed for reuse in other areas/contexts.
At first I had 3 dep. props. But, I could have just had one property (the usual shape) and used a value converted to transform the points into my other 2 desired shapes. Doing this I only make the one set of points in the control. The expense of transforming points to the secondary shapes is only incurred when used. Now my main control doesn't need to anticipate how data needs to look for every possible template thrown at the control - now its the template designers problem.
Update 2
Certainly INotifyPropertyChanged and regular properties are the recommended way to handle this.
You don't necessarily have to use dependency properties to enable data-binding. However, you then have to implement INotifyPropertyChanged if changes at the source should be propagated to the target of the binding. A "normal" .NET property is easy to lazy load perhaps like this:
PointCollection points
public PointCollection Points {
get {
return this.points ?? (this.points = CreatePoints());
}
}
PointCollection CreatePoints() {
// ...
}
I'm not sure how you can fit INotifyPropertyChanged into your control, but it sounds a bit strange that your control supplies data to other parts of the system. Perhaps you need to create a view-model containing the data that you then can let your control data-bind to.
If I paraphrase your question to
How do I get notified when dependency property is changed?
will this be correct? I draw this from your phrase "Normally in .NET I'd but some logic in the Property's getter, but these are bypassed by XAML data binding".
If I'm correct, then you can register your own property changed callback. It's always called. Doesn't matter who caused the change binding, style or trigger. The following code snippet is taken from MSDN Article "Dependency Property Callbacks and Validation":
public static readonly DependencyProperty CurrentReadingProperty =
DependencyProperty.Register(
"CurrentReading",
typeof(double),
typeof(Gauge),
new FrameworkPropertyMetadata(
Double.NaN,
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(OnCurrentReadingChanged),
new CoerceValueCallback(CoerceCurrentReading)
),
new ValidateValueCallback(IsValidReading)
);
public double CurrentReading
{
get { return (double)GetValue(CurrentReadingProperty); }
set { SetValue(CurrentReadingProperty, value); }
}
Your takeaway here is OnCurrentReadingChanged() method. Hope this helps :).