Skip to content
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

[WIP] Metro/MessageDialogs V2 #785

Merged
merged 29 commits into from
Nov 26, 2013

Conversation

AzureKitsune
Copy link
Member

I am trying to make the dialog system very abstract so that a user can plug in their own dialogs. A major revision of #764.

TODO List:

  • Make every abstract.
  • Implement ProgressDialog
  • Implement SimpleDialog where you can put arbitrary controls in it. ( Suggested by @flagbug )
  • Animations (basic). More animations will come in the next revision.
  • Fix bugs
  • Drink milk. img
  • Make ProgressDialog useable for the general public. Specifically this and return a "remote control" object so you can alter the dialog while its in view.
  • Remove (random/accidential?) clickonce stuff from the .csproj file.
  • Document code.

Screenshots

Message Dialog

md-v2
md-v2-2

Progress Dialog

Note the Indeterminate MetroProgressBar on the bottom of the dialog. The ProgressDialog is inspired by a similar construct in Github for Windows.
pd-v2-1
pd-v2-2
pd-v2-3
pd-v2-4

Simple Dialog

sd

Dark

pd-dark
md-dark

Breaking Changes

  • Dialog methods were moved outside of MetroWindow to extension methods. This was to remove clutter and ugly code from MetroWindow.cs.

@ghost ghost assigned AzureKitsune Nov 7, 2013
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mental note to self: Remove this.

@mcsdodo
Copy link

mcsdodo commented Nov 10, 2013

Will there be any way of showing message dialog with calling static method, just like MessageBox.Show? I can't figure a elegant way how to use this applying mvvm since I don't want to access instance of my view in viewmodel class...

@AzureKitsune
Copy link
Member Author

@mcsdodo Interesting question actually. In my own MVVM framework, I have something called services. I have one service called IMessageBoxService.

From what my implementation, I did:

((MetroWindow)Application.Current.MainWindow).ShowMessage(....

I don't know how that translates to other MVVM frameworks though. I did borrow the Services idea from another framework.

A MessageBox.Show would be interesting to do, however. Probably won't do that until later revisions though.

@michaelmairegger
Copy link
Contributor

@Amrykid . I work with MEF and do quite the same like you for displaying the message box. But I have a problem with modal dialogs. I tried to search for the current active window in order to display the message box on that window

Application.Current.Windows.OfType<Window>().SingleOrDefault(i => i.IsActive) as MetroWindow;

It is working unless i do not minimize the program. If I have a long running operation and want to notify the user after completion and the user minimizes the program then the code above will return null. Do you have any suggestion how to find the active window?

@AzureKitsune
Copy link
Member Author

@xxMUROxx That's an interesting idea. I'll add that for when a window is minimized, it will flash in the taskbar.

EDIT:
@xxMUROxx forgot to answer the question. If it were me, I'd keep track of the last active windows on a stack. Pop the last one off it its closed. Just call the WinAPI FlashWindow method on whatever window is on the stack.

@flagbug
Copy link
Member

flagbug commented Nov 29, 2013

@topvis The overlay makes it more or less to a modal dialog, you can't click anything else in the application until you close the dialog

@topvis
Copy link

topvis commented Nov 29, 2013

@flagbug, a modal dialog not only prevents user from clicking anything else in the application but also holds the program until the modal dialog is closed. What I'm missing is the latter. My code to handle window closing event is as below. The window will just close without waiting for user to click yes or no. I assume it's because the message dialog is opened "Async" so it doesn't hold other things as what modal dialog does.

Private Sub MetroWindow_Closing(sender As Object, e As ComponentModel.CancelEventArgs)
    Dialogs.MetroWindowDialogExtensions.ShowMessageAsync(Me, "INFO", "Save changes?", Dialogs.MessageDialogStyle.AffirmativeAndNegative)
End Sub

@flagbug
Copy link
Member

flagbug commented Nov 29, 2013

@topvis Can you use 'await'?

@flagbug
Copy link
Member

flagbug commented Nov 29, 2013

@topvis or use .Wait()

@topvis
Copy link

topvis commented Nov 29, 2013

@flagbug I tried Dialogs.MetroWindowDialogExtensions.ShowMessageAsync(....).Wait()
It doesn't work. Or do you mean a different "Wait"?

Furthermore, it would be good to have more options for the button style than just "yes and no". e.g., to remind user to save changes would need "yes, no and cancel".

@flagbug
Copy link
Member

flagbug commented Nov 29, 2013

Hm, I'm not yet familiar with the message boxes... summoning @Amrykid , maybe you can help here?

@AzureKitsune
Copy link
Member Author

Using Wait() in this scenario wouldn't help very much since Dialogs in this implementation are controls inside of the Window. Any blocking function will cause the window to lock, making it useless. I'm sure I'll come up with a way to do this, however.

Off-topic: By the way, ShowMessageAsync is an extension method. All you have to do is

Imports MahApps.Metro.Controls.Dialogs

and call it like this:

Me.ShowMessageAsync(.....) where 'Me' is your MetroWindow

@AzureKitsune
Copy link
Member Author

@topvis Found a solution for your issue. It's in C# but it is VERY VERY straight-forward to convert to VB.NET. It uses 'async/await' (built-into .NET 4.5 or you can use it in .NET 4.0 if you have the AsyncCTP installed). It can also be converted to normal Tasks by using '.ContinueWith' instead of 'await'.

private async void MetroWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!this.IsVisible) return; //window is invisible. can't show a message to the user anyway.

            var responseTask = this.ShowMessageAsync("Close?", "Are you sure you want to close this window?", MessageDialogStyle.AffirmativeAndNegative);

            e.Cancel = true;

            var response = await responseTask;

            if (response == MessageDialogResult.Affirmative)
            {
                e.Cancel = false;

                this.Hide(); //hide the window to let "myself" (the window) know that it is safe to close it.

                this.Close();   
            }
        }

