Advertisement
bobmarley12345

merging inherited list wpf

Feb 22nd, 2024
1,034
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 10.47 KB | None | 0 0
  1. //
  2. // Copyright (c) 2023-2024 REghZy
  3. //
  4. // This file is part of FramePFX.
  5. //
  6. // FramePFX is free software; you can redistribute it and/or
  7. // modify it under the terms of the GNU General Public License
  8. // as published by the Free Software Foundation; either
  9. // version 3.0 of the License, or (at your option) any later version.
  10. //
  11. // FramePFX is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. // Lesser General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU General Public License
  17. // along with FramePFX. If not, see <https://www.gnu.org/licenses/>.
  18. //
  19.  
  20. using System.Collections.Generic;
  21. using System.Diagnostics;
  22. using System.Windows;
  23. using System.Windows.Data;
  24. using FramePFX.Utils;
  25.  
  26. namespace FramePFX.Interactivity.Contexts {
  27.     /// <summary>
  28.     /// A class that is used to extract contextual information from WPF components
  29.     /// </summary>
  30.     public static class DataManager {
  31.         // public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (ButtonBase));
  32.  
  33.         /// <summary>
  34.         /// The context data property, used to store contextual information relative to a specific dependency object
  35.         /// </summary>
  36.         public static readonly DependencyProperty ContextDataProperty = DependencyProperty.RegisterAttached("ContextData", typeof(ContextData), typeof(DataManager), new PropertyMetadata(null, OnDataContextChanged));
  37.         private static readonly DependencyPropertyKey InheritedContextDataPropertyKey = DependencyProperty.RegisterAttachedReadOnly("InheritedContextData", typeof(ContextData), typeof(DataManager), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, OnInheritedDataContextChanged, OnCoerceInheritedContextData));
  38.  
  39.         private static readonly DependencyProperty IsUpdatingLocalInheritedContextProperty = DependencyProperty.RegisterAttached("IsUpdatingLocalInheritedContext", typeof(bool), typeof(DataManager), new PropertyMetadata(BoolBox.False));
  40.  
  41.         private static void SetIsUpdatingLocalInheritedContext(DependencyObject element, bool value) => element.SetValue(IsUpdatingLocalInheritedContextProperty, value.Box());
  42.         private static bool GetIsUpdatingLocalInheritedContext(DependencyObject element) => (bool) element.GetValue(IsUpdatingLocalInheritedContextProperty);
  43.  
  44.         /// <summary>
  45.         /// A property used to store the merged accumulation of all available context data from the visual tree for a specific element
  46.         /// </summary>
  47.         public static readonly DependencyProperty InheritedContextDataProperty = InheritedContextDataPropertyKey.DependencyProperty;
  48.  
  49.         public static IContextData GetInheritedContextData(DependencyObject element) {
  50.             return (IContextData) element.GetValue(InheritedContextDataProperty);
  51.         }
  52.  
  53.         private static void OnDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) {
  54.             // UpdateInheritedContextOnContextChanged(d, e.NewValue as ContextData);
  55.             // SetIsUpdatingLocalInheritedContext(element, true);
  56.             if (e.NewValue == null) {
  57.                 element.ClearValue(InheritedContextDataPropertyKey);
  58.             }
  59.             else {
  60.                 ContextData data = (ContextData) e.NewValue;
  61.                 DependencyObject parent = VisualTreeUtils.GetParent(element);
  62.                 if (parent?.GetValue(InheritedContextDataProperty) is ContextData parentData) {
  63.                     data = ContextData.Merge(parentData, data);
  64.                 }
  65.  
  66.                 element.SetValue(InheritedContextDataPropertyKey, data);
  67.             }
  68.  
  69.             // SetIsUpdatingLocalInheritedContext(element, false);
  70.         }
  71.  
  72.         private static object OnCoerceInheritedContextData(DependencyObject element, object value) {
  73.             if (GetIsUpdatingLocalInheritedContext(element)) {
  74.                 return value;
  75.             }
  76.  
  77.             CompositeCollection er;
  78.  
  79.             object localObject = element.ReadLocalValue(ContextDataProperty);
  80.             if (localObject == DependencyProperty.UnsetValue || !(localObject is ContextData localData) || localData.Count < 1) {
  81.                 return value;
  82.             }
  83.  
  84.             DependencyObject parent = VisualTreeUtils.GetParent(element);
  85.             if (parent?.GetValue(InheritedContextDataProperty) is ContextData parentData) {
  86.                 value = value is ContextData valueCtx ? ContextData.Merge(parentData, valueCtx) : parentData;
  87.             }
  88.  
  89.             return value;
  90.         }
  91.  
  92.         private static void OnInheritedDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) {
  93.             if (GetIsUpdatingLocalInheritedContext(element)) {
  94.                 return;
  95.             }
  96.  
  97.             Debug.WriteLine("ee");
  98.  
  99.             // ContextData parentData = GetInheritedContextFromParents(target);
  100.             // if (newData == null) {
  101.             //     newData = parentData;
  102.             // }
  103.             // else if (parentData != null) {
  104.             //     newData = ContextData.Merge(parentData, newData);
  105.             // }
  106.             // target.SetValue(InheritedContextDataPropertyKey, newData);
  107.             // UpdateChildrenInheritedContext(target, newData);
  108.         }
  109.  
  110.         /// <summary>
  111.         /// Sets or replaces the context data for the specific dependency object
  112.         /// </summary>
  113.         public static void SetContextData(DependencyObject element, ContextData value) {
  114.             element.SetValue(ContextDataProperty, value);
  115.         }
  116.  
  117.         /// <summary>
  118.         /// Gets the context data for the specific dependency object
  119.         /// </summary>
  120.         public static ContextData GetContextData(DependencyObject element) {
  121.             return (ContextData) element.GetValue(ContextDataProperty);
  122.         }
  123.  
  124.         /// <summary>
  125.         /// Clears the <see cref="ContextDataProperty"/> value for the specific dependency object
  126.         /// </summary>
  127.         public static void ClearContextData(DependencyObject element) {
  128.             element.ClearValue(ContextDataProperty);
  129.         }
  130.  
  131.         // private static readonly PropertyInfo TreeLevelPropertyInfo = typeof(Visual).GetProperty("TreeLevel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) ?? throw new Exception("Could not find TreeLevel property");
  132.  
  133.         /// <summary>
  134.         /// Does bottom-to-top scan of the element's visual tree, and then accumulates and merged all of the data keys
  135.         /// associated with each object from top to bottom, ensuring the bottom of the visual tree has the most power
  136.         /// over the final data context key values
  137.         /// </summary>
  138.         /// <param name="obj"></param>
  139.         /// <returns></returns>
  140.         public static ContextData EvaluateContextData(DependencyObject obj) {
  141.             ContextData ctx = new ContextData();
  142.  
  143.             // I thought about using TreeLevel, then thought reflection was too slow, so then I profiled the code...
  144.             // This entire method (for a clip, 26 visual elements to the root) takes about 20 microseconds
  145.             // Using the TreeLevel trick adds about 10 microseconds on top of it
  146.  
  147.             // int initialSize = 0;
  148.             // if (obj is UIElement element && element.IsArrangeValid)
  149.             //     initialSize = (int) (uint) TreeLevelPropertyInfo.GetValue(element);
  150.             // if (initialSize < 1)
  151.             //     initialSize = 32;
  152.  
  153.             // Accumulate visual tree bottom-to-top. Visual tree will contain the reverse tree
  154.             List<DependencyObject> visualTree = new List<DependencyObject>(32);
  155.             for (DependencyObject dp = obj; dp != null; dp = VisualTreeUtils.GetParent(dp)) {
  156.                 visualTree.Add(dp);
  157.             }
  158.  
  159.             // Scan top-down in order to allow deeper objects' entries to override higher up entries
  160.             for (int i = visualTree.Count - 1; i >= 0; i--) {
  161.                 DependencyObject dp = visualTree[i];
  162.                 object localEntry = dp.ReadLocalValue(ContextDataProperty);
  163.                 if (localEntry != DependencyProperty.UnsetValue && localEntry is IContextData dpCtx) {
  164.                     ctx.Merge(dpCtx);
  165.                 }
  166.             }
  167.  
  168.             return ctx;
  169.         }
  170.  
  171.         // private static ContextData GetInheritedContextFromParents(DependencyObject target) {
  172.         //     for (DependencyObject parent = VisualTreeUtils.GetParent(target); parent != null; parent = VisualTreeUtils.GetParent(parent)) {
  173.         //         if (target.GetValue(InheritedContextDataProperty) is ContextData parentData) {
  174.         //             return parentData;
  175.         //         }
  176.         //     }
  177.         //     return null;
  178.         // }
  179.         // private static void UpdateInheritedContextOnContextChanged(DependencyObject target, ContextData newData) {
  180.         //     ContextData parentData = GetInheritedContextFromParents(target);
  181.         //     if (newData == null) {
  182.         //         newData = parentData;
  183.         //     }
  184.         //     else if (parentData != null) {
  185.         //         newData = ContextData.Merge(parentData, newData);
  186.         //     }
  187.         //     target.SetValue(InheritedContextDataPropertyKey, newData);
  188.         //     UpdateChildrenInheritedContext(target, newData);
  189.         // }
  190.         // private static void UpdateChildrenInheritedContext(DependencyObject parent, ContextData parentData) {
  191.         //     int count = VisualTreeHelper.GetChildrenCount(parent);
  192.         //     for (int i = 0; i < count; i++) {
  193.         //         DependencyObject child = VisualTreeHelper.GetChild(parent, i);
  194.         //         ContextData childData = child.GetValue(ContextDataProperty) as ContextData;
  195.         //         if (childData == null) {
  196.         //             childData = parentData;
  197.         //         }
  198.         //         else if (parentData != null) {
  199.         //             childData = ContextData.Merge(parentData, childData);
  200.         //         }
  201.         //         child.SetValue(InheritedContextDataPropertyKey, childData);
  202.         //         UpdateChildrenInheritedContext(child, childData);
  203.         //     }
  204.         // }
  205.         public static void UpdateInheritedContext(DependencyObject element) {
  206.             element.CoerceValue(InheritedContextDataProperty);
  207.         }
  208.     }
  209. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement