How to handle Freezable in a custom MarkupExtension? - c#

I've got a working custom markup extension which retrieves information out of the DataContext in a specific way (unimportant for this question).
All is well until I use this markup extension in elements that are not part of the visual or logical tree. In my particular example in the element InputBindings. In this scenario instead of retrieving a FrameworkElement as DependencyObject I get a Freezable (KeyBinding).
How can I access the DataContext through code?
My XAML code:
<UserControl.InputBindings>
<KeyBinding
Key="CapsLock"
Command="{wtc:CommandBinding {x:Static b:Commands.OpenTimeLine}}" />
</UserControl.InputBindings>
Code in my custom markup extension where I normally retrieve my DataContext:
protected override object ProvideValue(
DependencyObject dependencyObject,
DependencyProperty dependencyProperty )
{
if ( dependencyObject is Freezable )
{
// TODO: How to handle freezable?
}
_frameworkElement = dependencyObject as FrameworkElement;
if ( _frameworkElement == null )
{
throw new InvalidImplementationException(
"The DataContextBinding may only be used on framework elements." );
}
if ( !_dataContextChangedHooked )
{
_frameworkElement.DataContextChanged += DataContextChanged;
_dataContextChangedHooked = true;
}
return ProvideValue( _frameworkElement.DataContext );
}
The entire source code is online as well. I have quite an extensive class hierarchy for markup extensions.
AbstractMarkupExtension ⇐ AbstractDependencyPropertyBindingExtension ⇐ AbstractDataContextBindingExtension ⇐ CommandBindingExtension

One solution is surprisingly easy. Assuming the DataContext you are looking for is the same as the DataContext of your root object you can simply use the IRootObjectProvider. This provider is accessible through the IServiceProvider which is passed as an argument of ProvideValue.
var rootProvider = (IRootObjectProvider)ServiceProvider
.GetService( typeof( IRootObjectProvider ) );
_frameworkElement = rootProvider.RootObject as FrameworkElement;
There might be more complex scenarios where you have to traverse the tree (through LogicalChildren) in order to find the desired DataContext.

Here would be the nasty reflection way:
var context = (FrameworkElement)typeof(DependencyObject)
.GetProperty("InheritanceContext", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dependencyObject, null);
var datacontext = context.DataContext;
(The cast to FrameworkElement is not safe, InheritanceContext is also of type DependencyObject, the InheritanceContext usually is the object declaring the property in which the Freezable is used, if it is not a FrameworkElement you might need to recurse)

Related

How to Get ContentControl Children Within Template

I spent several hours searching and testing and can't get it to work. I want to have a UserControl that exposes a template to fill a section of the UserControl. I got it to work by creating a DependencyProperty of type ContentTemplate (or DataTemplate?). Then I display it like this
<ContentControl x:Name="PlayerContent" ContentTemplate="{Binding PlayerTemplate, ElementName=W}" />
Now the problem is that when I use the UserControl I cannot set element names within the template.
<local:MediaPlayerWpf x:Name="PlayerUI" Height="auto" Width="auto">
<local:MediaPlayerWpf.PlayerTemplate>
<ControlTemplate>
<WindowsFormsHost x:Name="Host" Focusable="False" />
</ControlTemplate>
</local:MediaPlayerWpf.PlayerTemplate>
</local:MediaPlayerWpf>
This throws
Cannot set Name attribute value 'Host' on element
'WindowsFormsHost'. 'WindowsFormsHost' is under the
scope of element 'MediaPlayerWpf', which already had a name registered
when it was defined in another scope.
As a result, I have no way of accessing the control defined within the template. I also found no way of accessing the root of the children displayed in the ContentControl.
How can I access the "Host" control defined in the template?
It seems we can't use templates in such a way within UserControl. I don't know whether there's a work-around.
I ended up taking a different approach. I exposed a property to set the content.
public static DependencyProperty HostProperty = DependencyProperty.Register("Host", typeof(PlayerBase), typeof(MediaPlayerWpf), new PropertyMetadata(null, OnHostChanged));
public PlayerBase Host { get => (PlayerBase)base.GetValue(HostProperty); set => base.SetValue(HostProperty, value); }
private static void OnHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
MediaPlayerWpf P = d as MediaPlayerWpf;
if (e.OldValue != null)
P.HostGrid.Children.Remove(e.OldValue as PlayerBase);
if (e.NewValue != null) {
P.HostGrid.Children.Add(e.NewValue as PlayerBase);
P.UI.PlayerHost = e.NewValue as PlayerBase;
}
}
Then in the code-behind where the class is being used, I set it like this
Player.Host = new MpvMediaPlayerHost();
At least it's working.
EDIT: A better solution is to switch from User Control to Custom Control. I still applied the solution above, and switching to Custom Control allowed me to set the Host in a derived class' constructor.

