dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.01k stars 1.16k forks source link

WPF FrameworkElement control - Add new DisabledTooltip and EnabledTooltip properties #9410

Open vsfeedback opened 1 month ago

vsfeedback commented 1 month ago

This issue has been moved from a ticket on Developer Community.


WPF has a FrameworkElement control. Button is one of the controls that inherits from it, but I think it would be better to have this change on the FrameworkElement parent. Requested New Properties - Would love to have DisabledTooltip and EnabledTooltip properties added. If Tooltip is blank then use DisabledTooltip or EnabledTooltip depending on the current state. I would suggest protecting against showing hidden or collapsed buttons. Reason: Often I need the ToolTip to show reasons why the button is disabled or tell the user how to get the data in a state for this to become enabled. Often I need ToolTip to tell the user what the enabled button does which is often a different message.
Impact - None, since we are adding 2 new properties and not changing the existing interface of the controls.


Original Comments

Feedback Bot on 8/16/2022, 02:46 PM:

(private comment, text removed)

miloush commented 1 month ago

Use ToolTipService.ShowOnDisabled="True" and trigger on IsEnabled to set alternative tooltip.

hongruiyu commented 1 month ago

You could also use the converter to achieve the effect you want.

For ToolTip with text, you could refer to the second button and directly write the content of the ToolTip text.

For ToolTip without text, you could refer to the first button, and use "|" to separate the two parameters in ConverterParameter, and then separate them with (parameter as string).Split('|') in the converter.

<Grid>
    <Grid.Resources>
        <local:DataConvert x:Key="MyConvert"></local:DataConvert>
    </Grid.Resources>
    <StackPanel>
        <CheckBox x:Name="MyCheckbox" Content="Is Enabled?" IsChecked="True"/>
        <Button Content="OK" IsEnabled="{Binding ElementName=MyCheckbox,Path=IsChecked}"
            ToolTip="{Binding Path=IsEnabled,RelativeSource={RelativeSource Self},Converter={StaticResource MyConvert},ConverterParameter=AAA|BBB}"
            ToolTipService.ShowOnDisabled="True"/>
    <Button Content="NO" IsEnabled="{Binding ElementName=MyCheckbox,Path=IsChecked}"
        ToolTip="HAHA"
        ToolTipService.ShowOnDisabled="True"/>
    </StackPanel>
</Grid>
    [ValueConversion(typeof(bool), typeof(string))]
    internal class DataConvert : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var Result = (parameter as string).Split('|');
            if ((bool)value)
            {
                return Result[0].ToString();
            }
            else
            {
                return Result[1].ToString();
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
LiuHuiii commented 1 month ago

To achieve this behavior in WPF, where you want to enhance FrameworkElement controls (like Button) with DisabledTooltip and EnabledTooltip properties, you can create a custom attached behavior or a custom control that extends the existing behavior of Button. The ToolTipService.ShowOnDisabled can be used to allow the ToolTip to show when the button is disabled (as normally it would not).

<Window.Resources>
    <Style x:Key="DynamicToolTipButtonStyle" TargetType="Button">
        <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(local:ToolTipBehavior.EnabledTooltip)}"/>
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(local:ToolTipBehavior.DisabledTooltip)}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
<StackPanel>
    <Button Content="Click Me"
            Width="100" x:Name="MyButton"
            Height="30"
            Margin="50"  ToolTipService.ShowOnDisabled="True"
            IsEnabled="False"
            Style="{StaticResource DynamicToolTipButtonStyle}"
            local:ToolTipBehavior.DisabledTooltip="Button is disabled due to some reason"
            local:ToolTipBehavior.EnabledTooltip="Click to perform action"/>

    <Button x:Name="btn" Content="Click Me" Click="Button_Click"  Width="200" Height=" 60" IsEnabled="True"  ToolTipService.ShowOnDisabled="True"  local:ToolTipBehavior.DisabledTooltip="Button is disabled due to ..."  local:ToolTipBehavior.EnabledTooltip="Click to perform action"   />

</StackPanel>

Codebedhind:

  public partial class MainWindow : Window
  {

      public MainWindow()
      {
          InitializeComponent();

      }

      private void Button_Click(object sender, RoutedEventArgs e)
      {
          btn.IsEnabled = false;
      }

  }

  public static class ToolTipBehavior
  {
      public static readonly DependencyProperty DisabledTooltipProperty =
          DependencyProperty.RegisterAttached("DisabledTooltip", typeof(string), typeof(ToolTipBehavior),
              new PropertyMetadata(null, OnToolTipChanged));

      public static readonly DependencyProperty EnabledTooltipProperty =
          DependencyProperty.RegisterAttached("EnabledTooltip", typeof(string), typeof(ToolTipBehavior),
              new PropertyMetadata(null, OnToolTipChanged));

      public static string GetDisabledTooltip(FrameworkElement element)
      {
          return (string)element.GetValue(DisabledTooltipProperty);
      }

      public static void SetDisabledTooltip(FrameworkElement element, string value)
      {
          element.SetValue(DisabledTooltipProperty, value);
      }

      public static string GetEnabledTooltip(FrameworkElement element)
      {
          return (string)element.GetValue(EnabledTooltipProperty);
      }

      public static void SetEnabledTooltip(FrameworkElement element, string value)
      {
          element.SetValue(EnabledTooltipProperty, value);
      }

      private static void OnToolTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
      {
          if (d is FrameworkElement element)
          {
              element.IsEnabledChanged += Element_IsEnabledChanged;
              UpdateToolTip(element);
          }
      }

      private static void Element_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
      {
          if (sender is FrameworkElement element)
          {
              UpdateToolTip(element);
          }
      }

      internal static void UpdateToolTip(FrameworkElement element)
      {
          if (!element.IsEnabled)
          {
              string disabledTooltip = GetDisabledTooltip(element);
              if (!string.IsNullOrEmpty(disabledTooltip))
              {
                  ToolTipService.SetToolTip(element, disabledTooltip);
              }
          }
          else
          {
              string enabledTooltip = GetEnabledTooltip(element);
              if (!string.IsNullOrEmpty(enabledTooltip))
              {
                  ToolTipService.SetToolTip(element, enabledTooltip);
              }
          }
      }
  }