好的,我有一个解决方案。它非常复杂。
如果你只是想要一个带尾巴的简单弹出窗口,你可能可以使用其中的一部分(ActualLayout和UpdateTail逻辑)。如果你想要完整的帮助提示体验™,那么你将会经历一段不愉快的旅程。
我认为最好还是采用装饰器路线(我正在考虑重新设计这个来使用装饰器)。我发现了一些问题,仍在进行中。使用弹出窗口会导致它们出现在设计器的其他窗口上面,这真的很烦人。我还注意到它们在某些电脑上的位置不正确,原因很奇怪(但是我没有在安装Visual Studio以便正确调试的任何电脑上遇到过这种情况)。
它产生的效果如下:
具备以下条件:
每次屏幕上只能显示一个帮助提示
如果用户更改选项卡,并且所附加帮助提示的控件不再可见,则帮助提示消失并显示下一个帮助提示
关闭后,该类型的帮助提示将不再显示
可以通过一个中央选项关闭帮助提示
好的。所以,实际的帮助提示是一个完全透明的用户控件,添加到UI中。它有一个由静态类管理的弹出窗口。以下是控件:
<UserControl x:Class="...HelpPopup"
d:DesignHeight="0" d:DesignWidth="0">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
<Canvas>
<Popup x:Name="Popup"
d:DataContext="{d:DesignInstance {x:Null}}"
DataContext="{Binding HelpTip, ElementName=userControl}"
StaysOpen="True" PopupAnimation="Fade"
AllowsTransparency="True"
materialDesign:ShadowAssist.ShadowDepth="Depth3"
Placement="{Binding Placement, ElementName=userControl}"
HorizontalOffset="-10"
VerticalOffset="{Binding VerticalOffset, ElementName=userControl}">
<Grid Margin="0,0,0,0" SnapsToDevicePixels="True">
<Canvas Margin="10">
<local:RoundedCornersPolygon Fill="{StaticResource PrimaryHueDarkBrush}"
SnapsToDevicePixels="True"
ArcRoundness="4"
Points="{Binding PolygonPath, ElementName=userControl}"
Effect="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Popup}, Path=(materialDesign:ShadowAssist.ShadowDepth), Converter={x:Static converters:ShadowConverter.Instance}}"/>
</Canvas>
<Border BorderBrush="Transparent" BorderThickness="10,25,10,25">
<Grid x:Name="PopupChild">
<materialDesign:ColorZone Mode="PrimaryDark" Margin="5">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="AUTO"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Useful Tip"
FontWeight="Bold"
Margin="2,0,0,0"
Grid.ColumnSpan="2"
VerticalAlignment="Center"/>
<Button Style="{StaticResource MaterialDesignToolButton}" Click="CloseButton_Click" Grid.Column="1" Margin="0" Padding="0" Height="Auto">
<Button.Content>
<materialDesign:PackIcon Kind="CloseCircle" Height="20" Width="20" Foreground="{StaticResource PrimaryHueLightBrush}"/>
</Button.Content>
</Button>
</Grid>
<TextBlock Text="{Binding Message}"
TextWrapping="Wrap"
MaxWidth="300"
Margin="2,4,2,4"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Close" Padding="8,2" Height="Auto" Click="CloseButton_Click"
Margin="2"
Style="{StaticResource MaterialDesignFlatButtonInverted}"/>
<Button Content="Never show again"
Margin="2"
Padding="8,2"
Height="Auto"
Click="NeverShowButton_Click"
Style="{StaticResource MaterialDesignFlatButtonInverted}"/>
</StackPanel>
</StackPanel>
</materialDesign:ColorZone>
</Grid>
</Border>
</Grid>
</Popup>
</Canvas>
</UserControl>
您可以更改此样式以使其符合您的要求。我使用了自定义的圆角多边形类和MaterialDesign颜色区域。您可以随意替换它们。
现在,背后的代码...呃,有很多,而且不太好看:
public enum ActualPlacement { TopLeft, TopRight, BottomLeft, BottomRight }
/// <summary>
/// Interaction logic for HelpPopup.xaml
/// </summary>
public partial class HelpPopup : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ActualPlacement actualPlacement = ActualPlacement.TopRight;
public ActualPlacement ActualPlacement
{
get { return actualPlacement; }
internal set
{
if (actualPlacement != value)
{
if (actualPlacement == ActualPlacement.BottomLeft || ActualPlacement == ActualPlacement.BottomRight)
{
Console.WriteLine("-10");
VerticalOffset = 10;
}
else if (actualPlacement == ActualPlacement.TopLeft || ActualPlacement == ActualPlacement.TopRight)
{
VerticalOffset = -10;
Console.WriteLine("10");
}
actualPlacement = value;
UpdateTailPath();
NotifyOfPropertyChange("ActualPlacement");
}
}
}
public void UpdateTailPath()
{
double height = PopupChild.ActualHeight + 30;
double width = PopupChild.ActualWidth;
switch (actualPlacement)
{
case ActualPlacement.TopRight:
polygonPath = "0.5,15.5 " + (width - 0.5) + ",15.5 " + (width - 0.5) + "," + (height - 15.5) +
" 15.5," + (height - 15.5) + " 0.5," + height + " 0.5,15.5"; ;
break;
case ActualPlacement.TopLeft:
polygonPath = "0.5,15.5 " + (width - 0.5) + ",15.5 " + (width - 0.5) + "," + height + " " + (width - 15.5) + "," + (height - 15.5) +
" 0.5," + (height - 15.5) + " 0.5,15.5";
break;
case ActualPlacement.BottomRight:
polygonPath = "0.5,0.5 15.5,15.5 " + (width - 0.5) + ",15.5 " + (width - 0.5) + "," + (height - 15.5) +
" 0.5," + (height - 15.5) + " 0.5,0.5";
break;
case ActualPlacement.BottomLeft:
polygonPath = "0.5,15.5 " + (width - 15.5) + ",15.5 " + (width - 0.5) + ",0.5 " + (width - 0.5) + "," + (height - 15.5) +
" 0.5," + (height - 15.5) + " 0.5,15.5";
break;
}
NotifyOfPropertyChange("PolygonPath");
}
private String polygonPath;
public String PolygonPath
{
get { return polygonPath; }
}
public PlacementMode Placement
{
get { return (PlacementMode)GetValue(PlacementProperty); }
set { SetValue(PlacementProperty, value); }
}
// Using a DependencyProperty as the backing store for Placement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PlacementProperty =
DependencyProperty.Register("Placement", typeof(PlacementMode), typeof(HelpPopup), new PropertyMetadata(PlacementMode.Top));
public int VerticalOffset
{
get { return (int)GetValue(VerticalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
// Using a DependencyProperty as the backing store for VerticalOffset. This enables animation, styling, binding, etc...
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.Register("VerticalOffset", typeof(int), typeof(HelpPopup), new PropertyMetadata(-10));
public HelpTip HelpTip
{
get { return (HelpTip)GetValue(HelpTipProperty); }
set { SetValue(HelpTipProperty, value); }
}
// Using a DependencyProperty as the backing store for Message. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HelpTipProperty =
DependencyProperty.Register("HelpTip", typeof(HelpTip), typeof(HelpPopup), new PropertyMetadata(new HelpTip() { Message = "No help message found..." }, HelpTipChanged));
private static void HelpTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((d as HelpPopup).HelpTipOnScreenInstance == null)
{
(d as HelpPopup).HelpTipOnScreenInstance = new HelpTipOnScreenInstance((d as HelpPopup));
}
(d as HelpPopup).HelpTipOnScreenInstance.HelpTip = (e.NewValue as HelpTip);
}
private static void HelpTipOnScreenInstance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
HelpTipOnScreenInstance htosi = sender as HelpTipOnScreenInstance;
if (e.PropertyName.Equals(nameof(htosi.IsOpen)))
{
//open manually to avoid stupid COM errors
if (htosi != null)
{
try
{
htosi.HelpPopup.Popup.IsOpen = htosi.IsOpen;
}
catch (System.ComponentModel.Win32Exception ex)
{
Canvas parent = htosi.HelpPopup.Popup.Parent as Canvas;
htosi.HelpPopup.Popup.IsOpen = false;
parent.Children.Remove(htosi.HelpPopup.Popup);
Application.Current.Dispatcher.BeginInvoke(new Action(() => {
htosi.HelpPopup.Popup.IsOpen = true;
parent.Children.Add(htosi.HelpPopup.Popup);
htosi.HelpPopup.UpdatePositions();
}), DispatcherPriority.SystemIdle);
}
}
}
}
private HelpTipOnScreenInstance helpTipOnScreenInstance;
public HelpTipOnScreenInstance HelpTipOnScreenInstance
{
get { return helpTipOnScreenInstance; }
set
{
if (helpTipOnScreenInstance != value)
{
if (helpTipOnScreenInstance != null)
{
HelpTipOnScreenInstance.PropertyChanged -= HelpTipOnScreenInstance_PropertyChanged;
}
helpTipOnScreenInstance = value;
HelpTipOnScreenInstance.PropertyChanged += HelpTipOnScreenInstance_PropertyChanged;
NotifyOfPropertyChange("HelpTipOnScreenInstance");
}
}
}
private double popupX;
public double PopupX
{
get { return popupX; }
set
{
if (popupX != value)
{
popupX = value;
NotifyOfPropertyChange("PopupX");
}
}
}
private double popupY;
public double PopupY
{
get { return popupY; }
set
{
if (popupY != value)
{
popupY = value;
NotifyOfPropertyChange("PopupY");
}
}
}
private void NotifyOfPropertyChange(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public HelpPopup()
{
InitializeComponent();
// Wire up the Loaded handler instead
this.Loaded += new RoutedEventHandler(View1_Loaded);
this.Unloaded += HelpPopup_Unloaded;
Popup.Opened += Popup_Opened;
//PopupChild.LayoutUpdated += HelpPopup_LayoutUpdated;
PopupChild.SizeChanged += HelpPopup_SizeChanged;
UpdatePositions();
}
private void Popup_Opened(object sender, EventArgs e)
{
UpdateTail();
UpdateTailPath();
}
private void HelpPopup_SizeChanged(object sender, SizeChangedEventArgs e)
{
Console.WriteLine(HelpTip.Message + ": " + e.PreviousSize.ToString() + " to " + e.NewSize.ToString());
UpdateTail();
UpdateTailPath();
}
private void HelpPopup_Unloaded(object sender, RoutedEventArgs e)
{
//don't waste resources on never show popups
if (HelpTip.NeverShow)
{
return;
}
HelpTipOnScreenInstance.IsOnscreen = false;
}
/// Provides a way to "dock" the Popup control to the Window
/// so that the popup "sticks" to the window while the window is dragged around.
void View1_Loaded(object sender, RoutedEventArgs e)
{
//don't waste resources on never show popups
if (HelpTip.NeverShow)
{
return;
}
//wait for a few seconds, then set this to on-screen
HelpTipOnScreenInstance.IsOnscreen = true;
//update so tail is facing right direction
UpdateTail();
Window w = Window.GetWindow(this);
// w should not be Null now!
if (null != w)
{
w.LocationChanged += delegate (object sender2, EventArgs args)
{
// "bump" the offset to cause the popup to reposition itself
// on its own
UpdatePositions();
};
// Also handle the window being resized (so the popup's position stays
// relative to its target element if the target element moves upon
// window resize)
w.SizeChanged += delegate (object sender3, SizeChangedEventArgs e2)
{
UpdatePositions();
};
}
}
private void UpdatePositions()
{
var offset = Popup.HorizontalOffset;
Popup.HorizontalOffset = offset + 1;
Popup.HorizontalOffset = offset;
UpdateTail();
}
private void UpdateTail()
{
UIElement container = VisualTreeHelper.GetParent(this) as UIElement;
Point relativeLocation = PopupChild.TranslatePoint(new Point(5, 5), container); //It HAS(!!!) to be this.Child
if (relativeLocation.Y < 0)
{
if (relativeLocation.X < -(PopupChild.ActualWidth-5 / 2))
{
ActualPlacement = ActualPlacement.TopLeft;
}
else
{
ActualPlacement = ActualPlacement.TopRight;
}
}
else
{
if (relativeLocation.X < -(PopupChild.ActualWidth-5 / 2))
{
ActualPlacement = ActualPlacement.BottomLeft;
}
else
{
ActualPlacement = ActualPlacement.BottomRight;
}
}
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
lock (HelpTip.Lock)
{
HelpTip.Closed = true;
HelpTipOnScreenInstance.IsOpen = false;
}
}
private void NeverShowButton_Click(object sender, RoutedEventArgs e)
{
lock (HelpTip.Lock)
{
HelpTip.Closed = true;
HelpTip.NeverShow = true;
HelpTipOnScreenInstance.IsOpen = false;
}
}
}
需要注意的事项:
有一个 "ActualPlacement" 可以管理弹出窗口的实际位置,因为设置放置位置只是 WPF 的建议。
UpdateTailPath() 重新绘制多边形以便在位置发生变化后正确地放置尾巴。
我们有一个 HelpTip 类来存储信息(标题、内容等),以及 HelpTipOnScreenInstance 来控制它是否显示在屏幕上。这样做的原因是我们可以在屏幕上有多个相同类型的帮助提示,但只想显示一个。
各种监听器用于弹出窗口事件以触发尾巴更新。
我们附加到用户控件的加载和卸载事件。这使我们能够跟踪控件是否在屏幕上以及是否应该显示帮助提示 (HelpTipOnScreenInstance.IsOnscreen = true)。
我们还监听窗口更改事件,以便在窗口大小调整或移动时更新弹出窗口的位置。
现在,有 HelpTipOnScreenInstance 和 HelpTip 两个类:
public class HelpTipOnScreenInstance : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public object Lock = new Object();
private void NotifyOfPropertyChange(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
//handler(this, new PropertyChangedEventArgs(propertyName));
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private HelpTip helpTip;
public HelpTip HelpTip
{
get { return helpTip; }
set
{
if (helpTip != value)
{
helpTip = value;
NotifyOfPropertyChange("HelpTip");
}
}
}
private bool isOpen = false;
public bool IsOpen
{
get { return isOpen; }
set
{
if (isOpen != value)
{
isOpen = value;
Console.WriteLine("Opening " + HelpTip.Message);
NotifyOfPropertyChange("IsOpen");
}
}
}
private bool isOnscreen = false;
public bool IsOnscreen
{
get { return isOnscreen; }
set
{
if (isOnscreen != value)
{
isOnscreen = value;
NotifyOfPropertyChange("IsOnscreen");
}
}
}
private HelpPopup helpPopup;
public HelpPopup HelpPopup
{
get { return helpPopup; }
set
{
if (helpPopup != value)
{
helpPopup = value;
NotifyOfPropertyChange("HelpPopup");
}
}
}
public HelpTipOnScreenInstance(HelpPopup helpPopup)
{
HelpPopup = helpPopup;
HelpTipManager.AddHelpTip(this);
}
}
public class HelpTip : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public object Lock = new Object();
private void NotifyOfPropertyChange(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
//handler(this, new PropertyChangedEventArgs(propertyName));
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private String id;
public String ID
{
get { return id; }
set { id = value; }
}
private String message;
public String Message
{
get { return message; }
set
{
if (message != value)
{
message = value;
NotifyOfPropertyChange("Message");
}
}
}
private bool closed;
public bool Closed
{
get { return closed; }
set
{
if (closed != value)
{
closed = value;
NotifyOfPropertyChange("Closed");
}
}
}
public bool NeverShow { get; set; }
}
接着是一个静态管理器类,用于跟踪当前屏幕上的内容并选择下一个显示的项目:
public static class HelpTipManager
{
public static object Lock = new Object();
private static bool displayHelpTips = false;
public static bool DisplayHelpTips
{
get { return displayHelpTips; }
set {
if (displayHelpTips != value)
{
displayHelpTips = value;
if (displayHelpTips)
{
//open next!
OpenNext();
}
else
{
//stop displaying all
foreach(HelpTipOnScreenInstance helpTip in helpTipsOnScreen)
{
lock (helpTip.HelpTip.Lock)
{
helpTip.IsOpen = false;
}
}
}
}
}
}
private static List<HelpTipOnScreenInstance> helpTips = new List<HelpTipOnScreenInstance>();
private static List<HelpTipOnScreenInstance> helpTipsOnScreen = new List<HelpTipOnScreenInstance>();
private static bool supressOpenNext = false;
public static void AddHelpTip(HelpTipOnScreenInstance helpTip)
{
helpTip.PropertyChanged += HelpTip_PropertyChanged;
helpTips.Add(helpTip);
}
private static void HelpTip_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
HelpTipOnScreenInstance helpTip = sender as HelpTipOnScreenInstance;
if (helpTip != null)
{
//is this on screen or not?
switch (e.PropertyName)
{
case "IsOnscreen":
//Update our onscreen lists and perform related behaviour
if (helpTip.IsOnscreen)
{
AddedToScreen(helpTip);
}
else
{
RemovedFromScreen(helpTip);
}
break;
case "IsOpen":
lock (helpTip.Lock)
{
if (!supressOpenNext)
{
if (!helpTip.IsOpen)
{
OpenNext();
}
}
}
break;
}
}
}
private static void OpenNext()
{
if (DisplayHelpTips)
{
if (helpTipsOnScreen.Count > 0)
{
//check if none of them are open
if (helpTipsOnScreen.Count(ht => ht.IsOpen) == 0)
{
//open the first that's not been closed!
HelpTipOnScreenInstance firstNotClosed = helpTipsOnScreen.FirstOrDefault(ht => !ht.HelpTip.Closed);
if (firstNotClosed != null)
{
lock (firstNotClosed.Lock)
{
firstNotClosed.IsOpen = true;
}
}
}
}
}
}
private static void AddedToScreen(HelpTipOnScreenInstance helpTip)
{
lock (Lock)
{
helpTipsOnScreen.Add(helpTip);
OpenNext();
}
}
private static void RemovedFromScreen(HelpTipOnScreenInstance helpTip)
{
lock (Lock)
{
helpTipsOnScreen.Remove(helpTip);
supressOpenNext = true;
helpTip.IsOpen = false;
//OpenNext();
supressOpenNext = false;
}
}
}
<controls:HelpTip x:Key="KPIGraphMenu" ID="KPIGraphMenu" Message="Right click to change the colour, remove, or move KPI to view as a stacked trace. KPI can also be dragged onto other charts of any type."/>
然后在实际应用中使用它们,我喜欢将它们与相关的控件叠加在网格中,使用对齐方式确定尾巴指向的位置:
<controls:HelpPopup HelpTip="{StaticResource KPIGraphMenu}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
我已经使用了CustomPopupPlacementCallback委托。我甚至考虑了箭头的垂直移动。所以,在下面的例子中,箭头可以左右、上下移动。
你可以按照现有样例来使用它。
Window1.xaml
<Window ...>
<Grid>
<Button Click="Btn_Click" Width="110" Height="25" Content="Button" HorizontalAlignment="Left" Margin="437,26,0,0" VerticalAlignment="Top"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" Margin="10,90,0,0" VerticalAlignment="Top" Width="75"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" Margin="139,146,0,0" VerticalAlignment="Top" Width="75"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" Margin="180,0,0,0" VerticalAlignment="Top" Width="74"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" Margin="224,333,0,0" VerticalAlignment="Top" Width="76"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Right" VerticalAlignment="Top" Width="75"/>
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Bottom" Width="75" />
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="75" />
<Button Click="Btn_Click" Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" />
<Popup x:Name="Popup1" Placement="Custom" StaysOpen="False" Opened="Popup1_Opened">
<Grid x:Name="Grd" Width="300" Height="100" Background="AliceBlue">
<Canvas x:Name="Cnv">
<Path x:Name="TopArrow" Canvas.Left="50" Canvas.Top="25" Margin="5" Data="M0,0 L-5,-5 L-10,0 z" Fill="Black" Stroke="Black" StrokeThickness="2"/>
<TextBlock Canvas.Top="35" FontSize="18" x:Name="Tb1"/>
</Canvas>
</Grid>
</Popup>
</Grid>
</Window>
Window1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Shapes;
namespace ...
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Popup1.CustomPopupPlacementCallback =
new CustomPopupPlacementCallback(placePopup);
}
public CustomPopupPlacement[] placePopup(Size popupSize,
Size targetSize,
Point offset)
{
CustomPopupPlacement placement2 =
new CustomPopupPlacement(new Point(-(popupSize.Width - targetSize.Width / 2), targetSize.Height), PopupPrimaryAxis.Vertical);
CustomPopupPlacement placement1 =
new CustomPopupPlacement(new Point(targetSize.Width / 2, targetSize.Height), PopupPrimaryAxis.Vertical);
CustomPopupPlacement placement3 =
new CustomPopupPlacement(new Point(targetSize.Width/2, -popupSize.Height), PopupPrimaryAxis.Horizontal);
CustomPopupPlacement placement4 =
new CustomPopupPlacement(new Point(-(popupSize.Width - targetSize.Width/2), -popupSize.Height), PopupPrimaryAxis.Horizontal);
CustomPopupPlacement[] ttplaces =
new CustomPopupPlacement[] { placement1, placement2, placement3, placement4 };
return ttplaces;
}
private void Btn_Click(object sender, RoutedEventArgs e)
{
Popup1.PlacementTarget = sender as Button;
Popup1.IsOpen = true;
}
private void Popup1_Opened(object sender, EventArgs e)
{
Path arrow = ((Path)Popup1.FindName("TopArrow"));
Grid grd = ((Grid)Popup1.FindName("Grd"));
UIElement elem = (UIElement)Popup1.PlacementTarget;
Point elem_pos_lefttop = elem.PointToScreen(new Point(0, 0));
Point popup_pos_lefttop = grd.PointToScreen(new Point(0, 0));
if ( (elem_pos_lefttop.Y < popup_pos_lefttop.Y )
&&
((elem_pos_lefttop.X > popup_pos_lefttop.X))
)
{
Canvas.SetLeft(arrow, 280);
Canvas.SetTop(arrow, 25);
}
if ((elem_pos_lefttop.Y < popup_pos_lefttop.Y)
&&
((elem_pos_lefttop.X < popup_pos_lefttop.X))
)
{
Canvas.SetLeft(arrow, 30);
Canvas.SetTop(arrow, 25);
}
if ((elem_pos_lefttop.Y > popup_pos_lefttop.Y)
&&
((elem_pos_lefttop.X > popup_pos_lefttop.X))
)
{
Canvas.SetLeft(arrow, 280);
Canvas.SetTop(arrow, 90);
}
if ((elem_pos_lefttop.Y > popup_pos_lefttop.Y)
&&
((elem_pos_lefttop.X < popup_pos_lefttop.X))
)
{
Canvas.SetLeft(arrow, 30);
Canvas.SetTop(arrow, 90);
}
Tb1.Text = String.Format("Element = {0} \r\n Popup = {1}", elem_pos_lefttop, popup_pos_lefttop);
}
}
}
placePopup
从未被调用。 - Daniel PeñalbaPopup1.Placement = PlacementMode.Custom;
使其工作。 - Daniel Peñalba