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" x:Class="NotificationFlyout.Sample.NotificationFlyoutPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:NotificationFlyout.Uwp.UI.Controls" xmlns:controls="using:NotificationFlyout.Uwp.UI.Controls">
CornerRadius="4">
<Grid> <Grid>
<ListBox <ListBox
Width="300" Width="300"
@@ -122,7 +122,6 @@
<ItemGroup> <ItemGroup>
<Compile Include="NotificationFlyoutHost\NotificationFlyoutHost.cs" /> <Compile Include="NotificationFlyoutHost\NotificationFlyoutHost.cs" />
<Compile Include="NotificationFlyoutPresenter\NotificationFlyoutPresenter.cs" /> <Compile Include="NotificationFlyoutPresenter\NotificationFlyoutPresenter.cs" />
<Compile Include="NotificationFlyoutHost\NotificationFlyoutHostTemplateSettings.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\NotificationFlyout.Uwp.UI.Controls.rd.xml" /> <EmbeddedResource Include="Properties\NotificationFlyout.Uwp.UI.Controls.rd.xml" />
</ItemGroup> </ItemGroup>
@@ -11,20 +11,13 @@ namespace NotificationFlyout.Uwp.UI.Controls
typeof(NotificationFlyoutPresenter), typeof(NotificationFlyoutHost), typeof(NotificationFlyoutPresenter), typeof(NotificationFlyoutHost),
new PropertyMetadata(null)); new PropertyMetadata(null));
public static readonly DependencyProperty TemplateSettingsProperty = private bool _isLoaded;
DependencyProperty.Register(nameof(TemplateSettings), private string _placement;
typeof(NotificationFlyoutHostTemplateSettings), typeof(NotificationFlyoutHost),
new PropertyMetadata(null));
private readonly NotificationFlyoutHostTemplateSettings _templateSettings;
private Grid _root; private Grid _root;
public NotificationFlyoutHost() public NotificationFlyoutHost()
{ {
DefaultStyleKey = typeof(NotificationFlyoutHost); DefaultStyleKey = typeof(NotificationFlyoutHost);
_templateSettings = new NotificationFlyoutHostTemplateSettings();
SetValue(TemplateSettingsProperty, _templateSettings);
} }
public NotificationFlyoutPresenter FlyoutPresenter public NotificationFlyoutPresenter FlyoutPresenter
@@ -33,12 +26,6 @@ namespace NotificationFlyout.Uwp.UI.Controls
set => SetValue(FlyoutPresenterProperty, value); set => SetValue(FlyoutPresenterProperty, value);
} }
public NotificationFlyoutHostTemplateSettings TemplateSettings
{
get => (NotificationFlyoutHostTemplateSettings)GetValue(TemplateSettingsProperty);
set => SetValue(TemplateSettingsProperty, value);
}
public void HideFlyout() public void HideFlyout()
{ {
if (_root == null) return; if (_root == null) return;
@@ -46,11 +33,14 @@ namespace NotificationFlyout.Uwp.UI.Controls
flyout.Hide(); flyout.Hide();
} }
public void SetOffset(double verticalOffset, double horizontalOffset) public void SetFlyoutPlacement(string placement)
{ {
if (_templateSettings == null) return; if (!_isLoaded)
_templateSettings.FromVerticalOffset = verticalOffset; {
_templateSettings.FromHorizontalOffset = horizontalOffset; _placement = placement;
}
VisualStateManager.GoToState(this, placement, true);
} }
public void ShowFlyout(FlyoutPlacementMode placementMode) public void ShowFlyout(FlyoutPlacementMode placementMode)
@@ -60,13 +50,15 @@ namespace NotificationFlyout.Uwp.UI.Controls
flyout.ShowAt(_root, new FlyoutShowOptions flyout.ShowAt(_root, new FlyoutShowOptions
{ {
Placement = placementMode, Placement = placementMode,
ShowMode = FlyoutShowMode.Standard ShowMode = FlyoutShowMode.Standard,
}); });
} }
protected override void OnApplyTemplate() protected override void OnApplyTemplate()
{ {
_root = GetTemplateChild("Root") as Grid; _root = GetTemplateChild("Root") as Grid;
_isLoaded = true;
SetFlyoutPlacement(_placement);
} }
} }
} }
@@ -7,20 +7,88 @@
<Setter.Value> <Setter.Value>
<ControlTemplate TargetType="controls:NotificationFlyoutHost"> <ControlTemplate TargetType="controls:NotificationFlyoutHost">
<Grid x:Name="Root"> <Grid x:Name="Root">
<FlyoutBase.AttachedFlyout> <Grid.Resources>
<Flyout AreOpenCloseAnimationsEnabled="False" ShouldConstrainToRootBounds="False"> <Style x:Key="DefaultFlyoutPresenterStyle" TargetType="FlyoutPresenter">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="BorderThickness" Value="0" /> <Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" /> <Setter Property="Background" Value="Transparent" />
<Setter Property="IsDefaultShadowEnabled" Value="False" /> <Setter Property="IsDefaultShadowEnabled" Value="False" />
<Setter Property="Padding" Value="0" /> <Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style> </Style>
</Flyout.FlyoutPresenterStyle> <Style
<ContentControl Content="{TemplateBinding FlyoutPresenter}" /> 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
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> </Flyout>
</FlyoutBase.AttachedFlyout> </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> </Grid>
</ControlTemplate> </ControlTemplate>
</Setter.Value> </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}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"> 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 <ScrollViewer
x:Name="ScrollViewer" x:Name="ScrollViewer"
AutomationProperties.AccessibilityView="Raw" AutomationProperties.AccessibilityView="Raw"
@@ -88,10 +88,6 @@ namespace NotificationFlyout.Wpf.UI.Controls
private void OnTaskbarChanged(object sender, EventArgs args) private void OnTaskbarChanged(object sender, EventArgs args)
{ {
var taskbarState = _taskbarHelper.GetCurrentState();
Left = taskbarState.Screen.WorkingArea.Left;
Top = taskbarState.Screen.WorkingArea.Top;
UpdateWindow(); UpdateWindow();
} }
@@ -102,9 +98,9 @@ namespace NotificationFlyout.Wpf.UI.Controls
WindowStyle = WindowStyle.None; WindowStyle = WindowStyle.None;
ResizeMode = ResizeMode.NoResize; ResizeMode = ResizeMode.NoResize;
AllowsTransparency = true; AllowsTransparency = true;
Background = new SolidColorBrush(Colors.Transparent); Background = new SolidColorBrush(Colors.Red);
Height = 5; Height = 0;
Width = 5; Width = 0;
} }
private void PrepareNotificationIcon() private void PrepareNotificationIcon()
@@ -139,18 +135,13 @@ namespace NotificationFlyout.Wpf.UI.Controls
var taskbarState = _taskbarHelper.GetCurrentState(); var taskbarState = _taskbarHelper.GetCurrentState();
var screen = Screen.FromHandle(this.GetHandle()); Left = taskbarState.Screen.WorkingArea.Left;
MaxHeight = screen.Bounds.Height / 2; Top = taskbarState.Screen.WorkingArea.Top;
var windowWidth = DesiredSize.Width * this.DpiX(); var windowWidth = 0 * this.DpiX();
var windowHeight = DesiredSize.Height * this.DpiY(); var windowHeight = 0 * this.DpiY();
double top; double top, left, height, width;
double left;
double height;
double width;
double verticalOffset = 0;
double horizontalOffset = 0;
var taskbarRect = taskbarState.Rect; var taskbarRect = taskbarState.Rect;
switch (taskbarState.Position) switch (taskbarState.Position)
@@ -160,35 +151,31 @@ namespace NotificationFlyout.Wpf.UI.Controls
left = taskbarRect.Right; left = taskbarRect.Right;
height = windowHeight; height = windowHeight;
width = windowWidth; width = windowWidth;
horizontalOffset = -MaximumOffset;
break; break;
case TaskbarPosition.Top: case TaskbarPosition.Top:
top = taskbarRect.Bottom; top = taskbarRect.Bottom;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth; left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth;
height = windowHeight; height = windowHeight;
width = windowWidth; width = windowWidth;
verticalOffset = -MaximumOffset;
break; break;
case TaskbarPosition.Right: case TaskbarPosition.Right:
top = taskbarRect.Bottom - windowHeight; top = taskbarRect.Bottom - windowHeight;
left = taskbarRect.Left - windowWidth; left = taskbarRect.Left - windowWidth;
height = windowHeight; height = windowHeight;
width = windowWidth; width = windowWidth;
horizontalOffset = MaximumOffset;
break; break;
case TaskbarPosition.Bottom: case TaskbarPosition.Bottom:
top = taskbarRect.Top - windowHeight; top = taskbarRect.Top - windowHeight;
left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth; left = FlowDirection == FlowDirection.RightToLeft ? taskbarRect.Left : taskbarRect.Right - windowWidth;
height = windowHeight; height = windowHeight;
width = windowWidth; width = windowWidth;
verticalOffset = MaximumOffset;
break; break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
this.SetWindowPosition(top, left, height, width); this.SetWindowPosition(top, left, height, width);
flyoutHost.SetOffset(verticalOffset, horizontalOffset); flyoutHost.SetFlyoutPlacement(taskbarState.Position.ToString());
} }
} }
} }