10

I have two Windows for an application. One of them is MainWindow and the other is for settings. SettingsWindow opens when settings button is clicked by using ShowDialog and setting its Owner to MainWindow.

On the SettingsWindow I have a button at the very bottom of the window and it changes the color to red when IsMouseOver is True and blue for False. But it doesn't change when the cursor is over the MainWindow. The image is below to be clear. How can I fix this problem?

CASE: The cursor is out of SettingsWindow but it keeps the red color, no change.

enter image description here

Xaml code:

<Window x:Class="AltoSS.SettingsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SettingsWindow"
        Height="150"
        Width="360"
        WindowStyle="None"
        AllowsTransparency="True"
        WindowStartupLocation="CenterOwner">

  <!-- Other control codes-->
  <Button Grid.Row="2" Content="KAYDET" 
          FontSize="15"
          FontWeight="Bold"
          BorderBrush="Gray"
          BorderThickness="0,2,0,2">
    <Button.Style>
      <Style TargetType="Button">
        <Setter Property="Background" Value="Blue"/>
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="Button">
              <Border Background="{TemplateBinding Background}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Border>
              <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                  <Setter Property="Background" Value="Red"/>
                  <Setter Property="Foreground" Value="White"/>
                </Trigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </Button.Style>
  </Button>
</Window>
Ali Tor
  • 2,772
  • 2
  • 27
  • 58
  • how are you triggering? share your xaml – Henrique Forlani Aug 31 '16 at 20:36
  • Oh, sorry. I forgot it to add. I am gonna update the Q. – Ali Tor Aug 31 '16 at 20:37
  • @jstreet, I want to make a borderless window. If the problem is AllowTransparency, how can I get through it? – Ali Tor Sep 01 '16 at 00:46
  • That is not working. I also tried `ResizeMode="NoResize"` and `AllowTransparency="False"`, but it is still the same. `BorderThickness ` property doesn't effect when it is zero. `Resize` borders remains. I think the main problem is having a borderless window. No matter which values are used. – Ali Tor Sep 01 '16 at 01:03
  • But we have a problem there again. If `Window.Show` method is used, it works well. I am confused about it. An expert can figure out it, I think. – Ali Tor Sep 01 '16 at 01:51
  • I was able to reproduce it, but my guess is that you've found some sort of bug. When you use ShowDialog() but don't set the owner , and the dialog starts in a position outside the main window, it works. But when you set the owner or the dialog starts over the main window, it doesn't. I would guess that the IsMouseOver event gets lost due to the main window. I will do some more research ... – Henrique Forlani Sep 01 '16 at 12:45
  • The .Show is going to work, because is non-modal, which means that the parent window is not tied to its child. ShowDialog, however, displays the window modally, meaning that you can't go to the parent window. But you should use the ShowDialog to get the expected behavior that you want – Henrique Forlani Sep 01 '16 at 12:54
  • So does this mean it needs complex code to get a normally working trigger behavior using ShowDialog? – Ali Tor Sep 01 '16 at 13:29
  • or...we can workaround this by implementing the Show behavior as it was a ShowDialog. We can block the call to Show until the window is closed and return control to the main window...I think this would work and is quite simple. But we haven't addressed the original bug, what do you think? – Henrique Forlani Sep 01 '16 at 13:51
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/122435/discussion-between-ali-tor-and-forlani). – Ali Tor Sep 01 '16 at 15:38

3 Answers3

3

Alright, after doing some research, I couldn't find any logical reason for this to occur. It seems more like a bug to me. So if anyone knows exactly why this happens, let us know!

Anyway, I've come up with a workaround. Basically, we can use Show() and add some code to get closer to a modal behavior - like disabling the parent window until the dialog gets closed or the user has selected OK or Cancel for instance.

Example:

SettingsWindow settingsWindow = new SettingsWindow(); 
this.IsEnabled = false; //disables the main window 
settingsWindow.Owner = this; // main window is the settings window owner 
settingsWindow.Show(); 
settingsWindow.Closed += (o, e1) => { onWindowClosed(o,e1); }; // this is the close event

After subscribing for the settingsWindow closed event, we can now enable the parent window again when settingsWindow gets closed:

private void onWindowClosed(object sender, EventArgs e)
{
    this.IsEnabled = true;
}

Triggers will now work correctly and parent window gets disabled until its child is closed.