@topvis
Copy link

topvis commented Nov 30, 2013

@Amrykid I tried your method. It works. As you said, I have to install the AsyncCTP through NuGet because I'm on .NET 4.0. Thanks!

Do you think it's a good idea to include more button styles like yes/no/cancel; ok/cancel etc. that are defined in MessageBoxButton enum?

Here is another idea, to make the dialog system more abstract, how about allow user to put a UserControl wherever on the "overlay" layer after calling "ShowOverlayAsync"?

@AzureKitsune
Copy link
Member Author

@topvis Your first idea is already there. You can change what the Affirmative and Negative button say. Your second idea is already there as well. Define a 'SimpleDialog' and place your content in it.

@topvis
Copy link

topvis commented Dec 1, 2013

@Amrykid If I use the "SimpleDialog", is there any way to show the Affirmative and Negative button? If I define my own buttons (Yes/No/Cancel), how can I get which button the user clicks on?

What I'm trying to implement is something like below.
savechanges

@AzureKitsune
Copy link
Member Author

Nope, you'll have to handle all that logic yourself. SimpleDialog is an abstraction of MetroDialog and was made for scenarios such as the one depicted in that image.

What I would do is add all of the buttons to your dialog and handle all of their clicked events. When a button is clicked, unhook every button handler and whatever button was clicked is your result.

To show your dialog, call ShowDialogAsync and to hide it... well, HideDialogAsync.

@AzureKitsune
Copy link
Member Author

@topvis Did ^ answer your question?

@dairen
Copy link

dairen commented Dec 3, 2013

hello, thx for this dialog windows.

do you think it's possible to add

Dialog Button:
Yes/No
Yes/No/Cancel
Ok
Ok/Cancel

in the ShowMessageSync ?
do you want that i work on this and i show you ?

@AzureKitsune
Copy link
Member Author

@dairentech At this time, you can only have two buttons.

You can change the OK button to yes by using window.MetroDialogOptions.AffirmativeButtonText. The same for the Cancel button but with NegativeButtonText.

@dairen
Copy link

dairen commented Dec 3, 2013

Ok as you wish

Thx for the answer and well done for this, i like this

