1

我有一个附加属性来为我的 WPF 应用程序中的不同 uielements 提供上下文帮助。

附加属性设置为边框控件的子属性,该属性是用于提供上下文相关帮助的弹出控件的子属性。

问题是颜色前景颜色默认为随机事物,具体取决于附加属性所附加的 UIEelment。

猜测我认为这是因为内容属性正在使用附加控件的可视化树。

如何强制内容属性重新评估默认属性前景、字体等?

这是基于这篇文章: http: //www.codeproject.com/Articles/59535/A-Simple-Integrated-WPF-Help-System

最终可能是我派生的弹出类需要修复。我一直在寻找一种更好的方法来实现我的弹出窗口,但还没有找到。

所有有趣的代码都在下面找到。从弹出类开始。

感谢您的任何帮助。

public class HelpPopup : Popup
{
    private readonly ContentPresenter mContentPresenter = new ContentPresenter();
    private readonly Border mBorder = new Border();
    private readonly ContentControl mControl = new ContentControl();

    public HelpPopup()
    {
        mBorder.Child = mContentPresenter;
        mControl.Content = mBorder;
        Child = mControl;

        // no background for content control...
        mControl.Background = null;
        mControl.Foreground = SystemColors.ControlTextBrush;

        AllowsTransparency = true;
    }

    #region Content Dependency Property

    public static readonly DependencyProperty ContentProperty = ContentControl.ContentProperty.AddOwner(typeof (HelpPopup), new PropertyMetadata(default(object), ContentChanged));

