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.
Related
My application uses the MVVM architecture, with the ViewModel having no knowledge of the View. When a ViewModel object requires a new View be shown it exposes a public ShowNewView property that is an object whose class is based on my ViewModel base class. The WPF View binds a custom DependencyProperty to this and uses the PropertyChangedCallback to construct and show an approperiate Window.
This all works well the first time the ShowNewView property is set. However, if the user closes the window and then attempts to re-open it, the ShowNewView property's value has not changed when the PropertyChanged event is raised and the PropertyChangedCallback is not invoked.
In order to 'trick' the DependencyProperty into detecting that the value has changed (even though the value stored in the ViewModel's property may not have actually changed), I have used the SetCurrentValue method exposed by the Window class to force the DependencyProperty's value to null.
#region ShowNewViewProperty
private static readonly DependencyProperty _ShowNewViewProperty =
DependencyProperty.RegisterAttached
(
"ShowNewView",
typeof(IRootViewModel),
typeof(WpfViewWindow),
new PropertyMetadata(ShowNewViewPropertyChanged)
);
public static DependencyProperty ShowNewViewProperty { get { return _ShowNewViewProperty; } }
public static IRootViewModel GetShowNewView(Window source)
{
return (IRootViewModel)source.GetValue(ShowNewViewProperty);
}
public static void SetShowNewView(Window target, IRootViewModel value)
{
target.SetValue(ShowNewViewProperty, value);
}
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = window.CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
window.SetCurrentValue(ShowNewViewProperty, null);
}
}
#endregion
Technically this works, as it results in the callback being run when the PropertyChanged event fires, regardless of whether the value has actually changed or not. However, it results in the callback being called (recursively) twice every time the ViewModel's property is updated: once in response to the ViewModel's event and once in response to the SetCurrentValue method being called.
There are a number of questions here relating to the PropertyChangedCallback not being called, or not being called more than once, in other situations.
PropertyChangedCallback on DependencyProperty Only Firing Once covers the situation where the property is a collection and the collection content changes, but the collection itself does not. However, my property is not a collection and everything is working exactly as documented.
WPF dependency property setter not firing when PropertyChanged is fired, but source value is not changed looks very promising, but the answer only suggests using the callback that I am already.
Is there a more elegant way to achieve this that does not result in the callback being run twice for each PropertyChanged event from the ViewModel? I.e. is there some way to get around the framework's check to verify that the old and new values are different?
Clarification
The View being created isn't necessarily always a WPF Window, for example, in my unit tests it is a mock, and later in the project it may be a sperate logging assembly. Nor are all of the ViewModel objects from the same assembly, it is known that additional functionality will be required in the future, but the specifics are currently undefined. The application allows the user to connect a device by way of a simple network. Initially the network is ModbusRTU over RS-485, however, the end customer may want to use CANOpen or Profinet or some other transport layer, and I have to provide a plug-in mechanism that allows the new functionality to be added without changing the existing code.
To be fair, there are several alternative mechanisms that I could use to achieve the same result (i.e. having the ViewModel request a new View be created), but I'm interested in knowing if there is a way to make a DependencyPropety 'forget' what it's previous value was.
The usual solution to this type of problem is for you to extract the code from your ShowNewViewPropertyChanged method and to put it into a different method:
private void SomeNewMethod(IRootViewModel newValue)
{
// Create a child WpfViewWindow. This method is part of my
// framework that uses ResourceDictionary entries, imported by MEF
// to locate the View class corresponding to the ViewModel parameter's
// class.
WpfViewWindow modelessWindow = CreateWpfViewWindow(newValue);
if (null != modelessWindow)
{
// Show the new WpfViewWindow.
modelessWindow.Show();
}
// Clear the current value so that the next PropertyChanged event
// is processed even if the underlying value has not actually changed.
SetCurrentValue(ShowNewViewProperty, null);
}
Now you can simply call that method from both the ShowNewViewPropertyChanged handler and from wherever else you desire:
private static void ShowNewViewPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WpfViewWindow window = d as WpfViewWindow;
IRootViewModel newValue = e.NewValue as IRootViewModel;
if ((null != window) && (null != newValue))
{
window.SomeNewMethod(newValue);
}
}
I just searched for a way to enable a child control while the parent control has IsEnabled = false.
All answers that I have found up to now say that it is not possible - one has to enable the parent and disable the child controls except the ones that should still be enabled.
However, by overriding the Metadata for the IsEnabledProperty in the App.xaml.cs file, I was able to change this default behavior:
protected override void OnStartup(StartupEventArgs e)
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof(FrameworkElement),
new UIPropertyMetadata(true,IsEnabledChanged, CoerceIsEnabled));
}
private void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < childrenCount; ++i)
{
var child = VisualTreeHelper.GetChild(d, i);
child.CoerceValue(UIElement.IsEnabledProperty);
}
}
private object CoerceIsEnabled(DependencyObject d, object basevalue)
{
var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
if (parent != null && parent.IsEnabled == false)
{
if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
{
return false;
}
}
return basevalue;
}
Now you can manually set the IsEnabled property on a child, which overrides the parent value.
Are there any drawbacks of this approach?
The solution that is proposed in the question may be fine in some scenarios. However, changing the default framework behavior for all UIElements in the application, could introduce compatibility issues and it might be difficult in the future to understand where/why the behavior was changed.
An alternative approach would be to keep the default behavior of the framework and only override that behavior manually in specific places, when needed. One way to do this is be creating a simple wrapper element that breaks the IsEnabled inheritance chain from the parent.
The framework's default coerce callback checks the parent IsEnabled value and inherits it. This control sets a new coerce callback that just returns the value directly without checking inheritance.
public class ResetIsEnabled : ContentControl
{
static ResetIsEnabled()
{
IsEnabledProperty.OverrideMetadata(
typeof(ResetIsEnabled),
new UIPropertyMetadata(
defaultValue: true,
propertyChangedCallback: (_, __) => { },
coerceValueCallback: (_, x) => x));
}
}
It can be used like this
<ParentControl IsEnabled="False">
<!-- Any elements here will have IsEnabled set to false, inherited from the parent -->
<ResetIsEnabled>
<!-- Any child elements here will have IsEnabled set to true (the default value) -->
</ResetIsEnabled>
</ParentControl>
This worked for my situation on a control used several times with some slight modifications.
Placing here to help any future web searchers in a similar situation:
placed it in a static constructor instead of an event, otherwise it tried to set it multiple times and threw a "PropertyMetadata is already registered for type '{type}'." exception.
Changed the type to match the control
Code:
Make sure to find and replace [CustomControl] with the type name of your control.
static [CustomControl]()
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof([CustomControl]), new UIPropertyMetadata(true, [CustomControl]_IsEnabledChanged, CoerceIsEnabled));
}
private static void [CustomControl]_IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(d);
for (int i = 0; i < childrenCount; ++i)
{
var child = VisualTreeHelper.GetChild(d, i);
child.CoerceValue(UIElement.IsEnabledProperty);
}
}
private static object CoerceIsEnabled(DependencyObject d, object basevalue)
{
var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
if (parent != null && parent.IsEnabled == false)
{
if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
{
return false;
}
}
return basevalue;
}
The drawback is at least, that you break the basic concept, and the IsEnabled is not used for the intended scope. This workaround also makes maintenance a bit more complex (the developer has to understand first, why it works differently).
As it is suggested in comments, I would say, that a redesign of this window would help. Especially, if I would like to forbid only the editing (data modification) in the form, I would use other properties like IsReadOnly.
Another option is to override the FrameworkPropertyMetadataOptions to remove the Inherits property. I had a similar problem with the FontSize and this worked well:
FontSizeProperty.OverrideMetadata(
typeof(YourControl),
new FrameworkPropertyMetadata(8.0,
FrameworkPropertyMetadataOptions.None, changed));
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)?
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)
I am creating sort of a "Navigation panel" (which is actually an ItemControl) for SL and using Regions to allow each module to add his link to the panel.
Problem is that modules loading is inconsistent and thus order of links in the panel can change according to modules loading order.
Restricting the modules order is out of the question.
Other feasible option is the order the region's Views Collection that is binded to the ItemControl, the problem is that ViewCollection is very limited, so ordering it is pretty hard.
Did I miss an option, do you have an idea?
Thanks
Ariel
In Prism4 you just apply the ViewSortHintAttribute to your views:
[ViewSortHint("100")]
class FirstView : UserControl { }
[ViewSortHint("200")]
class SecondView : UserControl { }
The default sort comparer on the regions will pick up this attribute and sort the views accordingly. You can put any string into the attribute but I tend to use medium sized numbers that allow me to easily put a new view in between existing ones.
Refering to Sam's answer you first have to build your comparer. The following one is also capable of views that do not have a dedicated wish to be positioned at. To attach this comparer to the region that has to be sorted you can use a way intruduced by the prism manual:
public partial class MainView : UserControl
{
public MainView( )
{
InitializeComponent( );
ObservableObject<IRegion> observableRegion = RegionManager.GetObservableRegion( ContentHost );
observableRegion.PropertyChanged += ( sender, args ) =>
{
IRegion region = ( (ObservableObject<IRegion>)sender ).Value;
region.SortComparison = CompareViews;
};
}
private static int CompareViews( object x, object y )
{
IPositionView positionX = x as IPositionView;
IPositionView positionY = y as IPositionView;
if ( positionX != null && positionY != null )
{
//Position is a freely choosable integer
return Comparer<int>.Default.Compare( positionX.Position, positionY.Position );
}
else if ( positionX != null )
{
//x is a PositionView, so we favour it here
return -1;
}
else if ( positionY != null )
{
//y is a PositionView, so we favour it here
return 1;
}
else
{
//both are no PositionViews, so we use string comparison here
return String.Compare( x.ToString( ), y.ToString( ) );
}
}
}
At least in prism V4 there you can tell the region manager how to sort the views in a specific region. You just need to provide a compare function to the region.
This example sorts by a very stupid value, the function name:
private static int CompareViews(object x, object y)
{
return String.Compare(x.ToString(), y.ToString());
}
this._regionManager.Regions["MyRegion"].SortComparison = CompareViews;
Of course the region needs to be known to the region manager before you can set the SortComparison. So far the only workaround I found to achieve this was to defer to set the comparison function using the Dispatcher:
private readonly IRegionManager _regionManager;
[ImportingConstructor]
public ShellViewModel(IRegionManager regionManager)
{
this._regionManager = regionManager;
Dispatcher dp = Dispatcher.CurrentDispatcher;
dp.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate
{
if (this._regionManager.Regions.ContainsRegionWithName("MyRegion"))
this._regionManager.Regions["MyRegion"].SortComparison = CompareViews;
}));
}
Of course you should use some more useful information than the class name for the sorting order, but this should be easy to solve.
This is not built into Prism regions, however it's easily implementable.
Damian Schenkelman has posted an extension method he created for adding a region to an index that seems to work pretty well.
http://blogs.southworks.net/dschenkelman/2009/03/14/how-to-add-a-view-to-a-region-in-a-particular-index-with-prism-v2/
Hope this helps.
I found that Sam's solution worked, but discovered that it executes the sort when all views have been added to the region, thus sorting the views twice.
Although it is still a valid solution, reading this post in Prism discussion made me think about a way of implementing this just when the region has been loaded, but before any views have been added yet.
1 - Subscribe to the CollectionChanged of Regions collection
I placed this in the Shell ViewModel code which is the one associated to the View that contains the region I want to sort. Whenever the IRegionManager import has been resolved I subscribe to the CollectionChanged event of its Regions collection:
this._regionManager.Regions.CollectionChanged +=
new NotifyCollectionChangedEventHandler(Regions_CollectionChanged);
2 - Change the SortComparison of the region in the event delegate
Then the delegate Regions_CollectionChanged will execute whenever the Regions collection is updated and will change the SortComparison of my desired region:
void Regions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var o in e.NewItems)
{
IRegion region = o as IRegion;
if (region != null && region.Name == RegionNames.NavigationRegion)
{
region.SortComparison = CompareNavigatorViews;
}
}
}
}
3 - Define the CompareNavigatorViews delegate
In my case, I just sort the views by the title of the assembly where they are contained, you can implement your own compare method here. Remember that the objects you'll receive here are the Views and not the ViewModels.
private static int CompareNavigatorViews(object x, object y)
{
if (x == null)
if (y == null)
return 0;
else
return -1;
else
if (y == null)
return 1;
else
{
AssemblyInfo xAssemblyInfo = new AssemblyInfo(Assembly.GetAssembly(x.GetType()));
AssemblyInfo yAssemblyInfo = new AssemblyInfo(Assembly.GetAssembly(y.GetType()));
return String.Compare(xAssemblyInfo.Title, yAssemblyInfo.Title);
}
}
Just in case somebody asks, the AssemblyInfo class is an utility class I made. To get the title of an assembly you could use this function:
string GetAssemblyTitle(Assembly assembly)
{
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
if (attributes.Length == 1)
{
return (attributes[0] as AssemblyTitleAttribute).Title;
}
else
{
// Return the assembly name if there is no title
return this.GetType().Assembly.GetName().Name;
}
}
Hope this helps someone!
Well as the lack of answers counting. I have not found a solution with Prism.
Instead I've used MEF to solve this.
I will write a blog post on it and update this place holder.