Intercept RelativeSource FindAncestor

I have a WPF application that works as an excel plugin, it has its own visual tree like

  • Excel
    • Elementhost
      • WPF UserControl
        • WPF Tape Management

Now, any controls located on the WPF ribbon control panel are not included when the plugin is loaded into 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 install the ribbon path control in a separate window (outside of excel), it works fine.

Is there a way to intercept the FindAncestor call for a window and associate it with something else? Please note that I cannot change the specified binding, since this is not my control.

+2
source share
3 answers

FindAncestor WPF , , , . Visual, , , , . , FrameworkContentElement, . , ElementHost, , .

, - . , .

, , , updateFunction. updateFunction , , .

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);
  }
}

, , , AncestorType RelativeSource FindAncestor :

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,
    });
}

, .

ReplaceFindAncestorVisualType :

elementHost.LayoutUpdated += (obj, e) =>
{
  ReplaceFindAncestorType(elementHost, typeof(Window), typeof(ElementHost);
};

: IsActive ElementHost, . , , , RelativeSource. , :

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
    });
};

, , FindAncestor: - , . .

, : , ElementHost, . , , , ActiveWindow, ForegroundWindow, Z Order, Minimized state, keyboard focus .. , .

+2

Excel , , , Snoop, , , ( ) ?

0

Another option is to add a custom control that inherits from Window as the ancestor, and then bind it to the Excel control.

0
source

Source: https://habr.com/ru/post/1735942/


All Articles