- Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(InAppNotification): first version of the control #1343
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(InAppNotification): first version of the control #1343
Conversation
Should this follow the ContentDialog pattern and have PrimaryButton* and SecondaryButton*? |
Should this be a type of Flyout? eg: NotificationFlyout |
I would expect a control like this to be similar to Toastr |
@skendrot I don't know. It could have one button, two buttons, three buttons, no button, images, whatever. I suppose we are replicating the behavior of Toast notification in some sort of way. |
Yes, looks pretty much like a Flyout but not sure we can inherit from it. |
Flyout is not sealed and has a public ctor, so shouldn't be a problem |
You can perfectly create almost anything that you see in toastr. Like I said, my implementation came from Microsoft Edge in-app notification. |
What do you mean? I don't see a way to say it's a warning message or to set the duration of the notification or many other features |
@skendrot You can update properties the way you want :
Of course, these features are not natives and devs have to deal with it. Unless someone have a different opinion. :) |
Not exactly what I was referring too 😆 |
One question: Can we add DropShadow on a Flyout? Or ContentDialog? |
For a flyout you define the flyout presenter. I'm sure you could do it in there. |
@nmetulev It should work now. |
@Odonno I found two issues
|
|
@Odonno I think you can add |
@Vijay-Nirmal I would like a Property but not sure how to do it. The I can add a description on how to change the animation using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed the animation does not run the first time the control is shown, it just becomes visible, could you take a look?
I tried the sample code (with template) from the code bind file in a blank new app and the app crashed. I generated the nugets and used them in the app to test.
} | ||
| ||
_dismissButton = (Button)GetTemplateChild(DismissButtonPart); | ||
_dismissButton.Visibility = ShowDismissButton ? Visibility.Visible : Visibility.Collapsed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this move in the null check block below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That might be better. :)
</VisualStateGroup> | ||
</VisualStateManager.VisualStateGroups> | ||
| ||
<Grid x:Name="RootGrid" SizeChanged="RootGrid_SizeChanged"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the SizeChanged event?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not remember, I think this is a old version. Will update it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I remember. I made it that way to be able to detect the change of the Grid Width (not the App Width) so I can create a StateTrigger based on the container Size, not the App Size.
I do not know if there is a better option because it will crash if you do not have this code in your code-behind:
public bool IsRootGridActualWidthLargerThan700 { get; set; } private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e) { // When the root part size of the In App Notification template changed, we should apply VisualState bool newValue = e.NewSize.Width > 700; if (IsRootGridActualWidthLargerThan700 != newValue) { IsRootGridActualWidthLargerThan700 = newValue; OnPropertyChanged(nameof(IsRootGridActualWidthLargerThan700)); } }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that required for an example? Should it work without it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, the DataTemplate
available in the bind code file is a specific template that handle responsive design based on the container Width. And to make it work, I need a property that tell what StateTrigger I should use, in this case I used a boolean in the code behind.
Of course, it is not the default behavior and you can perfectly use the control without extra code :
<controls:InAppNotification x:Name="ExampleInAppNotification" />
And then use .ShowAsync()
method.
My objective with the DataTemplate
was to provide a more complete example that mimic MS Edge notification with buttons (Horizontal Orientation when Width > 700px, Vertical Orientation otherwise).
Should I remove this template from the code file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thoughts on this are to have a simple template that doesn't complicate the mechanics of the control and shows the minimum needed to try the control. We could always have both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nmetulev I can leave the complete Templates in the page (with buttons, with DropShadow, etc..) and add a simple template in the code file. I'll update it.
@nmetulev Another question: Is it interesting and how can we integrate Styles (like the vscode-like notification template) so developers can reuse them without copying the code everytime in their apps? My long-term goal would be to gather some styles devs are used to work with (like MS Edge-like, vscode-like and some more to come). |
@Odonno, I agree, it would be interesting to provide common style after the control is available to see what requests devs have |
| ||
## Syntax | ||
| ||
The control should be placed where you want your notification to be displayed in the page, generally in the root grid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should tell the dev to put this control in the Bottom of the grid because If they put at the top other controls will overlay this control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or use the ol' Canvas.ZIndex="99999"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or int.MaxValue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Canvas.ZIndex has a limit below Int.MaxValue, possibly 16 Bit, I remember, because once I entered a few too many 9s. Unless I added 11 9s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Vijay-Nirmal By default, the position is set to Bottom because I mimic the original component from MS Edge. But of course, you can place it at the Top or at the Center, it depends on the dev intention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Odonno What do you mean by "By default, the position is set to Bottom"?
I think we can set Canvas.ZIndex
by default because Notification will always be displayed about all controls. If devs want to change it they can easily change it by setting Canvas.ZIndex
to 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Vijay-Nirmal If you put the control at the last position of the parent container, it will be have priority over other controls, if you do not use Canvas.ZIndex
. I can add this to the description of the control if you think it is relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Odonno I think we should either say it in Doc or set a default Canvas.ZIndex
value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can always add a note in the docs if that will make things easier. How about:
Note: Since the control is part of the page visual tree, it will render in the order it was added in the parent control, and might be hidden by other elements. For the control to render on top of other elements, add it as the last child of the parent control or set the Canvas.ZIndex to a high number.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Will review this |
DefaultStyleKey = typeof(InAppNotification); | ||
} | ||
| ||
protected override void OnApplyTemplate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing documentations /// <inheritdoc />
/// <param name="text">Text used as the content of the notification</param> | ||
/// <param name="duration">Displayed duration of the notification in ms (less or equal 0 means infinite duration)</param> | ||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||
public async Task ShowAsync(string text, int duration = 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function could be done this way
public Task ShowAsync(string text, int duration = 0) { ContentTemplate = null; Content = text; return ShowAsync(duration); }
It's better because you're giving more control to the caller when to await instead of forcing the await in your function.
also this is applied to the rest of ShowAsync
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
except of the first ShowAsync :) you can't do that to it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fair.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The on applyTemplate is missing the documentations.
regarding the async functions feel free to do it or not :) but it's just a better approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excited to see this first version go in. I think if we want the new sample app to work we'll need the opened event though.
<TextBlock Text="*if duration is less or equal 0, the notification will never be dismissed" /> | ||
</StackPanel> | ||
| ||
<Button x:Name="ShowNotificationWithRandomTextButton" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These types of buttons in the sample should instead be registered as commands with Shell.Current.RegisterNewCommand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how to do this. From what sample control should I take inspiration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at this. It simply adds a button in the shell instead of having to add one in your sample.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I'm on it.
ExampleVSCodeInAppNotification.Dismiss(); | ||
SetDefaultControlTemplate(); | ||
| ||
var grid = new Grid(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add this as a template like the other examples?
This is a large example and templates work a lot better in the new live XAML world.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to show different examples : so there is one with XAML DataTemplate and another one using C#.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
People using the app won't see this code though, should it be in the .bind Code File instead?
| ||
<StackPanel Grid.Column="2" Orientation="Horizontal"> | ||
<Button Content="Action 1" | ||
Click="Action1Button_Click" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to think how this click event will work in the new sample app...
Since these are more geared to individual notifications vs a central service, I'd like to see an Opened/Opening event like FlyoutBase has. Then we can grab the buttons in that event and register handlers for the sample app.
/// <summary> | ||
/// In App Notification defines a control to show local notification in the app. | ||
/// </summary> | ||
public partial class InAppNotification |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we also have events for Opened/Opening/Closed/Closing like ContentDialog / FlyoutBase?
I would see closed/closing fire whenever it disappears (including dismiss or timeout) vs. the Dismiss event which fires when the user explicitly dismisses the notification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to figure out how to inherits from Flyout/FlyoutPresenter. For the dismiss event, I can easily add an argument whether the user clicked on a button or if timeout.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think there is a need to inherit from Flyout. You can add custom events. PullToRefeshListView events come to mind as an example.
@nmetulev I leave it to you. |
Linked to #1080
First version of the In App Notification control. The control is very flexible : devs can update Background color, Border, Padding, Margin. By default, the control looks like the Microsoft Edge in-app notification.
Need more things before merging :
DataTemplate
Add some default DataTemplates (just Text, Yes/No buttons, ...) and should be responsive (Adaptative design)EDIT : cannot make default DataTemplate because they require code-behind variables (text value, click event or command button)