优化更新代码,添加界面功能并整合
This commit is contained in:
138
WPFluent/Controls/Flyout/Flyout.cs
Normal file
138
WPFluent/Controls/Flyout/Flyout.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
|
||||
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WPFluent.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a control that creates a pop-up window that displays information for an element in the interface.
|
||||
/// </summary>
|
||||
[TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
|
||||
public class Flyout : System.Windows.Controls.ContentControl
|
||||
{
|
||||
private const string ElementPopup = "PART_Popup";
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Closed"/> routed event.
|
||||
/// </summary>
|
||||
public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent(
|
||||
nameof(Closed),
|
||||
RoutingStrategy.Bubble,
|
||||
typeof(TypedEventHandler<Flyout, RoutedEventArgs>),
|
||||
typeof(Flyout));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="IsOpen"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
|
||||
nameof(IsOpen),
|
||||
typeof(bool),
|
||||
typeof(Flyout),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Opened"/> routed event.
|
||||
/// </summary>
|
||||
public static readonly RoutedEvent OpenedEvent = EventManager.RegisterRoutedEvent(
|
||||
nameof(Opened),
|
||||
RoutingStrategy.Bubble,
|
||||
typeof(TypedEventHandler<Flyout, RoutedEventArgs>),
|
||||
typeof(Flyout));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="Placement"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty PlacementProperty = DependencyProperty.Register(
|
||||
nameof(Placement),
|
||||
typeof(PlacementMode),
|
||||
typeof(Flyout),
|
||||
new PropertyMetadata(PlacementMode.Top));
|
||||
|
||||
private Popup _popup = default;
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="Flyout"/> is opened.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<Flyout, RoutedEventArgs> Closed
|
||||
{
|
||||
add => AddHandler(ClosedEvent, value);
|
||||
remove => RemoveHandler(ClosedEvent, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when <see cref="Flyout"/> is opened.
|
||||
/// </summary>
|
||||
public event TypedEventHandler<Flyout, RoutedEventArgs> Opened
|
||||
{
|
||||
add => AddHandler(OpenedEvent, value);
|
||||
remove => RemoveHandler(OpenedEvent, value);
|
||||
}
|
||||
|
||||
protected virtual void OnPopupClosed(object sender, EventArgs e)
|
||||
{
|
||||
Hide();
|
||||
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
|
||||
}
|
||||
|
||||
protected virtual void OnPopupOpened(object sender, EventArgs e)
|
||||
{ RaiseEvent(new RoutedEventArgs(OpenedEvent, this)); }
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
if(IsOpen)
|
||||
{
|
||||
SetCurrentValue(IsOpenProperty, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked whenever application code or an internal process, such as a rebuilding layout pass, calls the
|
||||
/// ApplyTemplate method.
|
||||
/// </summary>
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_popup = GetTemplateChild(ElementPopup) as Popup;
|
||||
|
||||
if(_popup is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_popup.Opened -= OnPopupOpened;
|
||||
_popup.Opened += OnPopupOpened;
|
||||
|
||||
_popup.Closed -= OnPopupClosed;
|
||||
_popup.Closed += OnPopupClosed;
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
if(!IsOpen)
|
||||
{
|
||||
SetCurrentValue(IsOpenProperty, true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether a <see cref="Flyout"/> is visible.
|
||||
/// </summary>
|
||||
public bool IsOpen { get => (bool)GetValue(IsOpenProperty); set => SetValue(IsOpenProperty, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the orientation of the <see cref="Flyout"/> control when the control opens, and specifies the
|
||||
/// behavior of the <see cref="T:System.Windows.Controls.Primitives.Popup"/> control when it overlaps screen
|
||||
/// boundaries.
|
||||
/// </summary>
|
||||
[Bindable(true)]
|
||||
[Category("Layout")]
|
||||
public PlacementMode Placement
|
||||
{
|
||||
get => (PlacementMode)GetValue(PlacementProperty);
|
||||
set => SetValue(PlacementProperty, value);
|
||||
}
|
||||
}
|
||||
75
WPFluent/Controls/Flyout/Flyout.xaml
Normal file
75
WPFluent/Controls/Flyout/Flyout.xaml
Normal file
@@ -0,0 +1,75 @@
|
||||
<!--
|
||||
This Source Code Form is subject to the terms of the MIT License.
|
||||
If a copy of the MIT was not distributed with this file, You can obtain one at https://opensource.org/licenses/MIT.
|
||||
Copyright (C) Leszek Pomianowski and WPF UI Contributors.
|
||||
All Rights Reserved.
|
||||
|
||||
Based on Microsoft XAML for Win UI
|
||||
Copyright (c) Microsoft Corporation. All Rights Reserved.
|
||||
-->
|
||||
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:WPFluent.Controls">
|
||||
|
||||
<Style x:Key="DefaultFlyoutStyle" TargetType="{x:Type controls:Flyout}">
|
||||
<Setter Property="Background" Value="{DynamicResource FlyoutBackground}" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource FlyoutBorderBrush}" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="MinWidth" Value="20" />
|
||||
<Setter Property="MinHeight" Value="20" />
|
||||
<Setter Property="Padding" Value="12" />
|
||||
<Setter Property="VerticalAlignment" Value="Bottom" />
|
||||
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Placement" Value="Top" />
|
||||
<Setter Property="Popup.AllowsTransparency" Value="True" />
|
||||
<Setter Property="Popup.StaysOpen" Value="False" />
|
||||
<Setter Property="Popup.PopupAnimation" Value="Fade" />
|
||||
<Setter Property="Popup.VerticalOffset" Value="1" />
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
|
||||
<Setter Property="SnapsToDevicePixels" Value="True" />
|
||||
<Setter Property="OverridesDefaultStyle" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type controls:Flyout}">
|
||||
<Grid>
|
||||
<Popup
|
||||
x:Name="PART_Popup"
|
||||
MinWidth="{TemplateBinding MinWidth}"
|
||||
MinHeight="{TemplateBinding MinHeight}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"
|
||||
AllowsTransparency="{TemplateBinding Popup.AllowsTransparency}"
|
||||
Focusable="False"
|
||||
IsOpen="{TemplateBinding IsOpen}"
|
||||
Placement="{TemplateBinding Placement}"
|
||||
PopupAnimation="{TemplateBinding Popup.PopupAnimation}"
|
||||
StaysOpen="{TemplateBinding Popup.StaysOpen}"
|
||||
VerticalOffset="1">
|
||||
<Border
|
||||
x:Name="PopupBorder"
|
||||
Margin="{TemplateBinding Margin}"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{DynamicResource PopupCornerRadius}"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter Content="{TemplateBinding Content}" />
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutStyle}" TargetType="{x:Type controls:Flyout}" />
|
||||
|
||||
</ResourceDictionary>
|
||||
186
WPFluent/Controls/Flyout/FlyoutService.cs
Normal file
186
WPFluent/Controls/Flyout/FlyoutService.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace WPFluent.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// <seealso cref="Flyout"/>
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code lang="xml">
|
||||
/// <ui:Button Content = "Show Flyout" >
|
||||
/// < ui:FlyoutService.Flyout>
|
||||
/// <ui:Flyout Placement = "Bottom" >
|
||||
/// < StackPanel >
|
||||
/// < TextBlock
|
||||
/// HorizontalAlignment="Left"
|
||||
/// Text="Show the flyout message here" />
|
||||
/// <Button
|
||||
/// Command = "{Binding GotItCommand}"
|
||||
/// Content="Got it" />
|
||||
/// </StackPanel>
|
||||
/// </ui:Flyout>
|
||||
/// </ui:FlyoutService.Flyout>
|
||||
///</ui:Button>
|
||||
///</code>
|
||||
/// </example>
|
||||
public static class FlyoutService
|
||||
{
|
||||
public static Flyout GetFlyout(DependencyObject obj)
|
||||
{
|
||||
return (obj.GetValue(FlyoutProperty) as Flyout)!;
|
||||
}
|
||||
|
||||
public static void SetFlyout(DependencyObject obj, Flyout value)
|
||||
{
|
||||
obj.SetValue(FlyoutProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty FlyoutProperty =
|
||||
DependencyProperty.RegisterAttached("Flyout", typeof(Flyout), typeof(FlyoutService), new(null, OnFlyoutChanged));
|
||||
|
||||
public static void OnFlyoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is FrameworkElement buttonExpected)
|
||||
{
|
||||
if (e.NewValue is Flyout flyout)
|
||||
{
|
||||
// Inherit data context.
|
||||
flyout.DataContext = buttonExpected.DataContext;
|
||||
|
||||
buttonExpected.DataContextChanged -= ButtonExpectedDataContextChanged;
|
||||
buttonExpected.DataContextChanged += ButtonExpectedDataContextChanged;
|
||||
}
|
||||
|
||||
// Binding click or leftmouse event to show flyout.
|
||||
{
|
||||
if (d is Button button)
|
||||
{
|
||||
button.Click -= ShowFlyoutRequested;
|
||||
button.Click += ShowFlyoutRequested;
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
if (d is System.Windows.Controls.Button button)
|
||||
{
|
||||
button.Click -= ShowFlyoutRequested;
|
||||
button.Click += ShowFlyoutRequested;
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
buttonExpected.MouseLeftButtonUp -= ShowFlyoutRequested;
|
||||
buttonExpected.MouseLeftButtonUp += ShowFlyoutRequested;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ButtonExpectedDataContextChanged(object? sender, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement buttonExpected)
|
||||
{
|
||||
if (GetFlyout(buttonExpected) is Flyout flyout)
|
||||
{
|
||||
// Inherit data context.
|
||||
flyout.DataContext = buttonExpected.DataContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowFlyoutRequested(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement buttonExpected)
|
||||
{
|
||||
ShowFlyout(buttonExpected);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ShowFlyoutRequested(object? sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement buttonExpected)
|
||||
{
|
||||
ShowFlyout(buttonExpected);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowFlyout(FrameworkElement buttonExpected)
|
||||
{
|
||||
if (GetFlyout(buttonExpected) is Flyout flyout)
|
||||
{
|
||||
// Get the flyout popup from Flyout control default template.
|
||||
flyout.ApplyTemplate();
|
||||
if (flyout.GetTemplateChild("PART_Popup") is System.Windows.Controls.Primitives.Popup popup)
|
||||
{
|
||||
// Inherit data context.
|
||||
popup.DataContext = flyout.DataContext;
|
||||
|
||||
// Reset the popup placement.
|
||||
popup.PlacementTarget = buttonExpected;
|
||||
popup.Placement = flyout.Placement;
|
||||
|
||||
// Remove the popup parent
|
||||
if (popup.Parent is System.Windows.Controls.Panel parent)
|
||||
{
|
||||
parent.Children.Remove(popup);
|
||||
}
|
||||
|
||||
// Set the flyout parent
|
||||
if (flyout.Parent is null)
|
||||
{
|
||||
// Find nearest panel
|
||||
if (buttonExpected.Parent is System.Windows.Controls.Panel parent2)
|
||||
{
|
||||
parent2.Children.Add(flyout);
|
||||
}
|
||||
// Once fallback to window top level
|
||||
else if (Window.GetWindow(buttonExpected)?.Content is System.Windows.Controls.Panel parent3)
|
||||
{
|
||||
parent3.Children.Add(flyout);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flyout is not added to any parent.
|
||||
// Flyout will be shown but the theme is not synced.
|
||||
// See more https://github.com/emako/wpfui.violeta/issues/10.
|
||||
}
|
||||
}
|
||||
|
||||
// Following code is based on the Flyout control default template.
|
||||
// If default template is changed, this code will not work.
|
||||
// Check WPF-UI v3.0.5 since.
|
||||
if (flyout.Content is not null)
|
||||
{
|
||||
UIElement? contentElement = flyout.Content as UIElement;
|
||||
flyout.Content = null;
|
||||
((System.Windows.Controls.Border)popup.Child).Child = contentElement;
|
||||
}
|
||||
|
||||
// Spoof the flyout opening state.
|
||||
flyout.IsOpen = popup.IsOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void HideFlyout(FrameworkElement buttonExpected)
|
||||
{
|
||||
if (GetFlyout(buttonExpected) is Flyout flyout)
|
||||
{
|
||||
// Get the flyout popup from Flyout control default template.
|
||||
if (flyout.GetTemplateChild("PART_Popup") is System.Windows.Controls.Primitives.Popup popup)
|
||||
{
|
||||
// Spoof the flyout opening state.
|
||||
flyout.IsOpen = popup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DependencyObject? GetTemplateChild(this FrameworkElement self, string childName)
|
||||
{
|
||||
MethodInfo? method = typeof(FrameworkElement)
|
||||
.GetMethod("GetTemplateChild", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod);
|
||||
|
||||
return method?.Invoke(self, [childName]) as DependencyObject;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user