2

Ok, so I'm trying to figure out how to set a status bars label text to show information about the current control that a mouse is hovering over. I have seen this numerous times on many programs so I know it can be done and I'm sure there are explanations out there that could help me but I can't seem to find the right words to search for the answer unfortunately... The closest thing I could find was in the link below. I tried to utilize this but it gave me an error when I tried to set the text property.

Anyone have some information or a link to help me by chance? Thanks, Ryan

Display text in a label when hovering over a control without using events

My XAML Code:

<StatusBar>
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition Width="75" />
                        </Grid.ColumnDefinitions>

                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </StatusBar.ItemsPanel>
            <StatusBarItem Grid.Column="0">
                <Label Content="New Lead Inquiry" />
            </StatusBarItem>
            <Separator Grid.Column="1" Style="{StaticResource StylingStatusBarSeparator}" />
            <StatusBarItem Grid.Column="2">
                <Label x:Name="infoStatusBar" Content="Label for text about the currently hovered item" />
            </StatusBarItem>
            <Separator Grid.Column="3" Style="{StaticResource StylingStatusBarSeparator}" />
            <StatusBarItem Grid.Column="4">
                <Label Content="Not Saved" />
            </StatusBarItem>
        </StatusBar>
  • *"it gave me an error"* -- Did you happen to notice what the error was? – 15ee8f99-57ff-4f92-890c-b56153 Sep 01 '17 at 16:40
  • It doesn't give me an error when I run the program. Is it possibly because I have the style property set up to a StaticResource setup and it doesn't have that Resource when you run it? – Ryan Morris Sep 01 '17 at 16:50
  • When did it give you the error? – 15ee8f99-57ff-4f92-890c-b56153 Sep 01 '17 at 16:52
  • There's no error for me it just doesn't change the text on the statusbaritem label to the tooltip from a control on the window when I hover over it. – Ryan Morris Sep 01 '17 at 16:54
  • Anyway. The other question you vandalized with a non-answer doesn't appear to be a solution to your problem anyway. You can probably do this in code behind, but not in pure XAML. – 15ee8f99-57ff-4f92-890c-b56153 Sep 01 '17 at 16:55
  • 2
    Are you using MVVM, or are you using code behind? In either event, I think you could use the MouseEnter and MouseLeave events on the controls to set or clear the statusbar text. I can offer sample code if you give me a hint about how you are structuring your project. – Dave Smash Sep 01 '17 at 16:56
  • When you write "it gave me an error", folks can be forgiven, I hope, for assuming that you meant to say that it gave you an error. But I'm glad we've got that misunderstanding cleared up. – 15ee8f99-57ff-4f92-890c-b56153 Sep 01 '17 at 16:57
  • Lol well... I'm sure trying to use MVVM. I'm still wrapping my head around things so I do have things setup to use MVVM right now. – Ryan Morris Sep 01 '17 at 16:57
  • 1
    OH!!! Sorry, I misunderstood what you meant when you said it gave me an error. When I tried to use the code that was supplied in the link about " it says that the property "Text" is not recognized or not accessible. – Ryan Morris Sep 01 '17 at 16:59

2 Answers2

1

Here's a solution that doesn't require you to modify each child control or use any frameworks.

This isn't really related to MVVM, since it's pure UI stuff. There's nothing here that would involve a viewmodel.

Handle Window.PreviewMouseMove:

MainWindow.xaml

<Window 
    ...
    PreviewMouseMove="Window_PreviewMouseMove"
    >

MainWindow.xaml.cs

Define a dependency property of type Object, and in the preview mousemove handler, give it the nearest parent tooltip of the control the mouse is over:

    private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        var element = Mouse.DirectlyOver as FrameworkElement;

        HoverToolTip = GetTooltip(element);  
    }

    #region HoverToolTip Property
    public object HoverToolTip
    {
        get { return (object)GetValue(HoverToolTipProperty); }
        set { SetValue(HoverToolTipProperty, value); }
    }

    public static readonly DependencyProperty HoverToolTipProperty =
        DependencyProperty.Register(nameof(HoverToolTip), typeof(object), typeof(MainWindow),
            new PropertyMetadata(null));
    #endregion HoverToolTip Property

    protected static Object GetTooltip(FrameworkElement obj)
    {
        if (obj == null)
        {
            return null;
        }
        else if (obj.ToolTip != null)
        {
            return obj.ToolTip;
        }
        else
        {
            return GetTooltip(VisualTreeHelper.GetParent(obj) as FrameworkElement);
        }
    }

And bind that to whatever in the XAML.

    <Label
        x:Name="StatusBar"
        Content="{Binding HoverToolTip, RelativeSource={RelativeSource AncestorType=Window}}"
        Grid.Row="2"
        />

That Label is just the quickie I put in my test XAML. This binding is the important part there:

{Binding HoverToolTip, RelativeSource={RelativeSource AncestorType=Window}}
0

You can wire the MouseEnter and MouseLeave commands on your controls to set a HelpText property in your viewmodel, and then bind the status bar label to HelpText so that when it is set to something new, the new value appears in the status bar.

This answer uses the MVVM Light toolkit, but should be adaptable for any MVVM setup:

In XAML:

