Rebinding all properties of all elements in the visual tree? - silverlight

I know that I can rebind all instances of a specific property for a specific type of element, as in this method that rebinds the Text property of all Textblocks.
public void Rebind()
{
foreach (var textBlock in LayoutRoot.GetDescendents().OfType<TextBlock>())
{
BindingExpression bindExp = textBlock.GetBindingExpression(TextBlock.TextProperty);
if (bindExp != null)
{
Binding bind = bindExp.ParentBinding;
textBlock.SetBinding(TextBlock.TextProperty, bind);
}
}
}
What I want to be able to do though is rebind all properties that have bindings for all elements in the visual tree. More specifically I would like to rebind all bindings that use a specific value converter. How might I do so?

This isn't realistically acheivable since FrameworkElement provides no way to enumerate the set of binding expressions that currently apply to it.
In order to achieve this you would need to have first collected all the dependency properties that may apply (at least at a per element type but that adds further complications) and then attempt GetBindingExpression on each per element. Real ugly and real slow.
Time to design this requirement out.

Related

Can custom UIElement implement IList<UIElement> and be assigned children directly (in XAML)?

Scenario: I have a range of custom UIElements (in fact, I have replaced all the standard WPF FrameworkElements I would use with lighter, more efficient counterparts) for a custom layout system intended to only use those. They all inherit from a class called Surface (which in turn is a direct descendant of UIElement).
I am now wondering if my version of Panel (let's call it SurfacePanel) can simply implement IList<Surface> and allow child Surface elements to be added directly to it, rather than to a Children property (as with regular WPF panels), in XAML.
To illustrate - in codebehind, I can do now this:
SurfacePanel.Add(child);
And from that, I would like to be able to do this in XAML:
<SurfacePanel>
<child />
</SurfacePanel>
But XAML seems to require me to have a codebehind pattern like this:
SurfacePanel.Children.Add(child)
(I don't really need these controls to support XAML to work in the runtime environment, but when testing and prototyping, I like to make my UI controls "XAML friendly" so I can benefit from the visual designer in VS (along with the property pane etc), if nothing more than as a 'preview' window).
Since my controls inherit from UIElement (and have the proper Measure/Arrange/Render overrides and so on), they function quite well when put on, say, a regular Canvasor Grid. But the VS XAML parser is not too happy about my SurfacePanel (that implements IList<Surface>) when I am adding children to it in markup. It says "Cannot add content to an object of type "SurfacePanel"".
I know that if I add a Children property of an appropriate type and add an attribute to the SurfaceCanvas class ([ContentProperty("Children")]), it will work. But since the SurfacePanel is itself a collection capable of the same thing, is there a way to make XAML 'get it'?
Edit:
I can solve the XAML 'compliance' by adding a Children property on the SurfacePanel that simply returns its inner List, but then adding and removal of elements on that directly bypasses the internal logic that wire the child elements up.
If the inner list was an ObservableCollection, I could do it the conventional way and do the wiring in a CollectionChanged event handler - but basically the whole point of integrating IList in the Panel directly is to avoid that..
Edit 2:
This "works" (but bypasses the wiring):
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList<Surface>
{
private readonly List<Surface> _children = new List<Surface>();
public List<Surface> Children
{
get { return _children; }
}
}
I cannot return this because SurfacePanel is not a List<Surface>, but an IList<Surface>.
If I change the property to
public IList<Surface> Children
{
get { return this; }
}
I get an error message even with the following XAML (but not with <m:SurfacePanel/>):
<m:SurfacePanel>
</m:SurfacePanel>
The error message is
Cannot set content property 'Children' on element 'SurfacePanel'. 'Children' has incorrect access level or its assembly does not allow access.
Also implement IList and declare the Children property like this:
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList, IList<Surface>
{
public IList Children
{
get { return this; }
}
...
}

WPF binding to a function?

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).

WPF Exposing a calculated property for binding (as DependencyProperty)

I have a complex WPF control that for some reasons (ie. performance) is not using dependency properties but simple C# properties (at least at the top level these are exposed as properties).
The goal is to make it possible to bind to some of those top level properties -> I guess I should declare them as DPs.(right? or is there some other way to achieve this? )
I started reading on MSDN about DependencyProperties and DependencyObjects and found an example:
public class MyStateControl : ButtonBase
{
public MyStateControl() : base() { }
public Boolean State
{
get { return (Boolean)this.GetValue(StateProperty); }
set { this.SetValue(StateProperty, value); }
}
public static readonly DependencyProperty StateProperty = DependencyProperty.Register(
"State", typeof(Boolean), typeof(MyStateControl),new PropertyMetadata(false));
}
If I'm right - this code enforces the property to be backed up by DependencyProperty which restricts it to be a simple property with a store(from functional point of view, not technically) instead of being able to calculate the property value each time getter is called and setting other properties/fields each time setter is called.
What can I do about that? Is there any way I could make those two worlds meet at some point?
//edit
I guess I have to tell you a little more about what I want to do and what my limitations are. So:
I have TabControl that is bound to a collection of ViewModel(I'm using MVVM pattern) objects. Every tab is meant to be an editor for one object of that collection. Objects can be of different types so I have multiple definitions each with a different DataType property. Now I have that complex WPF Control that I want to use as a part of one of those DataTemplates. If I use usual TextBox I can simply bind to its Text property, but I can't do the same with Text property of that custom control simply because its Text property is not a dependency property.
In this scenario I have :
no direct access to the control itself nor to its events
no code behind file that I can use to do that kind of thing
I can see however a dirty solution -
In the Window class I would have to subscribe to CollectionChanged event of the collection that is bound to the TabControl.
Whenever an item is added to that collection use ItemContainerGenerator to obtain a copy of I suppose TabItem and use it to find the right copy of 'complex control'
Regiter items handlers to 'complex controls' events to do the job
This is wrong because:
this is agains MVVM - I have to play with tab control to do the job instead of doing it in the ViewModel class
this couples in an unwanted way the view and viewmodel
I think you are mixing up Dependency Properties and implementing INotifyPropertyChanged on your classes.
You don't need your property to be a dependency property, you just need your class to implement INotifyPropertyChanged and call OnPropertyChanged whenever the state of your object changes in a way that would affect the value you want to expose to binding.
So let's say you have a property Sum that you want to bind to. The Sum property simple adds two other properties (or fields, or whatever) together. When anything happens that affects the Sum calculation, you want to notify that the Sum value has changed, so the any controls bound to Sum get updated.
public int Sum => Value1 + Value2;
public int Value1
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value1 = value;
OnPropertyChanged("Sum");
}
}
public int Value2
{
set
{
// changing this affects "Sum", so I need to notify that the binding should update
_value2 = value;
OnPropertyChanged("Sum");
}
}
It seems to me that you've been saddled with a WPF user control that was built by someone who didn't intend it to be used with data binding. I would assume that this is for one of two reasons: a) there's some logical reason that you shouldn't be able to bind to this property, or b) the original author of this control didn't know what he was doing. You haven't provided enough information for me to know which of those two conditions is the one you're really working under.
But in general, the reason you can't expose calculated properties for binding is that calculated properties generally don't have a setter. It doesn't make sense to set a property whose value is calculated.
If there are other properties whose values need to be updated when this one changes, the right approach (or at least the one consonant with the design of dependency properties) is to handle those updates in the dependency property's callback function, which is kind of what the callback function is for.

