问题
I am new to WPF and XAML and cant find what I am doing wrong here.
My UserControl
has few controls, and I need to control Popup.IsOpen
from code-behind.
public bool PopupVisible
{
get
{
//true
if( txtSearch.IsKeyboardFocusWithin ) return true;
if( txtSearch.IsMouseOver ) return true;
//false
if( txtSearch.IsKeyboardFocusWithin == false &&
( grid.IsMouseOver == false || popContainer.IsMouseOver == false || popContainer.IsMouseOver == false)
)
{
return false;
}
return false;
}
}
My XAML:
<UserControl x:Class="Controls.KlasifikacijaBolestSearchBox.KlasifikacijaBolestSearchBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Controls"
xmlns:itemClass="clr-namespace:Controls.KlasifikacijaBolestSearchBox.ViewModels"
x:Name="this"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="this_Loaded">
<Grid x:Name="grid" DataContext="{Binding Class}">
<DockPanel LastChildFill="False">
<controls:SearchTextBox x:Name="txtSearch" DockPanel.Dock="Top" TextChanged="txtSearch_TextChanged" OnSearch="txtSearch_OnSearch" Background="WhiteSmoke"></controls:SearchTextBox>
</DockPanel>
<Popup x:Name="popContainer"
Placement="Bottom"
PlacementTarget="{Binding ElementName=txtSearch}"
AllowsTransparency="True"
StaysOpen="True"
IsOpen="{Binding PopupVisible, Mode=TwoWay}"
PopupAnimation="Fade"
MinWidth="500"
MinHeight="500"
Width="{Binding ElementName=txtSearch, Path=ActualWidth}">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Top" TextAlignment="Center" Text="Klasifikacija bolesti" FontWeight="Bold" Background="{DynamicResource WindowBackgroundBrush}" Foreground="White">
</TextBlock>
<TreeView
DockPanel.Dock="Top"
x:Name="tvKlasifikacijaBolest"
ItemTemplate="{StaticResource tvKlasifikacijaBolestHDStyle}"
ItemContainerStyle="{StaticResource tvKlasifikacijaBolestItemStyle}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
MouseDoubleClick="tvKlasifikacijaBolest_MouseDoubleClick">
</TreeView>
</DockPanel>
</Popup>
</Grid>
</UserControl>
Then I was thinking I need to implement INotifyPropertyChanged
so I change my code to this:
private bool popupOpen;
public bool PopupOpen
{
get { return popupOpen; }
set
{
popupOpen = value;
OnNotifyPropertyChanged("PopupOpen");
}
}
private void txtSearch_TextChanged(string searchText, string searchType)
{
PopupOpen = true;
}
And of course changed my XAML part:
<Popup x:Name="popContainer"
IsOpen="{Binding PopupOpen, Mode=TwoWay}">
<!-- ... -->
</Popup>
And my popup never gets Opened.
At first I used datatriggers and it work fine until I turn AllowTransparency="True"
then my triggers had some issue when I move mouse over my control some part of poup is transparent and it closes popup so I changed to code-behind but I dont know what I am doing wrong.
Bottom line is - I need my popup to be open while txtSearch.IsMouseOver
or IsKeyboardFocusedWithin
, or my treeview IsMouseOver
or any child componenet in popup control IsMouseOver = true
(border, label, textblock...).
If txtSearch
has focus then popup need to stay open no metter if IsMouseOver
on Popup
elements is True
or False
.
回答1:
You have two problems.
Note: Two paragraphs below is only for educational purposes.
The first. The PopupVisible
never notifies that it has changed. To notify targets you need to change this to make it a read-only DependencyPrperty
:
private static readonly DependencyPropertyKey IsPopupOpenPropertyKey = DependencyProperty.RegisterReadOnly(
"IsPopupOpen",
typeof(bool),
typeof(MainWindow),
new FrameworkPropertyMetadata(false)
);
private static readonly DependencyProperty IsPopupOpenProperty = IsPopupOpenPropertyKey.DependencyProperty;
public bool IsPopupOpen
{
get { return (bool)GetValue(IsPopupOpenProperty); }
private set { SetValue(IsPopupOpenPropertyKey, value); }
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == UIElement.IsMouseOverProperty ||
e.Property == UIElement.IsKeyboardFocusWithinProperty)
{
IsPopupOpen =
txtSearch.IsMouseOver ||
txtSearch.IsKeyboardFocusWithin;
}
}
The second. When you bind to the property of a control then you must specify ElementName
or RelativeSource
. Otherwise, a Binding
will see for the property in the inherited data context.
<UserControl x:Class="Controls.KlasifikacijaBolestSearchBox.KlasifikacijaBolestSearchBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:searchBox="Controls.KlasifikacijaBolestSearchBox">
<!-- ... -->
<Popup x:Name="popContainer"
IsOpen="{Binding IsPopupOpen,
RelativeSource={RelativeSource AncestorType={x:Type searchBox:KlasifikacijaBolestSearchBox}},
Mode=OneWay}">
Solution. To achieve the listed behavior you should use a IMultiValueConverter
and a MultiBinding
.
public sealed class OrConverter : IMultiValueConverter
{
public object Convert(object[] values, System.Type targetType, object parameter, CultureInfo culture)
{
foreach (bool value in values)
{
if (value) { return true; }
}
return false;
}
public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
XAML:
<UserControl x:Class="Controls.KlasifikacijaBolestSearchBox.KlasifikacijaBolestSearchBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Controls"
xmlns:converters="clr-namespace:Converters"
xmlns:itemClass="clr-namespace:Controls.KlasifikacijaBolestSearchBox.ViewModels"
x:Name="this"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Loaded="this_Loaded">
<UserControl.Resources>
<converters:OrConverter x:Key="Converter" />
</UserControl.Resources>
<Grid x:Name="grid" DataContext="{Binding Class}">
<DockPanel LastChildFill="False">
<TextBox x:Name="txtSearch"
Background="WhiteSmoke"
DockPanel.Dock="Top" />
</DockPanel>
<Popup x:Name="popContainer"
Width="{Binding ElementName=txtSearch,
Path=ActualWidth}"
MinWidth="500"
MinHeight="500"
AllowsTransparency="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=txtSearch}"
PopupAnimation="Fade"
StaysOpen="True">
<Popup.IsOpen>
<MultiBinding Converter="{StaticResource OrConverter}">
<Binding ElementName="txtSearch" Path="IsMouseOver" Mode="OneWay" />
<Binding ElementName="txtSearch" Path="IsKeyboardFocusWithin" Mode="OneWay" />
<Binding ElementName="popContainer" Path="IsMouseOver" Mode="OneWay" />
</MultiBinding>
</Popup.IsOpen>
回答2:
Try to handle Opened and Closed events of Popup. Here's an example: Popup.Closed Event
来源:https://stackoverflow.com/questions/29727252/wpf-popup-isopen-binding-in-code-behind-doesnt-work-usercontrol