    private static void ContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            // simply update content to match that what's being set..
            src.mContentPresenter.Content = e.NewValue;
        }
    }

    public object Content
    {
        get
        {
            object res = default(object);

            if (CheckAccess() != false)
            {
                res = (object) GetValue(ContentProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (object) GetValue(ContentProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(ContentProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(ContentProperty, value));
            }
        }
    }

    #endregion

    #region Background Dependency Property

    public static readonly DependencyProperty BackgroundProperty = 
                    Border.BackgroundProperty.AddOwner(typeof (HelpPopup), new PropertyMetadata(default(Brush), BackgroundChanged));

    private static void BackgroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mBorder.Background = e.NewValue as Brush;
        }
    }

    public Brush Background
    {
        get
        {
            Brush res = default(Brush);

            if (CheckAccess() != false)
            {
                res = (Brush) GetValue(BackgroundProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (Brush) GetValue(BackgroundProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(BackgroundProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(BackgroundProperty, value));
            }
        }
    }

    #endregion

    #region Foreground Dependency Property

    public static readonly DependencyProperty ForegroundProperty = Control.ForegroundProperty.AddOwner(
        typeof (HelpPopup), new FrameworkPropertyMetadata(Brushes.Black, ForegroundChanged));

    private static void ForegroundChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mControl.Foreground = e.NewValue as Brush;
        }
    }

    public Brush Foreground
    {
        get
        {
            Brush res = default(Brush);

            if (CheckAccess() != false)
            {
                res = (Brush) GetValue(ForegroundProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (Brush) GetValue(ForegroundProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(ForegroundProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(ForegroundProperty, value));
            }
        }
    }

    #endregion

    #region BorderBrush Dependency Property

    public static readonly DependencyProperty BorderBrushProperty = Border.BorderBrushProperty.AddOwner(
        typeof (HelpPopup),
        new PropertyMetadata(default(Brush), BorderBrushChanged));

    private static void BorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mBorder.BorderBrush = e.NewValue as Brush;
        }
    }

    public Brush BorderBrush
    {
        get
        {
            Brush res = default(Brush);

            if (CheckAccess() != false)
            {
                res = (Brush) GetValue(BorderBrushProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (Brush) GetValue(BorderBrushProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(BorderBrushProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(BorderBrushProperty, value));
            }
        }
    }

    #endregion

    #region BorderThickness Dependency Property

    public static readonly DependencyProperty BorderThicknessProperty = Border.BorderThicknessProperty.AddOwner(
        typeof (HelpPopup),
        new PropertyMetadata(default(Thickness), BorderThicknessChanged));

    private static void BorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mBorder.BorderThickness = (Thickness) e.NewValue;
        }
    }

    public Thickness BorderThickness
    {
        get
        {
            Thickness res = default(Thickness);

            if (CheckAccess() != false)
            {
                res = (Thickness) GetValue(BorderThicknessProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (Thickness) GetValue(BorderThicknessProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(BorderThicknessProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(BorderThicknessProperty, value));
            }
        }
    }

    #endregion

    #region CornerRadius Dependency Property

    public static readonly DependencyProperty CornerRadiusProperty = Border.CornerRadiusProperty.AddOwner(
        typeof (HelpPopup),
        new PropertyMetadata(default(CornerRadius), CornerRadiusChanged));

    private static void CornerRadiusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mBorder.CornerRadius = (CornerRadius) e.NewValue;
        }
    }

    public CornerRadius CornerRadius
    {
        get
        {
            CornerRadius res = default(CornerRadius);

            if (CheckAccess() != false)
            {
                res = (CornerRadius) GetValue(CornerRadiusProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (CornerRadius) GetValue(CornerRadiusProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(CornerRadiusProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(CornerRadiusProperty, value));
            }
        }
    }

    #endregion

    #region Padding Dependency Property

    public static readonly DependencyProperty PaddingProperty = Border.PaddingProperty.AddOwner(
        typeof (HelpPopup),
        new PropertyMetadata(default(Thickness), PaddingChanged));

    private static void PaddingChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpPopup src = obj as HelpPopup;

        if (src != null)
        {
            src.mBorder.Padding = (Thickness) e.NewValue;
        }
    }

    public Thickness Padding
    {
        get
        {
            Thickness res = default(Thickness);

            if (CheckAccess() != false)
            {
                res = (Thickness) GetValue(PaddingProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (Thickness) GetValue(PaddingProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(PaddingProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(PaddingProperty, value));
            }
        }
    }

    #endregion

    static HelpPopup()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(HelpPopup), new FrameworkPropertyMetadata(typeof(HelpPopup)));
    }

附加属性在这里定义:

class Help
{
    struct HelpActiveInfo
    {
        private EventHandler Handler { get; set; }
        private UIElement Element { get; set; }
        private DependencyPropertyDescriptor Descriptor { get; set; }

        public HelpActiveInfo(UIElement element, DependencyProperty property, EventHandler handler) : this()
        {
            if (element == null)
                throw new InvalidArgumentValue("element", "element cannot be null");

            Element = element;

            if (handler == null)
                throw new InvalidArgumentValue("handler", "handler cannot be null");

            Handler = handler;

            if (property == null)
                throw new InvalidArgumentValue("property", "property cannot be null");

            Descriptor = DependencyPropertyDescriptor.FromProperty(property, typeof(UIElement));
        }

        public void AddHandler()
        {
            if (Handler != null)
            {
                if (Descriptor != null)
                {
                    if (Element != null)
                        Descriptor.AddValueChanged(Element, Handler);
                }
            }
        }

        public void RemoveHandler()
        {
            if (Handler != null)
            {
                if (Descriptor != null)
                {
                    if (Element != null)
                        Descriptor.RemoveValueChanged(Element, Handler);
                }
            }
        }
    }

    private readonly static Dictionary<UIElement, HelpActiveInfo> smChangeMap = new Dictionary<UIElement, HelpActiveInfo>(); 

    #region Help Attached Property

    public static readonly DependencyProperty HelpProperty = DependencyProperty.RegisterAttached("Help", typeof(UIElement), typeof(Help), new PropertyMetadata(default(UIElement), HelpChanged));

    private static void HelpChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        UIElement ui = obj as UIElement;

        if (ui != null)
        {
            HelpActiveInfo hi;
            HelpWindow h = GetParentWindow(ui) as HelpWindow;

            if (smChangeMap.TryGetValue(ui, out hi) != false)
            {
                hi.RemoveHandler();
                smChangeMap.Remove(ui);
            }

            // if have help window, and the new help is not null
            // then monitor help active property to decide when to 
            // add adorner to the element....
            if ((h != null) && (e.NewValue != null))
            {
                hi = new HelpActiveInfo(h, HelpWindow.HelpActiveProperty,
                    (sender, args) =>
                    {
                        AdornerLayer al = AdornerLayer.GetAdornerLayer(ui);

                        if (al != null)
                        {
                            Adorner[] adorners = al.GetAdorners(ui);

                            // remove and existing help adorners
                            if (adorners != null)
                            {
                                List<Adorner> rl = adorners.Where(a => a.GetType() == typeof (HelpAdorner)).ToList();

                                foreach (var r in rl)
                                    al.Remove(r);
                            }

                            // no adorners, add new one if needed
                            if (h.HelpActive != false)
                            {
                                HelpAdorner ha = new HelpAdorner(ui);

                                al.Add(ha);
                            }
                        }
                    });

                smChangeMap[ui] = hi;
                smChangeMap[ui].AddHandler();
            }
        }
    }

    public static void SetHelp(DependencyObject element, UIElement value)
    {
        element.SetValue(HelpProperty, value);
    }

    public static UIElement GetHelp(DependencyObject element)
    {
        return (UIElement)element.GetValue(HelpProperty);
    }
    #endregion

    #region Glow Brush Attached Property
    public static readonly DependencyProperty GlowBrushProperty = DependencyProperty.RegisterAttached(
        "GlowBrush", typeof (Brush), typeof (Help), new PropertyMetadata(new SolidColorBrush(Colors.Red) { Opacity = 0.3 }));

    public static void SetGlowBrush(DependencyObject element, Brush value)
    {
        element.SetValue(GlowBrushProperty, value);
    }

    public static Brush GetGlowBrush(DependencyObject element)
    {
        return (Brush) element.GetValue(GlowBrushProperty);
    }
    #endregion

    #region helpers
    public static Window GetParentWindow(DependencyObject child)
    {
        Window res = null;

        if (child != null)
        {
            DependencyObject parentObject = VisualTreeHelper.GetParent(child);

            res = parentObject as Window ?? GetParentWindow(parentObject);
        }

        return res;
    }
    #endregion
}