intercept RelativeSource FindAncestor

I have a WPF application which runs as a excel plugin, it has its visual tree like so
Excel
ElementHost
WPF UserControl
WPF ribbon bar control
Now any controls sitting on the WPF ribbon bar control are not enabled when the plugin is loaded within excel. See error below
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=IsActive; DataItem=null; target element
is 'Ribbon' (Name=''); target property is 'NoTarget' (type 'Object')
If I nest the ribbon bar control in a standalone Window(outside excel) it works fine.
Is there a way to intercept the FindAncestor call for a Window and wire it to something else.? Note that I cannot change the above binding as it isn't my control.
The most direct answer
FindAncestor is processed internally by WPF and will search up the visual tree as far as it can before going anywhere else. Only when it reaches a Visual that has no visual parent will it search elsewhere, and this depends on what it reached. For example, if it hits a FrameworkContentElement it can go to the document's container. Unfortunately if the top of the visual tree is a ElementHost, it will stop, so there is no way to reroute the call.
This means that your simplest option is to replace the binding. Fortunately this is not very difficult.
How to automatically replace a binding
Here is a simple method I wrote a while back that searches through a visual tree and replaces bindings as directed by an updateFunction. If the updateFunction returns a different binding than it is passed, the binding is updated.
static void UpdateBindings(Visual visual, Func<Binding, Binding> updateFunction)
{
if(visual==null) return;
for(int i=0; i<VisualTreeHelper.GetChildrenCount(visual); i++)
UpdateBindings(VisualTreeHelper.GetChild(visual, i) as Visual, updateFunction);
for(var enumerator = visual.GetLocalValueEnumerator(); enumerator.MoveNext(); )
{
var property = enumerator.Current.Property;
var binding = BindingOperations.GetBinding(visual, property);
if(binding==null) continue;
var newBinding = updateFunction(binding);
if(newBinding!=binding)
BindingOperations.SetBinding(visual, property, newBinding);
}
}
To illustrate how this works, here is how you could write a method that replaces a specific AncestorType in all RelativeSource FindAncestor instances, as follows:
static void ReplaceFindAncestorType(Visual visual, Type fromType, Type toType)
{
UpdateBindings(visual, binding =>
binding.RelativeSource.Mode != RelativeSourceMode.FindAncestor ? binding :
binding.RelativeSource.AncestorType != fromType ? binding :
new Binding
{
RelativeSource = new RelativeSource(
RelativeSourceMode.FindAncestor,
toType,
binding.RelativeSource.AncestorLevel),
Path = binding.Path,
Mode = binding.Mode,
Converter = binding.Converter,
StringFormat = binding.StringFormat,
UpdateSourceTrigger = binding.UpdateSourceTrigger,
});
}
Note that only commonly-used properties are copied over to the new binding.
The ReplaceFindAncestorVisualType method could be used something like this:
elementHost.LayoutUpdated += (obj, e) =>
{
ReplaceFindAncestorType(elementHost, typeof(Window), typeof(ElementHost);
};
In your case this generic replace technique won't work: It will be looking for an IsActive property on your ElementHost, which does not exist. So you probably need to change more than just the RelativeSource. This means your actual code will be more like this:
elementHost.LayoutUpdated += (obj, e) =>
{
UpdateBindings(elementHost, binding =>
binding.RelativeSource.AncestorType != typeof(Window) ? binding :
new Binding
{
Source = ultimateContainingWindowOrOtherObjectHavingIsActiveProperty,
Path = new PropertyPath("IsActive"), // Put property name here
});
};
Note that the above code assumes any FindAncestor:Window binding is the one we are looking for. More conditions can be added as needed in the conditional.
Alternative solution
There is another, completely different, solution available: It is possible to actually host the content in a borderless Window and add custom code to keep this window positioned over the ElementHost so it appears to be within the other window. This is trickier than it sounds since you have to deal with things such as ActiveWindow, ForegroundWindow, Z Order, Minimized state, keyboard focus, etc. But if your needs are very simple this can be a reasonable solution.
When using the control in Excel there is no Window in the ancestry, however, perhaps you can use Snoop to find where the binding is defined, then during run-time, find the dependency object (by type) and change its property's binding expression?
Another option would be to add a custom control that inherits from Window as an ancestor, then bind that to the Excel control.

Delayed "rendering" of WPF/Silverlight Dependency Properties?

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 :).

Resources