Use VisualStates to manage the placement of the flyout

This commit is contained in:
Daniel Clark
2021-02-06 21:53:43 +00:00
parent 6558c262c4
commit 3232f1ac77
7 changed files with 103 additions and 92 deletions
@@ -2,8 +2,7 @@
x:Class="NotificationFlyout.Sample.NotificationFlyoutPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:NotificationFlyout.Uwp.UI.Controls"
CornerRadius="4">
xmlns:controls="using:NotificationFlyout.Uwp.UI.Controls">
<Grid>
<ListBox
Width="300"
@@ -122,7 +122,6 @@
<ItemGroup>
<Compile Include="NotificationFlyoutHost\NotificationFlyoutHost.cs" />
<Compile Include="NotificationFlyoutPresenter\NotificationFlyoutPresenter.cs" />
<Compile Include="NotificationFlyoutHost\NotificationFlyoutHostTemplateSettings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\NotificationFlyout.Uwp.UI.Controls.rd.xml" />
</ItemGroup>
@@ -11,20 +11,13 @@ namespace NotificationFlyout.Uwp.UI.Controls
typeof(NotificationFlyoutPresenter), typeof(NotificationFlyoutHost),
new PropertyMetadata(null));
public static readonly DependencyProperty TemplateSettingsProperty =
DependencyProperty.Register(nameof(TemplateSettings),
typeof(NotificationFlyoutHostTemplateSettings), typeof(NotificationFlyoutHost),
new PropertyMetadata(null));
private readonly NotificationFlyoutHostTemplateSettings _templateSettings;
private bool _isLoaded;
private string _placement;
private Grid _root;
public NotificationFlyoutHost()
{
DefaultStyleKey = typeof(NotificationFlyoutHost);
_templateSettings = new NotificationFlyoutHostTemplateSettings();
SetValue(TemplateSettingsProperty, _templateSettings);
}
public NotificationFlyoutPresenter FlyoutPresenter
@@ -33,12 +26,6 @@ namespace NotificationFlyout.Uwp.UI.Controls
set => SetValue(FlyoutPresenterProperty, value);
}
public NotificationFlyoutHostTemplateSettings TemplateSettings
{
get => (NotificationFlyoutHostTemplateSettings)GetValue(TemplateSettingsProperty);
set => SetValue(TemplateSettingsProperty, value);
}
public void HideFlyout()
{
if (_root == null) return;
@@ -46,11 +33,14 @@ namespace NotificationFlyout.Uwp.UI.Controls
flyout.Hide();
}
public void SetOffset(double verticalOffset, double horizontalOffset)
public void SetFlyoutPlacement(string placement)
{
if (_templateSettings == null) return;
_templateSettings.FromVerticalOffset = verticalOffset;
_templateSettings.FromHorizontalOffset = horizontalOffset;
if (!_isLoaded)
{
_placement = placement;
}
VisualStateManager.GoToState(this, placement, true);
}
public void ShowFlyout(FlyoutPlacementMode placementMode)
@@ -60,13 +50,15 @@ namespace NotificationFlyout.Uwp.UI.Controls
flyout.ShowAt(_root, new FlyoutShowOptions
{
Placement = placementMode,
ShowMode = FlyoutShowMode.Standard
});
ShowMode = FlyoutShowMode.Standard,
});
}
protected override void OnApplyTemplate()
{
_root = GetTemplateChild("Root") as Grid;
_isLoaded = true;
SetFlyoutPlacement(_placement);
}
}
}
@@ -7,20 +7,88 @@
<Setter.Value>
<ControlTemplate TargetType="controls:NotificationFlyoutHost">
<Grid x:Name="Root">
<Grid.Resources>
<Style x:Key="DefaultFlyoutPresenterStyle" TargetType="FlyoutPresenter">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="IsDefaultShadowEnabled" Value="False" />
<Setter Property="Padding" Value="0" />
</Style>
<Style
x:Key="TopFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="0,-5,0,0" />
</Style>
<Style
x:Key="BottomFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="0,5,0,0" />
</Style>
<Style
x:Key="LeftFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="-5,0,0,0" />
</Style>
<Style
x:Key="RightFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
</Grid.Resources>
<FlyoutBase.AttachedFlyout>
<Flyout AreOpenCloseAnimationsEnabled="False" ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="IsDefaultShadowEnabled" Value="False" />
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
</Flyout.FlyoutPresenterStyle>
<ContentControl Content="{TemplateBinding FlyoutPresenter}" />
<Flyout
x:Name="Flyout"
AreOpenCloseAnimationsEnabled="False"
FlyoutPresenterStyle="{StaticResource BottomFlyoutPresenterStyle}"
ShouldConstrainToRootBounds="False">
<ContentControl Content="{TemplateBinding FlyoutPresenter}">
<ContentControl.Transitions>
<TransitionCollection>
<EntranceThemeTransition
x:Name="EntranceThemeTransition"
FromHorizontalOffset="0"
FromVerticalOffset="0" />
</TransitionCollection>
</ContentControl.Transitions>
</ContentControl>
</Flyout>
</FlyoutBase.AttachedFlyout>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlacementStates">
<VisualState x:Name="Bottom">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource BottomFlyoutPresenterStyle}" />
<Setter Target="EntranceThemeTransition.FromHorizontalOffset" Value="0" />
<Setter Target="EntranceThemeTransition.FromVerticalOffset" Value="80" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Top">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource TopFlyoutPresenterStyle}" />
<Setter Target="EntranceThemeTransition.FromHorizontalOffset" Value="0" />
<Setter Target="EntranceThemeTransition.FromVerticalOffset" Value="-80" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Left">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource LeftFlyoutPresenterStyle}" />
<Setter Target="EntranceThemeTransition.FromHorizontalOffset" Value="-80" />
<Setter Target="EntranceThemeTransition.FromVerticalOffset" Value="0" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Right">
<VisualState.Setters>
<Setter Target="Flyout.FlyoutPresenterStyle" Value="{StaticResource RightFlyoutPresenterStyle}" />
<Setter Target="EntranceThemeTransition.FromHorizontalOffset" Value="80" />
<Setter Target="EntranceThemeTransition.FromVerticalOffset" Value="0" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
@@ -1,29 +0,0 @@
using Windows.UI.Xaml;
namespace NotificationFlyout.Uwp.UI.Controls
{
public class NotificationFlyoutHostTemplateSettings : DependencyObject
{
public static readonly DependencyProperty FromHorizontalOffsetProperty =
DependencyProperty.Register(nameof(FromHorizontalOffset),
typeof(double), typeof(NotificationFlyoutHostTemplateSettings),
new PropertyMetadata(0d));
public static readonly DependencyProperty FromVerticalOffsetProperty =
DependencyProperty.Register(nameof(FromVerticalOffset),
typeof(double), typeof(NotificationFlyoutHostTemplateSettings),
new PropertyMetadata(0d));
public double FromHorizontalOffset
{
get => (double)GetValue(FromHorizontalOffsetProperty);
set => SetValue(FromHorizontalOffsetProperty, value);
}
public double FromVerticalOffset
{
get => (double)GetValue(FromVerticalOffsetProperty);
set => SetValue(FromVerticalOffsetProperty, value);
}
}
}
@@ -14,11 +14,6 @@
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<Border.Transitions>
<TransitionCollection>
<EntranceThemeTransition FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.FromHorizontalOffset, Mode=TwoWay}" FromVerticalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.FromVerticalOffset, Mode=TwoWay}" />
</TransitionCollection>
</Border.Transitions>
<ScrollViewer
x:Name="ScrollViewer"
AutomationProperties.AccessibilityView="Raw"
@@ -88,10 +88,6 @@ namespace NotificationFlyout.Wpf.UI.Controls
private void OnTaskbarChanged(object sender, EventArgs args)
{
var taskbarState = _taskbarHelper.GetCurrentState();
Left = taskbarState.Screen.WorkingArea.Left;
Top = taskbarState.Screen.WorkingArea.Top;
UpdateWindow();
}
@@ -102,9 +98,9 @@ namespace NotificationFlyout.Wpf.UI.Controls
WindowStyle = WindowStyle.None;
ResizeMode = ResizeMode.NoResize;
AllowsTransparency = true;
Background = new SolidColorBrush(Colors.Transparent);
Height = 5;
Width = 5;
Background = new SolidColorBrush(Colors.Red);
Height = 0;
Width = 0;
}
private void PrepareNotificationIcon()
@@ -139,19 +135,14 @@ namespace NotificationFlyout.Wpf.UI.Controls
var taskbarState = _taskbarHelper.GetCurrentState();
var screen = Screen.FromHandle(this.GetHandle());
MaxHeight = screen.Bounds.Height / 2;
Left = taskbarState.Screen.WorkingArea.Left;
Top = taskbarState.Screen.WorkingArea.Top;
var windowWidth = DesiredSize.Width * this.DpiX();
var windowHeight = DesiredSize.Height * this.DpiY();
double top;
double left;
double height;
double width;
double verticalOffset = 0;
double horizontalOffset = 0;
var windowWidth = 0 * this.DpiX();
var windowHeight = 0 * this.DpiY();
double top, left, height, width;
var taskbarRect = taskbarState.Rect;
switch (taskbarState.Position)
{
@@ -160,35 +151,31 @@ namespace NotificationFlyout.Wpf.UI.Controls
left = taskbarRect.Right;
height = windowHeight;
width = windowWidth;
horizontalOffset = -MaximumOffset;
break;
case TaskbarPosition.Top:
top = taskbarRect.Bottom;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth;
height = windowHeight;
width = windowWidth;
verticalOffset = -MaximumOffset;
break;
case TaskbarPosition.Right:
top = taskbarRect.Bottom - windowHeight;
left = taskbarRect.Left - windowWidth;
height = windowHeight;
width = windowWidth;
horizontalOffset = MaximumOffset;
break;
case TaskbarPosition.Bottom:
top = taskbarRect.Top - windowHeight;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth;
height = windowHeight;
width = windowWidth;
verticalOffset = MaximumOffset;
break;
default:
throw new ArgumentOutOfRangeException();
}
this.SetWindowPosition(top, left, height, width);
flyoutHost.SetOffset(verticalOffset, horizontalOffset);
flyoutHost.SetFlyoutPlacement(taskbarState.Position.ToString());
}
}
}