在WPF中固定/聚焦工具提示

时间:2016-09-26 12:47:47

标签: wpf

在Eclipse中,我可以通过在F2打开时按F2来聚焦工具提示。它转换为一个单独的弹出窗口,只要我想要保持打开状态,我就可以调整它。

如何让它在WPF中运行?

工具提示在5秒后关闭,这对大多数人来说都是有意义的。默认情况下,我没有工具提示永远保持打开状态,但有时我想阅读时间超过5秒。我喜欢按一个键让工具提示粘到GUI上的想法,直到我手动关闭它。

我希望使用ToolTip可以实现这一点,因为我不想用自定义的Popup行为替换GUI中的每个工具提示......

1 个答案:

答案 0 :(得分:1)

由于这给了我一个不眠之夜,我现在和永远地做了一个(或多或少)简单的通用方法来解决这个问题。

以下解决方案基于Style-Behaviors,它需要一些准备,因为没有任何内置。

让样式接受行为

 /// <summary>
  /// Collection for <see cref="StylizedBehaviors"/>
  /// </summary>
  public class StylizedBehaviorCollection : FreezableCollection<Behavior> {
    protected override Freezable CreateInstanceCore() {
      return new StylizedBehaviorCollection();
    }
  }

  /// <summary>
  /// This Behavior allows us to set Behaviors in Styles for a Generic approach
  /// </summary>
  public class StylizedBehaviors {
    private static readonly DependencyProperty OriginalBehaviorProperty = DependencyProperty.RegisterAttached(@"OriginalBehaviorInternal", typeof(Behavior), typeof(StylizedBehaviors), new UIPropertyMetadata(null));

    public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached(
        @"Behaviors",
        typeof(StylizedBehaviorCollection),
        typeof(StylizedBehaviors),
        new FrameworkPropertyMetadata(null, OnPropertyChanged));
    public static StylizedBehaviorCollection GetBehaviors(DependencyObject uie) {
      return (StylizedBehaviorCollection)uie.GetValue(BehaviorsProperty);
    }

    public static void SetBehaviors(DependencyObject uie, StylizedBehaviorCollection value) {
      uie.SetValue(BehaviorsProperty, value);
    }
    private static Behavior GetOriginalBehavior(DependencyObject obj) {
      return obj.GetValue(OriginalBehaviorProperty) as Behavior;
    }

    private static int GetIndexOf(BehaviorCollection itemBehaviors, Behavior behavior) {
      var index = -1;

      var orignalBehavior = GetOriginalBehavior(behavior);

      for (var i = 0; i < itemBehaviors.Count; i++) {
        var currentBehavior = itemBehaviors[i];
        if (currentBehavior == behavior || currentBehavior == orignalBehavior) {
          index = i;
          break;
        }
        var currentOrignalBehavior = GetOriginalBehavior(currentBehavior);
        if (currentOrignalBehavior != behavior && currentOrignalBehavior != orignalBehavior)
          continue;
        index = i;
        break;
      }
      return index;
    }

    private static void OnPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e) {
      var uie = dpo as UIElement;
      if (uie == null) {
        return;
      }
      var itemBehaviors = Interaction.GetBehaviors(uie);
      var newBehaviors = e.NewValue as StylizedBehaviorCollection;
      var oldBehaviors = e.OldValue as StylizedBehaviorCollection;
      if (newBehaviors == oldBehaviors) {
        return;
      }

      if (oldBehaviors != null) {
        foreach (var index in oldBehaviors.Select(behavior => GetIndexOf(itemBehaviors, behavior)).Where(index => index >= 0)) {
          itemBehaviors.RemoveAt(index);
        }
      }

      if (newBehaviors == null)
        return;
      foreach (var behavior in newBehaviors) {
        var index = GetIndexOf(itemBehaviors, behavior);

        if (index >= 0)
          continue;
        var clone = (Behavior)behavior.Clone();
        SetOriginalBehavior(clone, behavior);
        itemBehaviors.Add(clone);
      }
    }

    private static void SetOriginalBehavior(DependencyObject obj, Behavior value) {
      obj.SetValue(OriginalBehaviorProperty, value);
    }
  }

我们想要的热键行为

 public class ToolTipHotKeyBehavior : Behavior<Control> {

    public Key HotKey {
      get {
        return (Key)this.GetValue(HotKeyProperty);
      }
      set {
        this.SetValue(HotKeyProperty, value);
      }
    }

    public static readonly DependencyProperty HotKeyProperty =
        DependencyProperty.Register("HotKey", typeof(Key), typeof(ToolTipHotKeyBehavior), new PropertyMetadata(Key.F1));

    protected override void OnAttached() {
      this.AssociatedObject.Loaded += this.TargetLoaded;
      base.OnAttached();
    }

    private void TargetLoaded(object sender, RoutedEventArgs e) {
      var ctrl = (sender as Control);
      if (ctrl == null)
        return;
      ctrl.PreviewKeyDown += (o, args) => {
        if (args.Key != this.HotKey)
          return;
        if (ctrl.ToolTip.GetType() != typeof(ToolTip))
          ToolTipService.SetToolTip(ctrl, new ToolTip { Content = ctrl.ToolTip });

        (ctrl.ToolTip as ToolTip).IsOpen = !(ctrl.ToolTip as ToolTip).IsOpen;
      };

    }
  }

在XAML中使用示例

 <Window.Resources>
        <Style TargetType="{x:Type PasswordBox}">
            <Setter Property="Background" Value="Green"></Setter>
            <Setter Property="local:StylizedBehaviors.Behaviors">
                <Setter.Value>
                    <local:StylizedBehaviorCollection>
                        <local:ToolTipHotKeyBehavior />
                    </local:StylizedBehaviorCollection>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <PasswordBox Margin="10" VerticalAlignment="Center" ToolTip="Enter Password"/>
        <Button Content="hello" ToolTip="This is a Button">
            <Button.Style>
                <Style>
                    <Style.Setters>
                        <Setter Property="local:StylizedBehaviors.Behaviors">
                            <Setter.Value>
                                <local:StylizedBehaviorCollection>
                                    <local:ToolTipHotKeyBehavior HotKey="F3" />
                                </local:StylizedBehaviorCollection>
                            </Setter.Value>
                        </Setter>
                    </Style.Setters>
                </Style>
            </Button.Style>
        </Button>
    </StackPanel>

<强>封闭

不幸的是,这可能是执行此类操作的最简单和通用的方法。如您所见,行为可以应用于子类Control的所有内容 此外,您可以在每个控件/控件类型上设置不同的热键(如果需要,也可以绑定它)

注意

此示例基于以下事实:您使用字符串作为初始工具提示,因为行为将其转换为真实的工具提示以便能够打开/关闭它。

我希望这符合您的需求。干杯