Reputation: 5505
I created some attached dependency properties to have several elements setting some information on a centralized manager which tracks the 'active information'.
This is the attached behavior:
public class ActiveItemBehavior
{
public static readonly DependencyProperty ManagerProperty = DependencyProperty.RegisterAttached("Manager", typeof(ActiveItemManager), typeof(ActiveItemBehavior), new UIPropertyMetadata(ManagerChanged));
public static readonly DependencyProperty ItemProperty = DependencyProperty.RegisterAttached("Item", typeof(object), typeof(ActiveItemBehavior));
public static ActiveItemManager GetManager(DependencyObject depObj)
{
return (ActiveItemManager)depObj.GetValue(ManagerProperty);
}
public static void SetManager(DependencyObject depObj, ActiveItemManager manager)
{
depObj.SetValue(ManagerProperty, manager);
}
public static object GetItem(DependencyObject depObj)
{
return depObj.GetValue(ItemProperty);
}
public static void SetItem(DependencyObject depObj, object item)
{
depObj.SetValue(ItemProperty, item);
}
private static void ManagerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement fe = d as FrameworkElement;
fe.GotFocus += fe_GotFocus;
}
private static void fe_GotFocus(object sender, RoutedEventArgs e)
{
var manager = GetManager(sender as DependencyObject);
var newItem = GetItem(sender as DependencyObject);
var oldItem = manager.ActiveItem;
if (Object.ReferenceEquals(newItem, oldItem)) return;
manager.ActiveItem = newItem;
}
}
So basically the idea is that I attach two properties, one is the manager which keeps track of the active item and the other one is the object which is the item (to be the active one when the element gets the focus).
The manager is even more simple:
public class ActiveItemManager : FrameworkElement
{
public static readonly DependencyProperty ActiveItemProperty = DependencyProperty.Register("ActiveItem", typeof(object), typeof(ActiveItemManager));
public object ActiveItem
{
get { return this.GetValue(ActiveItemProperty); }
set { this.SetValue(ActiveItemProperty, value); }
}
}
So in my <UserControl.Resources>
I define one (or more managers):
<cc:ActiveItemManager x:Key="ActiveItemManager" ActiveItem="{Binding MyActiveItem, Mode=TwoWay, PresentationTraceSources.TraceLevel=High}" />
And I get several controls like TextBox
etc. which attach like this:
<TextBox Text="{Binding Station}" cc:ActiveItemBehavior.Manager="{StaticResource ActiveItemManager}" cc:ActiveItemBehavior.Item="{Binding}" />
So I basically working with MVVM pattern here and the VM for the view has a property MyActiveItem
which never gets the correct value. The manager instance itself gets the correct value set as ActiveItem
so this works as expected. But from the manager it never gets replicated back to the VM.
The binding trace shows:
System.Windows.Data Warning: 56 : Created BindingExpression (hash=43567975) for Binding (hash=27811722)
System.Windows.Data Warning: 58 : Path: 'ActiveMoxItem'
System.Windows.Data Warning: 61 : BindingExpression (hash=43567975): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=43567975): Attach to cc.ActiveItemManager.ActiveItem (hash=38526232)
System.Windows.Data Warning: 67 : BindingExpression (hash=43567975): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=43567975): Found data context element: ActiveItemManager (hash=38526232) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=43567975): DataContext is null
System.Windows.Data Warning: 65 : BindingExpression (hash=43567975): Resolve source deferred
System.Windows.Data Warning: 67 : BindingExpression (hash=43567975): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=43567975): Found data context element: ActiveItemManager (hash=38526232) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=43567975): DataContext is null
System.Windows.Data Warning: 67 : BindingExpression (hash=43567975): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=43567975): Found data context element: ActiveItemManager (hash=38526232) (OK)
System.Windows.Data Warning: 71 : BindingExpression (hash=43567975): DataContext is null
System.Windows.Data Warning: 67 : BindingExpression (hash=43567975): Resolving source (last chance)
System.Windows.Data Warning: 70 : BindingExpression (hash=43567975): Found data context element: ActiveItemManager (hash=38526232) (OK)
System.Windows.Data Warning: 78 : BindingExpression (hash=43567975): Activate with root item <null>
System.Windows.Data Warning: 106 : BindingExpression (hash=43567975): Item at level 0 is null - no accessor
System.Windows.Data Warning: 80 : BindingExpression (hash=43567975): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=43567975): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=43567975): TransferValue - using final value <null>
What am I missing to have the Manager receive the new active item and populate it to the VM by a binding?
Note: I already tried with some ElementName
or proxy elements. Also I found this which looks similar but is unanswered: StackOverflow
Upvotes: 0
Views: 557
Reputation: 5505
Okay, I got it to work.
I used a ProxyElement
.
The Resources-section looks like:
<UserControl.Resources>
<FrameworkElement x:Key="ProxyElement" DataContext="{Binding}" />
<cc:ActiveItemManager x:Key="ActiveItemManager" ActiveItem="{Binding DataContext.MyActiveItem, Source={StaticResource ProxyElement}, Mode=TwoWay}" />
</UserControl.Resources>
The proxy elements can only provide a datacontext if it is used on the UI so I got it put there but hidden.
<ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}" />
Now it works as expected. Although I must admit that I do not fully understand why.
Upvotes: 0
Reputation: 8791
The point of your code is you have a random value in ActiveItemBehavior.Item then you click inside your TextBox and ActiveItemManager.ActiveItem receives that value. Futhermore ActiveItemManager.ActiveItem shall push the value to MyActiveItem through Binding. To cut the story short why dont you just bind ActiveItemBehavior.Item to MyActiveItem?
Anyhow there is no DataContext when you inside a Resource. Therefore you cannot set MyActiveItem. To have DataContext inside Resource you need to inherit from a Freezable.
Upvotes: 1