How to set ItemsSource property programmatically?

This is XAML code;
<toolkit:AutoCompleteBox x:Name="newTaskNameTextBox"
ItemsSource="{StaticResource BankNamesList}" />
How to assign this ItemSource attribute to the newTaskNameTextBox by C# programmatically ?
(Solution for WPF)
You should use the TryFindResource method.
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
This searches up the logical tree, in the same way {StaticResource BankNamesList} does.
UPDATE: (solution for WP8)
Sounds lile you're using WP8 (which doesn't include FindResource / TryFindResource) so try this instead:
newTaskNameTextBox.ItemsSource = (IEnumerable)Resources["BankNamesList"];
UPDATE: (how to implement the missing TryFindResource)
Note that the code above requires the resource to exist in the owner of this code behind (e.g. the window). However, there may be cases where the resource exists in another parent element up the logical tree. For example, you may be writing the code behind for a custom user control but the resource you're looking for exists in the MainWindow. For such cases, it wouldn't be too hard to write a basic implementation of WPF's TryFindResouces, which has the advantage of searching up the logical tree (source link):
public static class FrameworkElementExtensions
{
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
var currentElement = element;
while (currentElement != null)
{
var resource = currentElement.Resources[resourceKey];
if (resource != null)
{
return resource;
}
currentElement = currentElement.Parent as FrameworkElement;
}
return Application.Current.Resources[resourceKey];
}
}
/**********************************************************************/
// Or, the recursive version of TryFindResource method as suggested by #Default:
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
if (element == null)
return Application.Current.Resources[resourceKey];
var resource = element.Resources[resourceKey];
if (resource != null)
{
return resource;
}
return TryFindResource(element.Parent, resourceKey);
}
So if you include this FrameworkElementExtensions class in your namespace, then you should be able to do this (same code I've given for WPF originally):
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
If BankNamesList is resource in the resources of your window, then in code behind you can do:
newTaskNameTextBox.ItemsSource = Resources["BankNamesList"]
Try this:
newTaskNameTextBox.ItemsSource = (IEnumerable)(Application.Current.Resources["BankNamesList"]);

Is data binding faster than modifying the properties of subcontrols manually?

So i have several subcontrols that need to take the value of the parent controls dependency property. Is binding the value to the dependency property of the parent going to be faster than just creating a callback method to occur when the parent's dependency property changes?
I was about to code it such that:
ItemsControl ic = this.signal_viewer_item_control;
int count = VisualTreeHelper.GetChildrenCount(ic);
foreach (var item in ic.Items)
{
ContentPresenter container = ic.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
if (container != null)
{
SignalGraph sg = container.ContentTemplate.FindName("signal_graph", container) as SignalGraph;
if (sg != null)
{
sg.GraphPenWidth = GraphPenWidth;
sg.DrawSignals();
}
}
}
so that I just manually modify the subcontrols graphpenwidth. Is it better to make that a dependency property and bind it to the parent's graphpenwidth value? i just thought that creating two dependency properties would be unnecessary overhead, but i'm wondering if there are benefits to having it in this situation
Edit: so i went back and tried to use dependency properties to compare the two, but then I can't seem to get it to work.
<wpfExp:SignalGraph
x:Name="signal_graph"
Height="75"
Signal="{Binding}"
signal_graph_window_width="{Binding ElementName=signal_box, Path=signal_graph_window_width, Mode=OneWay}"
X_Scale="{Binding ElementName=signal_box, Path=X_Scale, Mode=OneWay}"
MaxTimeValue="{Binding Source = {StaticResource ResourceKey=signal_data}, Path = MaxTimeValue, Mode=OneWay}">
<wpfExp:SignalGraph.GraphPenWidth>
<Binding ElementName="signal_box" Path="GraphPenWidth" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" NotifyOnTargetUpdated="True">
</Binding>
</wpfExp:SignalGraph.GraphPenWidth>
</wpfExp:SignalGraph>
I put a two way binding on graph penwidth between the two properties and then registered the new properties like so:
private static readonly DependencyProperty GraphPenWidthProperty =
DependencyProperty.Register("GraphPenWidth",
typeof(int), typeof(SignalGraph),
new FrameworkPropertyMetadata(new int(), new PropertyChangedCallback(GraphPenWidthChanged)));
public int GraphPenWidth
{
get
{
return (int)GetValue(GraphPenWidthProperty);
}
set
{
SetValue(GraphPenWidthProperty, value);
default_pen = new Pen(Brushes.Green, value);
}
}
private static void GraphPenWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SignalGraph sg = d as SignalGraph;
sg.DrawSignals();
}
but on modification the graphpenwidthchanged callback method is never being called. I'm wondering does it have something to do with it being in an itemtemplate? any clue what could cause it not to update?
If you dont have tons of bindings to different dependency properties, difference in performance between binding and manual setter is not noticeable. Anyway, if you are going to use manual setter, searching element in template every time your property is being changed - is not a good idea. What about returning a custom control in the GetContainerForItemOverride and storing its template child (SignalGraph in your case) as a property (you can get it in the OnApplyTemplate using the GetTemplateChild method)?