<Window x:Class="MvvmLightPlayground.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ignore="http://www.galasoft.ch/ignore"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d ignore"
        Height="300"
        Width="600"
        Title="MVVM Light Application">


    <Window.DataContext>
        <Binding Path="Main" Source="{StaticResource Locator}" />
    </Window.DataContext>

    <StackPanel>
        <Label Content="This is Label1" x:Name="Label1">
            <!-- Triggers like this are easy to wire up using blend.  If you do it manually, add the i: definition to your window tag as shown above -->
            <i:Interaction.Triggers>
                <i:EventTrigger SourceName="Label1" EventName="MouseEnter">
                    <i:InvokeCommandAction Command="{Binding MouseEnter}" CommandParameter="This is Label1.  Look how neat it is!" />
                </i:EventTrigger>
                <i:EventTrigger SourceName="Label1" EventName="MouseLeave">
                    <i:InvokeCommandAction Command="{Binding MouseLeave}" CommandParameter="This is Label1.  Look how neat it is!" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Label>
        <Label Content="This is Label2" x:Name="Label2">
            <i:Interaction.Triggers>
                <i:EventTrigger SourceName="Label2" EventName="MouseEnter">
                    <i:InvokeCommandAction Command="{Binding MouseEnter}" CommandParameter="This is Label2.  It's a different label." />
                </i:EventTrigger>
                <i:EventTrigger SourceName="Label2" EventName="MouseLeave">
                    <i:InvokeCommandAction Command="{Binding MouseLeave}" CommandParameter="This is Label2.  It's a different label." />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Label>

        <StatusBar>
            <StatusBar.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </StatusBar.ItemsPanel>
            <StatusBarItem>
                <Label Content="{Binding HelpText, Mode=OneWay}" /> <!-- Bind to your HelpText property in the VM -->
            </StatusBarItem>
        </StatusBar>
    </StackPanel>
</Window>

In your viewmodel:

First, add properties for your HelpText and your ICommands:

private string _helpText = "Testing";
public string HelpText
{
    get
    {
        return _helpText;
    }
    set
    {
        Set(() => HelpText, ref _helpText, value);
    }
}

private ICommand _mouseEnter;
public ICommand MouseEnter
{
    get
    {
        return _mouseEnter;
    }
    set
    {
        Set(() => MouseEnter, ref _mouseEnter, value);
    }
}

private ICommand _mouseLeave;
public ICommand MouseLeave
{
    get
    {
        return _mouseLeave;
    }
    set
    {
        Set(() => MouseLeave, ref _mouseLeave, value);
    }
}

Then initialize your ICommands in your viewmodel constructor to point at methods in the viewmodel:

public MainViewModel()
{
    MouseEnter = new RelayCommand<string>(SetHelpText);
    MouseLeave = new RelayCommand<string>(ClearHelpText);
}

Then create your helper methods to set the HelpText property:

public void SetHelpText(string helpText)
{
    HelpText = helpText;
}

public void ClearHelpText(string textToClear)
{
    // check to see whether it has already been set to something else by another MouseEnter event...
    if (HelpText == textToClear)
    {
        HelpText = "";
    }
}

This is the running sample shown with the mouse hovered over Label2:

Screenshot

Dave Smash
  • 2,941
  • 1
  • 18
  • 38
  • Oh man... That's a lot of stuff that I'm not familiar with at all BUT I will work my way through it and see if I can get it to work. Thank you for your help. I will update the status afterward. :) – Ryan Morris Sep 01 '17 at 17:42
  • Get the MVVMLight Toolkit from http://www.mvvmlight.net/ (or possibly NuGet) if you want to do it this way. It is well documented, and the creator did an excellent Pluralsight course (which I used a free trial to view) that got me up to speed. This is all possible without that toolkit, but it would have to be modified - basically the parts of this that are dependent on that are the `Set(() => HelpText, ref _helpText, value)` setters (which automatically implement INotifyPropertyChanged for you) and the RelayCommands, which make it easier to wire up command bindings to viewmodel code. – Dave Smash Sep 01 '17 at 17:48
  • Lol well, I sure gave it my best! I am running into an issue with my StatusBarHelpText.cs file for my ViewModel. The section where you wanted me to put Set(() => MouseEnter, ref _mouseEnter, value); it says that name 'Set' does not exist in the current context. Also, the MainView() section says that it needs a return type and then RelayCommand asks if I'm missing a using directive or assembly reference. I'm sure this is just something that I'm supposed to add but I'm not really sure what that is... Here's a link to the GitHub for the files. https://github.com/G33kman/RI – Ryan Morris Sep 01 '17 at 18:21
  • Lol!!! Ok so I'm assuming that was my issue right there... I will look into the mvvmlight light. – Ryan Morris Sep 01 '17 at 18:22
  • Yes, Set is part of the MVVMLight toolkit, not part of .Net. If you don't use it, just make sure that your setter is calling INotifyPropertyChanged, or your changes won't show up in the UI. MainViewModel() is a constructor - make sure that its name matches the class name so that the compiler knows that it's a constructor. If your class is called StatusBarHelpText and your method is called MainView(), then it will expect a return type because those names don't match. – Dave Smash Sep 01 '17 at 18:28
  • If MVVM is brand new to you, I would encourage you to learn it, but this would be quicker to accomplish by adding code to the codebehind. Just wire up MouseEnter and MouseLeave event handlers for your controls, and in those methods, set the text in the status bar. Since it's all being done in the same file, there is much less abstraction to worry about... – Dave Smash Sep 01 '17 at 18:31
  • Thank you for your help with this! I sure am trying to learn how MVVM works since I know that's kind of how things work now but learning that and C# and WPF and whatever else that includes is quite the task so I'm just doing what I can to figure it out. – Ryan Morris Sep 01 '17 at 18:45
  • Alright, I got a solution that works for me but I will definitely be looking into the MVVM stuff and trying to figure that out. Thank you for your help with this I really appreciate it. :) – Ryan Morris Sep 01 '17 at 19:12