帮助窗口类管理各个帮助项:

public class HelpWindow : Window
{
    private DependencyObject CurrentHelpObject { get; set; }
    private HelpPopup CurrentHelpPopup { get; set; }

    public HelpWindow()
    {
        CommandBindings.Add(new CommandBinding(ApplicationCommands.Help,
            (x, y) =>
            {
                HelpActive = (HelpActive == false);
            },
            (x, y) =>
            {
                y.CanExecute = true;
            }));

        Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        InitHelpSystem(this);
    }

    private void InitHelpSystem(DependencyObject obj)
    {
        // Continue recursive toggle. Using the VisualTreeHelper works nicely.
        for (int x = 0; x < VisualTreeHelper.GetChildrenCount(obj); x++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, x);
            InitHelpSystem(child);
        }

        UIElement help = obj.GetValue(Help.HelpProperty) as UIElement;

        if (help != null)
        {
            // force a reset of the property because window is up and running
            obj.SetValue(Help.HelpProperty, null);
            obj.SetValue(Help.HelpProperty, help);
        }
    }

    // Return the result of the hit test to the callback.
    private readonly Stack<DependencyObject> mHitTestCollection = new Stack<DependencyObject>();

    public HitTestResultBehavior HitTestResult(HitTestResult result)
    {
        DependencyObject d = result.VisualHit;

        if (d != null)
            mHitTestCollection.Push(d);

        // Set the behavior to return visuals at all z-order levels. 
        return HitTestResultBehavior.Continue;
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        object content = null;
        DependencyObject checkHelpObject = null;
        List<UIElement> dl = new List<UIElement>();

        mHitTestCollection.Clear();

        // You can check the HelpActive property if desired, however 
        // the listener should not be hooked up so this should not be firing
        VisualTreeHelper.HitTest(sender as Visual, null, new HitTestResultCallback(HitTestResult), new PointHitTestParameters(e.GetPosition(this)));

        // walk the list find the objects (or the parents that have 
        while (mHitTestCollection.Count > 0)
        {
            DependencyObject d = mHitTestCollection.Pop();

            if (d != null)
            {
                object c = null;
                UIElement oc = null;

                // find the content property for this one...
                while ((c == null) && (Equals(d, this) == false))
                {
                    oc = d as UIElement;

                    if (oc != null)
                        c = oc.GetValue(Help.HelpProperty) as DependencyObject;

                    d = VisualTreeHelper.GetParent(d);
                }

                if (c != null)
                {
                    // remove the containing control if it already is n the list
                    if (dl.Contains(oc) != false)
                        dl.Remove(oc);

                    // then add it to the list
                    // this ensures that top of the list is always the lowest seen 
                    // object that implements the attached property
                    dl.Add(oc);
                }
            }
        }

        // go get the object to use
        checkHelpObject = dl.FirstOrDefault();

        // fetch content
        content = (checkHelpObject != null) ? checkHelpObject.GetValue(Help.HelpProperty) : null;

        // and process fetched content...
        if ((content == null) && (CurrentHelpPopup != null))
        {
            CurrentHelpPopup.IsOpen = false;
            CurrentHelpPopup = null;
            CurrentHelpObject = null;
        }
        else
        {
            if ((content != null) && (Equals(CurrentHelpObject, checkHelpObject) == false))
            {
                CurrentHelpObject = checkHelpObject;

                // New visual "stack" hit, close old popup, if any
                if (CurrentHelpPopup != null)
                    CurrentHelpPopup.IsOpen = false;

                CurrentHelpPopup = new HelpPopup()
                                   {
                                       IsOpen = true,
                                       Content = content,
                                       AllowsTransparency = true,
                                       PopupAnimation = PopupAnimation.Fade,
                                       PlacementTarget = (UIElement)checkHelpObject
                                   };
            }
        }
    }

    #region HelpActive Dependency Property

    public static readonly DependencyProperty HelpActiveProperty =
        DependencyProperty.Register("HelpActive", typeof (bool), typeof (HelpWindow),
            new PropertyMetadata(default(bool), HelpActiveChanged));

    private static void HelpActiveChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        HelpWindow src = obj as HelpWindow;

        if (src != null)
        {
            if (src.CurrentHelpPopup != null)
                src.CurrentHelpPopup.IsOpen = false;

            src.CurrentHelpObject = null;
            src.CurrentHelpPopup = null;

            // enable/disable mouse move handlers...
            if (((bool) e.OldValue) != false)
                src.MouseMove -= src.OnMouseMove;

            if (((bool)e.NewValue) != false)
                src.MouseMove += src.OnMouseMove;
        }
    }

    public bool HelpActive
    {
        get
        {
            bool res = default(bool);

            if (CheckAccess() != false)
            {
                res = (bool) GetValue(HelpActiveProperty);
            }
            else
            {
                Dispatcher.Invoke(() => res = (bool) GetValue(HelpActiveProperty));
            }

            return res;
        }
        set
        {
            if (CheckAccess() != false)
            {
                SetValue(HelpActiveProperty, value);
            }
            else
            {
                Dispatcher.Invoke(() => SetValue(HelpActiveProperty, value));
            }
        }
    }

    #endregion
}
4

0 回答 0