Order that DependencyProperties bindings are evaluated?

What determines the order that multiple DepdencyProperties on the same control get evaluated in?
I am using the Extended WPF Toolkit PropertyGrid and have both SelectedObject and PropertyDefinitions bound:
<extToolkit:PropertyGrid AutoGenerateProperties="False" SelectedObject="{Binding ActiveDataPoint}" PropertyDefinitions="{Binding ActiveDataPoint.Properties}">
The problem is that the OnSelectedObjectChanged fires from the dependency property, and in that changed handler it is referencing PropertyDefinitions, which it is seeing as null. If I comment out the OnSelectedObjectChanged handler then I can see when debugging that OnPropertyDefinitionsChanged is called AFTER the call to OnSelectedObjectChanged.
public static readonly DependencyProperty PropertyDefinitionsProperty = DependencyProperty.Register( "PropertyDefinitions", typeof( PropertyDefinitionCollection ), typeof( PropertyGrid ), new UIPropertyMetadata( null, OnPropertyDefinitionsChanged ) );
public PropertyDefinitionCollection PropertyDefinitions
{
get
{
return ( PropertyDefinitionCollection )GetValue( PropertyDefinitionsProperty );
}
set
{
SetValue( PropertyDefinitionsProperty, value );
}
}
private static void OnPropertyDefinitionsChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
Console.Write("I changed!");
}
public static readonly DependencyProperty SelectedObjectProperty = DependencyProperty.Register( "SelectedObject", typeof( object ), typeof( PropertyGrid ), new UIPropertyMetadata( null, OnSelectedObjectChanged ) );
public object SelectedObject
{
get
{
return ( object )GetValue( SelectedObjectProperty );
}
set
{
SetValue( SelectedObjectProperty, value );
}
}
private static void OnSelectedObjectChanged( DependencyObject o, DependencyPropertyChangedEventArgs e )
{
PropertyGrid propertyInspector = o as PropertyGrid;
if( propertyInspector != null )
propertyInspector.OnSelectedObjectChanged( ( object )e.OldValue, ( object )e.NewValue );
}
The problem I am facing is discussed on this forum thread, but I am asking a more general WPF question of how I can change the order that these properties are updated.
I have tried having multiple calls to NotifyPropertyChanged in different orders but that doesn't seem to affect this. Can I cause the order to be different or should I just modify the PropertyGrid so that it will work for either order?
And just one more contra-example to confirm what has been said already
...to never rely on the order of properties being applied
In a custom UserControl with defined DependencyProperty-ies (.NET 4.5 etc.) - as PropertyChangedCallbacks are called at initialization...
the actual order is determined from the order of "code behind definitions" (static fields)
...I'm guessing that has to do with the order of Registration.
In some other cases the order depends on how the properties are lined up in the XAML.
The short answer is that it is all a black box and you should not rely on one being evaluated before or after another. So the best approach would be to modify the PropertyGrid so it works regardless of the order the properties are set.
The long answer is it looks like it depends on how the order that the bindings are specified. So you can do:
<extToolkit:PropertyGrid AutoGenerateProperties="False"
PropertyDefinitions="{Binding ActiveDataPoint.Properties}"
SelectedObject="{Binding ActiveDataPoint}"
>
Instead of:
<extToolkit:PropertyGrid AutoGenerateProperties="False"
SelectedObject="{Binding ActiveDataPoint}"
PropertyDefinitions="{Binding ActiveDataPoint.Properties}"
>
Again, it would be bad practice to rely on this. And this quirk may only work for when the control is initialized. Changes to ActiveDataPoint or the DataContext later, may result in a different order.

How to resolve bound object from bindingexpression with WPF?

