我有一个附加属性,可以在我的WPF应用程序中为我不同的元素提供上下文帮助。
附加属性设置为边框控件的子属性,该边框控件是弹出控件的子属性,用于提供上下文相关帮助。
问题是颜色前景色默认为随机内容,具体取决于附加属性所附加的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
}