Henrique Forlani
  • 1,325
  • 9
  • 22
  • 1
    the problem is, that the dialog window is not part of the visual tree from main window. If you leave the window by moving the cursor, the property IsMouseOver and RoutetEvents (MouseLeave) aren't performed, because the mouse is not in window scope. To get the bahaviour you like to have you must integrate your dialog into MainWindow (as workaraound) and toggle Visibilty for example to show/hide your dialog. If your mouse then leaves the bounds of your button the IsMouseOver property is performed. – WPFGermany Sep 19 '16 at 07:51
  • I think you are right @Fabian , that is the reason for this to occur. It's a weird behavior though – Henrique Forlani Sep 24 '16 at 00:16
1

I think you have to observe the mouse position manually. For this you could use the code behind posted by Peheje here.

I used this to program a working example. While leaving your window, the Button gets the correct style.

using System.Runtime.InteropServices;
using Point = System.Drawing.Point;

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(ref Point lpPoint);

public bool IsMouseOverButton {
    get { return _isMouseOverButton; }
    set {
        if (value == _isMouseOverButton) return;
        _isMouseOverButton = value;
        OnPropertyChanged();
    }
}

public SettingsWindow()
{
    InitializeComponent();

    new Thread(() =>
    {
        while (true)
        {
            //Logic
            Point p = new Point();
            GetCursorPos(ref p);

            //Update UI
            Application.Current.Dispatcher.Invoke(() =>
            {
                double btnLeft = DlgWindow.Left;
                double btnRight = btnLeft + DlgBtn.ActualWidth;
                double btnBottom = DlgWindow.Top + DlgWindow.ActualHeight;
                double btnTop = btnBottom - DlgBtn.ActualHeight;

                IsMouseOverButton =
                    p.X >= btnLeft && p.X <= btnRight &&
                    p.Y >= btnTop && p.Y <= btnBottom;
            });

            //async wait (non blocking)
            (new ManualResetEvent(false)).WaitOne(100);
        }
    }).Start();
}

xaml

<Window x:Name="DlgWindow"
        DataContext="{Binding RelativeSource={RelativeSource Self}}" 
        AllowsTransparency="True">

  <Button x:Name="DlgBtn"
          Height="50"
          VerticalAlignment="Bottom"
          BorderBrush="Gray"
          BorderThickness="0,2,0,2"
          Content="KAYDET"
          FontSize="15"
          FontWeight="Bold">
    <Button.Style>
      <Style TargetType="Button">
        <Setter Property="Background" Value="Blue"/>
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="Button">
              <Border Background="{TemplateBinding Background}">
                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Border>
            <ControlTemplate.Triggers>
              <DataTrigger Binding="{Binding IsMouseOverButton}" Value="True">
                <Setter Property="Background" Value="Red" />
                <Setter Property="Foreground" Value="White" />
              </DataTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Button.Style>
</Button>
Community
  • 1
  • 1
WPFGermany
  • 1,639
  • 2
  • 12
  • 30
0

This works for me: Define a style resource (for the button) under settings Window.Resources - This style then sets the new Template (a border), the default background as blue, and the IsMouseOver trigger to change it to red. Reference the style either explicitly for implicitly (both worked for me).

Link to small test project: https://1drv.ms/u/s!AhlMAmchX3R6nDJ1MXS6DxlRXtnA

<Window x:Class="IsMouseOverTriggerSecondWindow.SettingsWindow"
        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:local="clr-namespace:IsMouseOverTriggerSecondWindow"
        mc:Ignorable="d"
        Title="SettingsWindow" Height="170" Width="330">

    <!-- my settings window button style-->
    <!-- defined as a resource in SettingsWindow.xaml, so it doesnt effect MainWindow -->
    <Window.Resources>
        <Style TargetType="Button" >
            <Setter Property="Background" Value="Blue"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}">
                            <ContentPresenter Content="{TemplateBinding Content}" 
                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="Red" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Grid>
        <TextBlock Text="This is the settings window" />
        <Button Content="KAYDET" Height="30" VerticalAlignment="Bottom" Foreground="White" FontWeight="Bold" />
    </Grid>
</Window>
Fredrik
  • 2,247
  • 18
  • 21
  • Thanks for trying to help. But in your project both Windows must be borderless. So you need to set `AllowsTransparency="True" WindowStyle="None"`. I know it is working with bordered windows. We need to work with borderless windows. – Ali Tor Sep 20 '16 at 11:24
  • you're right. that does make a difference. If you only need to get rid of the border, then removing AllowsTransparency="True" resolves the issue, but i'm sure you have tried this already. – Fredrik Sep 20 '16 at 12:01
  • Setting only WindowStyle="None" doesn't work, at that situation resizing borders still remains. So `AllowsTransparency="True" WindowStyle="None"` or `ResizeMode="NoResize" WindowStyle="None"` have to be used – Ali Tor Sep 20 '16 at 12:58