De : Amrykid [mailto:notifications@github.com]
Envoyé : mardi 3 décembre 2013 14:28
À : MahApps/MahApps.Metro
Cc : Sébastien BONNET
Objet : Re: [MahApps.Metro] [WIP] Metro/MessageDialogs V2 (#785)

@dairentechhttps://github.com/dairentech At this time, you can only have two buttons.

You can change the OK button to yes by using window.MetroDialogOptions.AffirmativeButtonText. The same for the Cancel button but with NegativeButtonText.


Reply to this email directly or view it on GitHubhttps://github.com//pull/785#issuecomment-29708977.


Aucun virus trouvé dans ce message.
Analyse effectuée par AVG - www.avg.frhttp://www.avg.fr
Version: 2014.0.4259 / Base de données virale: 3629/6886 - Date: 02/12/2013

@AzureKitsune
Copy link
Member Author

@dairentech Thank you for the compliment. A third button will be implemented in the next revision.

@topvis
Copy link

topvis commented Dec 3, 2013

@Amrykid You did answer my question. But it's a little complicated for me as I'm still new to WPF and async stuff. I implemented the dialog in a different way which can also meet my requirement. I just created a Window to mimic your dialog style and show it as a modal dialog by calling ShowDialog. Before show it I call MainWindow's ShowOverlayAsync to show the overlay and call HideOverlayAsync after the dialog is closed. Below is the snapshot.
savechanges

@dairen
Copy link

dairen commented Dec 5, 2013

sorry to bother you but I wanted to know one thing:

MessageDialog has been used for a window.
But if I want to have the same design for a message window appears before the launch of the first application window
Example: a message saying: Database not found ...

how I could use the Dialog controls that you create?

De : Amrykid [mailto:notifications@github.com]
Envoyé : mardi 3 décembre 2013 14:28
À : MahApps/MahApps.Metro
Cc : Sébastien BONNET
Objet : Re: [MahApps.Metro] [WIP] Metro/MessageDialogs V2 (#785)

@dairentechhttps://github.com/dairentech At this time, you can only have two buttons.

You can change the OK button to yes by using window.MetroDialogOptions.AffirmativeButtonText. The same for the Cancel button but with NegativeButtonText.


Reply to this email directly or view it on GitHubhttps://github.com//pull/785#issuecomment-29708977.


Aucun virus trouvé dans ce message.
Analyse effectuée par AVG - www.avg.frhttp://www.avg.fr
Version: 2014.0.4259 / Base de données virale: 3629/6886 - Date: 02/12/2013

@AzureKitsune
Copy link
Member Author

@dairentech Since the Dialog is just a control, you could make your own window and put the dialog inside of it. That can be your dialog.

@dairen
Copy link

dairen commented Dec 5, 2013

I tried this but if i write msg.show() with the dialog sync, i see the dialog but the programm continue to run and run the login windows

And if I write msg.showdialog(), I see the windows but no dialog metro…

I’m not a super c#/WPF guy ☺ ☺

De : Amrykid [mailto:notifications@github.com]
Envoyé : jeudi 5 décembre 2013 22:49
À : MahApps/MahApps.Metro
Cc : Sébastien BONNET
Objet : Re: [MahApps.Metro] [WIP] Metro/MessageDialogs V2 (#785)

@dairentechhttps://github.com/dairentech Since the Dialog is just a control, you could make your own window and put the dialog inside of it. That can be your dialog.


Reply to this email directly or view it on GitHubhttps://github.com//pull/785#issuecomment-29941783.


Aucun virus trouvé dans ce message.
Analyse effectuée par AVG - www.avg.frhttp://www.avg.fr
Version: 2014.0.4259 / Base de données virale: 3658/6893 - Date: 05/12/2013

@AzureKitsune
Copy link
Member Author

You're going to have to jump through hoops in order to get your MainWindow to show after the Dialog closes. You're better off showing your main window and using the dialog inside of it before you load any content.

@robertstefan
Copy link

Ok... so I really need some guidance as a starting point for this version of MetroDialogs. :|

I managed to create a custom MetroDialog with login credentials based on the #550 PR and it worked somehow great [not exactly the way I really intended] so I decided to give this version o shot; but I'm stuck. I can't find a way to start creating a custom design for the content of a MetroDialog.

This is screenshot of my custom MetroDialog (as you have guessed, the login button is on the second step):
connection

If I have to create another project, what type should I chose? Class Library or Custom Control? :(

Somebody, please do enlighten me. :(

@AzureKitsune
Copy link
Member Author

@robertstefan Relax my friend. It is easy as pie.
pie

Depending if you want to make your own dialog or stick your controls into a 'SimpleDialog'.

If you want to make your own dialog...

  1. Right click your project and go to Add New Item -> User Control (WPF). Give it whatever name you want.
  2. Once VS loads your new control in the designer, change it from UserControl to 'BaseMetroDialog'. (Since its an external type like most of MA.M's controls, you're going to have to include its xml namespace.. e.g. the little 'xmlns=bla' at the top.)
  3. In the code behind, change your control from UserControl (or Control?) to BaseMetroDialog. (You will need to import the namespace here too, like usual).
  4. Back in the designer. BaseMetroDialog gives you three ares to add content: The top, the middle and the bottom (ProgressDialog uses this area for it's ProgressBar). Judging by your screenshot, I'd say you would want to put your stuff in the center area.

Below is how I created the ProgressDialog using the exact same process...

<Dialogs:BaseMetroDialog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Controls="clr-namespace:MahApps.Metro.Controls"
                    xmlns:Dialogs="clr-namespace:MahApps.Metro.Controls.Dialogs"
                    xmlns:conv="clr-namespace:MahApps.Metro.Converters"
                    x:Class="MahApps.Metro.Controls.Dialogs.ProgressDialog"
                    Cursor="Wait">
    <Dialogs:BaseMetroDialog.DialogBody>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBlock Margin="0 5 0 0"
                       FontSize="15"
                       Text="{Binding Message, RelativeSource={RelativeSource AncestorType=Dialogs:ProgressDialog, Mode=FindAncestor}, UpdateSourceTrigger=PropertyChanged}"
                       TextWrapping="Wrap"
                       Grid.Row="0"
                       Foreground="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Dialogs:ProgressDialog, Mode=FindAncestor}, UpdateSourceTrigger=PropertyChanged}" />

            <StackPanel Grid.Row="1"
                        Orientation="Horizontal"
                        HorizontalAlignment="Right"
                        Height="85">
                <Button x:Name="PART_NegativeButton"
                        Height="35"
                        MinWidth="80"
                        Style="{DynamicResource AccentedDialogSquareButton}"
                        Content="{Binding NegativeButtonText, RelativeSource={RelativeSource AncestorType=Dialogs:ProgressDialog, Mode=FindAncestor}, UpdateSourceTrigger=PropertyChanged}"
                        Margin="5 0 0 0"
                        Visibility="Hidden" />
            </StackPanel>
        </Grid>
    </Dialogs:BaseMetroDialog.DialogBody>
    <Dialogs:BaseMetroDialog.DialogBottom>
        <!-- omitted. progressbar was here. -->
    </Dialogs:BaseMetroDialog.DialogBottom>
</Dialogs:BaseMetroDialog>

Once you have your dialog defined, you can show it in a window by using window.ShowMetroDialogAsync(dialogClass).

@robertstefan
Copy link

Sweet! I like it where it goes. I simply love this library. And all you guys for the good work. :) 👍
And thanks @Amrykid for the fast reply.

I have one more question:

there should be no problem if I show a BaseMetroDialog and after the user clicks the "next" button to close it and show a dialog with the progress bar, right?

Edit: The button seems to lose the close behavior on the custom window. Am I missing something [the code is exactly as you specified]?

@AzureKitsune
Copy link
Member Author

@robertstefan Check out the metro demo. The MessageDialog demo code and the ProgressDialog demo code show them one after enough.

By default, any button in a Metro Dialog grabs the 'SquareButton' style. That could be your issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

7 participants