Hi does anyone know if there are any inbuilt classes for resolving a bound object from a bindingexpression and it's DataItem and property path?
I'm attempting to write a Blend 3 behavior for textboxes which automatically invokes methods on an object bound to the textbox Text property.
The textbox is bound to a property on a viewmodel class. What I want to do is resolve the viewmodel class from the binding expression and then make calls on this.
I first retrieve the binding expression from the behavior's associated object like so:
private BindingExpression GetTextBinding()
{
return this.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
}
Having done this, if we look at the binding expression, we can see it has a reference to the data context via the binding expression's DataItem property.
In addition, we have the relative path of the property which is bound on the binding expression's parent binding.
So, we can get this information:
var bindingExpression = GetTextBinding();
object dataContextItem = bindingExpression.DataItem;
PropertyPath relativePropertyPath = bindingExpression.ParentBinding.Path;
Now, this property path could potentially be a deeply nested and complex path, which I would very much like to avoid having to (re?)implement resolution of. I've searched around the .NET documentation and bounced around the assemblies with reflector, all to no avail - I can't find what surely must exist - there's got to be some class which performs the resolution of the path for the dataitem (the data context).
Does anyone know where this might exist? Any suggestions for alternative ways of resolving the bound object?
Note, I'm trying to get at the bound object which is the parent of the bound property (the string in this case) - I can obviously easily get at the bound value, but it's the parent I need.
Thanks in advance for any help!
Phil
For people in the future who stumble on this question:
When .NET 4.5 becomes available it will have a number of new properties on the BindingExpression to greatly simplify what you are looking for.
ResolvedSource - The object that is actually being bound to, helpful when you have a binding source like 'grandparent.parent.me.Name'. This would return the 'me' object.
ResolvedSourcePropertyName - The name of the property on the ResolvedSource that is bound to. In the case above, "Name".
Similarly, there will be Target, and TargetName properties.
With these helper properties on BindingExpression you could use some shorter and much more simplified reflection that is more likely to work in extreme situations (indexers).
Below is a quick implementation for an extension method that will do just what you are looking for. I couldn't find anything related to this either. The method below will always return null if for some reason the value cannot be found. The method won't work when the path includes []. I hope this helps!
public static T GetValue<T>(this BindingExpression expression, object dataItem)
{
if (expression == null || dataItem == null)
{
return default(T);
}
string bindingPath = expression.ParentBinding.Path.Path;
string[] properties = bindingPath.Split('.');
object currentObject = dataItem;
Type currentType = null;
for (int i = 0; i < properties.Length; i++)
{
currentType = currentObject.GetType();
PropertyInfo property = currentType.GetProperty(properties[i]);
if (property == null)
{
currentObject = null;
break;
}
currentObject = property.GetValue(currentObject, null);
if (currentObject == null)
{
break;
}
}
return (T)currentObject;
}
Just for further information, PropertyPath resolution is handled by an internal MS class called PropertyPathWorker, which lives in PresentationFramework under MS.Internal.Data. If you open it up in Reflector, you can see it's quite complicated, so I wouldn't recommend trying to duplicate its functionality. It's tightly coupled with the overall Binding architecture.
The most robust way to support all property path syntax--including attached dependency properties and hierarchical traversal--is probably to create a dummy DependencyObject with a DependencyProperty. You can then create a Binding from your 'owner' path to the dummy dependency property, create a new BindingExpression and then call the expression's UpdateTarget. It's a rather heavy way of accomplishing what, on the surface, looks like a simple task, but I think there are a lot of hidden gotchas in the way property paths are resolved for binding.
I believe this other StackOverflow solution posted here may also work for you.
Copied code block for reference, read original post for more details provided by Thomas Levesque.
public static class PropertyPathHelper
{
public static object GetValue(object obj, string propertyPath)
{
Binding binding = new Binding(propertyPath);
binding.Mode = BindingMode.OneTime;
binding.Source = obj;
BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
return _dummy.GetValue(Dummy.ValueProperty);
}
private static readonly Dummy _dummy = new Dummy();
private class Dummy : DependencyObject
{
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(Dummy), new UIPropertyMetadata(null));
}
}
As Dan Bryant already noted, PropertyPath resolution is tightly coupled with the overall binding architecture.
If you need the exact same resolution as WPF does it, you should probably use Thomas Levesque's answer to this question.
However, if you just need general path resolution, you can use a nuget package Pather.CSharp I developed that does exactly that.
It is essentially similar to zhech's answer, but more sophisticated.
Its main method is Resolve on the Resolver class. Passing in the target object and a path as string returns the desired result.
An Example:
IResolver resolver = new Resolver();
var target = new { Property1 = new { Property2 = "value" } };
object result = r.Resolve(target, "Property1.Property2");
It also supports collection access via index or dictionary access via key.
Example paths for these are:
"ArrayProperty[5]"
"DictionaryProperty[Key]"
Just in case others find it useful, below is a quick one liner extension method for .net4.5 (and beyond) to retrieve the bound value..
public static object GetValue(this BindingExpression bindingExpression)
{
return bindingExpression?.ResolvedSource.GetType().GetProperty(bindingExpression.ResolvedSourcePropertyName)?.GetValue(bindingExpression.ResolvedSource);
}

Categories

Resources