Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // Copyright (c) 2023-2024 REghZy
- //
- // This file is part of FramePFX.
- //
- // FramePFX is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either
- // version 3.0 of the License, or (at your option) any later version.
- //
- // FramePFX is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- // Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with FramePFX. If not, see <https://www.gnu.org/licenses/>.
- //
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Windows;
- using System.Windows.Data;
- using FramePFX.Utils;
- namespace FramePFX.Interactivity.Contexts {
- /// <summary>
- /// A class that is used to extract contextual information from WPF components
- /// </summary>
- public static class DataManager {
- // public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (ButtonBase));
- /// <summary>
- /// The context data property, used to store contextual information relative to a specific dependency object
- /// </summary>
- public static readonly DependencyProperty ContextDataProperty = DependencyProperty.RegisterAttached("ContextData", typeof(ContextData), typeof(DataManager), new PropertyMetadata(null, OnDataContextChanged));
- private static readonly DependencyPropertyKey InheritedContextDataPropertyKey = DependencyProperty.RegisterAttachedReadOnly("InheritedContextData", typeof(ContextData), typeof(DataManager), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, OnInheritedDataContextChanged, OnCoerceInheritedContextData));
- private static readonly DependencyProperty IsUpdatingLocalInheritedContextProperty = DependencyProperty.RegisterAttached("IsUpdatingLocalInheritedContext", typeof(bool), typeof(DataManager), new PropertyMetadata(BoolBox.False));
- private static void SetIsUpdatingLocalInheritedContext(DependencyObject element, bool value) => element.SetValue(IsUpdatingLocalInheritedContextProperty, value.Box());
- private static bool GetIsUpdatingLocalInheritedContext(DependencyObject element) => (bool) element.GetValue(IsUpdatingLocalInheritedContextProperty);
- /// <summary>
- /// A property used to store the merged accumulation of all available context data from the visual tree for a specific element
- /// </summary>
- public static readonly DependencyProperty InheritedContextDataProperty = InheritedContextDataPropertyKey.DependencyProperty;
- public static IContextData GetInheritedContextData(DependencyObject element) {
- return (IContextData) element.GetValue(InheritedContextDataProperty);
- }
- private static void OnDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) {
- // UpdateInheritedContextOnContextChanged(d, e.NewValue as ContextData);
- // SetIsUpdatingLocalInheritedContext(element, true);
- if (e.NewValue == null) {
- element.ClearValue(InheritedContextDataPropertyKey);
- }
- else {
- ContextData data = (ContextData) e.NewValue;
- DependencyObject parent = VisualTreeUtils.GetParent(element);
- if (parent?.GetValue(InheritedContextDataProperty) is ContextData parentData) {
- data = ContextData.Merge(parentData, data);
- }
- element.SetValue(InheritedContextDataPropertyKey, data);
- }
- // SetIsUpdatingLocalInheritedContext(element, false);
- }
- private static object OnCoerceInheritedContextData(DependencyObject element, object value) {
- if (GetIsUpdatingLocalInheritedContext(element)) {
- return value;
- }
- CompositeCollection er;
- object localObject = element.ReadLocalValue(ContextDataProperty);
- if (localObject == DependencyProperty.UnsetValue || !(localObject is ContextData localData) || localData.Count < 1) {
- return value;
- }
- DependencyObject parent = VisualTreeUtils.GetParent(element);
- if (parent?.GetValue(InheritedContextDataProperty) is ContextData parentData) {
- value = value is ContextData valueCtx ? ContextData.Merge(parentData, valueCtx) : parentData;
- }
- return value;
- }
- private static void OnInheritedDataContextChanged(DependencyObject element, DependencyPropertyChangedEventArgs e) {
- if (GetIsUpdatingLocalInheritedContext(element)) {
- return;
- }
- Debug.WriteLine("ee");
- // ContextData parentData = GetInheritedContextFromParents(target);
- // if (newData == null) {
- // newData = parentData;
- // }
- // else if (parentData != null) {
- // newData = ContextData.Merge(parentData, newData);
- // }
- // target.SetValue(InheritedContextDataPropertyKey, newData);
- // UpdateChildrenInheritedContext(target, newData);
- }
- /// <summary>
- /// Sets or replaces the context data for the specific dependency object
- /// </summary>
- public static void SetContextData(DependencyObject element, ContextData value) {
- element.SetValue(ContextDataProperty, value);
- }
- /// <summary>
- /// Gets the context data for the specific dependency object
- /// </summary>
- public static ContextData GetContextData(DependencyObject element) {
- return (ContextData) element.GetValue(ContextDataProperty);
- }
- /// <summary>
- /// Clears the <see cref="ContextDataProperty"/> value for the specific dependency object
- /// </summary>
- public static void ClearContextData(DependencyObject element) {
- element.ClearValue(ContextDataProperty);
- }
- // private static readonly PropertyInfo TreeLevelPropertyInfo = typeof(Visual).GetProperty("TreeLevel", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) ?? throw new Exception("Could not find TreeLevel property");
- /// <summary>
- /// Does bottom-to-top scan of the element's visual tree, and then accumulates and merged all of the data keys
- /// associated with each object from top to bottom, ensuring the bottom of the visual tree has the most power
- /// over the final data context key values
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- public static ContextData EvaluateContextData(DependencyObject obj) {
- ContextData ctx = new ContextData();
- // I thought about using TreeLevel, then thought reflection was too slow, so then I profiled the code...
- // This entire method (for a clip, 26 visual elements to the root) takes about 20 microseconds
- // Using the TreeLevel trick adds about 10 microseconds on top of it
- // int initialSize = 0;
- // if (obj is UIElement element && element.IsArrangeValid)
- // initialSize = (int) (uint) TreeLevelPropertyInfo.GetValue(element);
- // if (initialSize < 1)
- // initialSize = 32;
- // Accumulate visual tree bottom-to-top. Visual tree will contain the reverse tree
- List<DependencyObject> visualTree = new List<DependencyObject>(32);
- for (DependencyObject dp = obj; dp != null; dp = VisualTreeUtils.GetParent(dp)) {
- visualTree.Add(dp);
- }
- // Scan top-down in order to allow deeper objects' entries to override higher up entries
- for (int i = visualTree.Count - 1; i >= 0; i--) {
- DependencyObject dp = visualTree[i];
- object localEntry = dp.ReadLocalValue(ContextDataProperty);
- if (localEntry != DependencyProperty.UnsetValue && localEntry is IContextData dpCtx) {
- ctx.Merge(dpCtx);
- }
- }
- return ctx;
- }
- // private static ContextData GetInheritedContextFromParents(DependencyObject target) {
- // for (DependencyObject parent = VisualTreeUtils.GetParent(target); parent != null; parent = VisualTreeUtils.GetParent(parent)) {
- // if (target.GetValue(InheritedContextDataProperty) is ContextData parentData) {
- // return parentData;
- // }
- // }
- // return null;
- // }
- // private static void UpdateInheritedContextOnContextChanged(DependencyObject target, ContextData newData) {
- // ContextData parentData = GetInheritedContextFromParents(target);
- // if (newData == null) {
- // newData = parentData;
- // }
- // else if (parentData != null) {
- // newData = ContextData.Merge(parentData, newData);
- // }
- // target.SetValue(InheritedContextDataPropertyKey, newData);
- // UpdateChildrenInheritedContext(target, newData);
- // }
- // private static void UpdateChildrenInheritedContext(DependencyObject parent, ContextData parentData) {
- // int count = VisualTreeHelper.GetChildrenCount(parent);
- // for (int i = 0; i < count; i++) {
- // DependencyObject child = VisualTreeHelper.GetChild(parent, i);
- // ContextData childData = child.GetValue(ContextDataProperty) as ContextData;
- // if (childData == null) {
- // childData = parentData;
- // }
- // else if (parentData != null) {
- // childData = ContextData.Merge(parentData, childData);
- // }
- // child.SetValue(InheritedContextDataPropertyKey, childData);
- // UpdateChildrenInheritedContext(child, childData);
- // }
- // }
- public static void UpdateInheritedContext(DependencyObject element) {
- element.CoerceValue(InheritedContextDataProperty);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement