New Posts New Posts RSS Feed: Customize DialogHostBase
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Customize DialogHostBase

 Post Reply Post Reply
Author
jbiddle61 View Drop Down
Newbie
Newbie


Joined: 18-Dec-2012
Location: Arizona
Posts: 24
Post Options Post Options   Quote jbiddle61 Quote  Post ReplyReply Direct Link To This Post Topic: Customize DialogHostBase
    Posted: 16-Apr-2013 at 2:38pm
I'm trying to customize the behavior of the DialogManager by sub-classing the existing DialogHostBase and creating a custom view.
The problem that I am running into is that many of the existing DialogHostBase methods and properties are private.
This is making it very difficult to customize.

I'm trying to 'fix' the behavior of the window close button (red X).
Currently, this button will do NOTHING(!) unless you have a cancel button.
If there is no cancel button, I would like to defer to the default button.
If there are neither cancel nor default buttons, I would like it to act as if there were a default button (close the dialog successfully).

Here is the current Cocktail implementation of CanClose:

        /// <summary>
        ///   Called to check whether the dialog host can be closed.
        /// </summary>
        public override void CanClose(Action<bool> callback)
        {
            if (DialogResult != null)
            {
                base.CanClose(callback);
                return;
            }

            var cancelButton = _dialogButtons.FirstOrDefault(x => x.Command.IsCancelCommand);
            if (cancelButton != null && cancelButton.InvokeCommand())
            {
                DialogResult = cancelButton.DialogResult;
                InvokedCommand = cancelButton.Command;
                base.CanClose(callback);
                return;
            }

            callback(false);
        }


Here is what I WANT to do, but can't because the setter (and the backing field) for InvokedCommand are private instead of protected.


        public override void CanClose(Action<bool> callback)
        {
            if (DialogResult != null)
            {
                base.CanClose(callback);
                return;
            }

            var cancelButton = _dialogButtons.FirstOrDefault(x => x.Command.IsCancelCommand);
            if (cancelButton != null)
            {
                if (cancelButton.InvokeCommand())
                {
                    DialogResult = cancelButton.DialogResult;
                    InvokedCommand = cancelButton.Command;
                    base.CanClose(callback);
                    return;
                }
                else
                {
                    callback(false);
                }
            }

            var defaultButton = _dialogButtons.FirstOrDefault(x => x.Command.IsDefaultCommand);
            if (defaultButton != null)
            {
                if (defaultButton.InvokeCommand())
                {
                    DialogResult = defaultButton.DialogResult;
                    InvokedCommand = defaultButton.Command;
                    base.CanClose(callback);
                    return;
                }
                else
                {
                    callback(false);
                }
            }

            callback(true);
        }


I would also like the escape key to invoke the default button if no cancel button is defined.
I am again thwarted because the method I need to override is private!
Exiting code:

        private void OnKeyDown(KeyEventArgs args)
        {
            if (args.Key == Key.Escape && _dialogButtons.Any(x => x.Command.IsCancelCommand))
            {
                var button = _dialogButtons.First(x => x.Command.IsCancelCommand);
                if (!button.Enabled)
                    return;
                Close(button);
            }

            if (args.Key == Key.Enter && _dialogButtons.Any(x => x.Command.IsDefaultCommand))
            {
                var button = _dialogButtons.First(x => x.Command.IsDefaultCommand);
                if (!button.Enabled)
                    return;
                Close(button);
            }
        }


Code I would like to use:

        private void OnKeyDown(KeyEventArgs args)
        {
            if (args.Key == Key.Escape)
            {
                if (DialogButtons.Any(x => x.Command.IsCancelCommand))
                {
                    var button = DialogButtons.First(x => x.Command.IsCancelCommand);
                    if (!button.Enabled)
                        return;
                    Close(button);
                }
                else if (DialogButtons.Any(x => x.Command.IsDefaultCommand))
                {
                    var button = DialogButtons.First(x => x.Command.IsDefaultCommand);
                    if (!button.Enabled)
                        return;
                    Close(button);
                }
            }

            if (args.Key == Key.Enter && DialogButtons.Any(x => x.Command.IsDefaultCommand))
            {
                var button = DialogButtons.First(x => x.Command.IsDefaultCommand);
                if (!button.Enabled)
                    return;
                Close(button);
            }
        }



Am I way off-base in what I'm trying to do?
Is there a better way?

Back to Top
jbiddle61 View Drop Down
Newbie
Newbie


Joined: 18-Dec-2012
Location: Arizona
Posts: 24
Post Options Post Options   Quote jbiddle61 Quote  Post ReplyReply Direct Link To This Post Posted: 19-Apr-2013 at 10:33am
Well, since there has been no response in three days(!), here is what I had to do to get my customizations working.

I had to completely replace the following source file in Cocktail with my own implementations.
In each file I renamed the classes to avoid conflicts and changed some of the
internal
or
private
access modifiers to
public
or
protected
.

Dialog.cs
DialogButton.cs
DialogHostBase.cs
DialogManager.cs
DialogUICommand.cs
IDialogManager.cs
IDialogUICommand.cs

I would have had to bring in PartLocator.cs too, but since I'm not worried about finding other implementations of my custom DialogHostBase and MessageBoxBase, I just asked MEF directly for my implementations

All of this because some of the methods and property setters in DialogHostBase are
private
instead of
protected
and some of the classes and interfaces by it are
internal
!
What a huge pain for such a simple change in behavior!

All I needed was to always have the Windows close button (red X) ALWAYS close the dialog, even if there is no Cancel button.
The fact that the Cocktail implementation does not do this is a BUG in my opinion.

I'm hoping that I don't run into similar problems if/when I need to customize other behavior in Cocktail!

Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 19-Apr-2013 at 10:36pm
Sorry about the trouble and the delay in responding. I'm currently traveling, but I will look into making changes to accommodate better customization of the dialog host. There hasn't been a use case yet to customize it to the level you are doing it. 

The dialog manager is intended to handle the most common cases. You can always completely forgo it and do popups yourself by using the Caliburn.Micro WindowManager directly if you have a use case that doesn't fit. 

Just so I know, are you using Cocktail 2012 or 2010?
Back to Top
jbiddle61 View Drop Down
Newbie
Newbie


Joined: 18-Dec-2012
Location: Arizona
Posts: 24
Post Options Post Options   Quote jbiddle61 Quote  Post ReplyReply Direct Link To This Post Posted: 23-Apr-2013 at 10:02am
I'm using Cocktail 2012.

Here is my current custom DialogHostBase:

    [Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class FSLDialogHostViewModel : DialogHostBase
    {
        protected override void OnViewLoaded(object view)
        {
            ((UIElement)view).KeyDown += (sender, args) => OnKeyDown(args);
        }

        public override void CanClose(Action<bool> callback)
        {
            if (DialogResult != null)
            {
                callback(true);
                return;
            }

            var cancelButton = DialogButtons.FirstOrDefault(x => x.Command.IsCancelCommand);
            if (cancelButton != null)
            {
                invokeButtonCommand(callback, cancelButton);
                return;
            }

            var defaultButton = DialogButtons.FirstOrDefault(x => x.Command.IsDefaultCommand);
            if (defaultButton != null)
            {
                invokeButtonCommand(callback, defaultButton);
                return;
            }

            if (DialogButtons.Any())
            {
                callback(false);
            }
            else
            {
                DialogResult = null;
                InvokedCommand = null;
                callback(true);
            }
        }

        private void invokeButtonCommand(Action<bool> callback, DialogButton button)
        {
            if (button.InvokeCommand())
            {
                DialogResult = button.DialogResult;
                InvokedCommand = button.Command;
                callback(true);
            }
            else
            {
                callback(false);
            }
        }

        private void OnKeyDown(KeyEventArgs args)
        {
            if (args.Key == Key.Escape)
            {
                if (DialogButtons.Any(x => x.Command.IsCancelCommand))
                {
                    var button = DialogButtons.First(x => x.Command.IsCancelCommand);
                    if (button.Enabled)
                        Close(button);
                }
                else if (DialogButtons.Any(x => x.Command.IsDefaultCommand))
                {
                    var button = DialogButtons.First(x => x.Command.IsDefaultCommand);
                    if (button.Enabled)
                        Close(button);
                }
                else if (!DialogButtons.Any())
                {
                    TryClose();
                }
            }
            else if (args.Key == Key.Enter)
            {
                if (DialogButtons.Any(x => x.Command.IsDefaultCommand))
                {
                    var button = DialogButtons.First(x => x.Command.IsDefaultCommand);
                    if (button.Enabled)
                        Close(button);
                }
                else if (!DialogButtons.Any())
                {
                    TryClose();
                }
            }
        }
    }


To get this to compile I had to make the following changes to my local copy of the Cocktail 2.2.2 source:

Changed the setters for DialogHostBase.DialogResult and DialogHoseBase.InvokedCommand from private to protected.
Changed the access of DialogButton.InvokeCommand and DialogButton.DialogResut from internal to public.

Back to Top
cypher View Drop Down
Newbie
Newbie
Avatar

Joined: 26-Nov-2012
Location: Deutschland
Posts: 20
Post Options Post Options   Quote cypher Quote  Post ReplyReply Direct Link To This Post Posted: 12-Jun-2013 at 5:07am
Great.. i will have to customize my implementation two.. we are running a multi language interface, and alle buttons need to be translated. currently there is no way to translate the buttons without writing a overcomplex overload:

 public async Task<QuestionDialogResult> ShowQuestionAsync(
            string message, 
            string title = null)
        {
            var questionDialogResult = default(QuestionDialogResult);
            var dialogResult = string.Empty;

            try
            {
                dialogResult =
                    await _manager.ShowMessageAsync(message, new List<string> { Resources.Dialog_Yes, Resources.Dialog_No }, title);
            }
            catch (TaskCanceledException)
            {
                questionDialogResult = QuestionDialogResult.No;
            }

            if (dialogResult.Equals(Resources.Dialog_Yes))
                questionDialogResult = QuestionDialogResult.Yes;

            if (dialogResult.Equals(Resources.Dialog_No))
                questionDialogResult = QuestionDialogResult.No;

            _logger.Info(
                string.Format(
                    "Displayed Question: --> {0} <-- to User: {1}, he choosed option: {2}", 
                    message, 
                    Thread.CurrentPrincipal.Identity.Name, 
                    questionDialogResult));

            return questionDialogResult;
        }

i really don't like to write so much code, for a very simple translation. 
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 12-Jun-2013 at 10:15am
cypher,
The upcoming Cocktail v2.4 release will have jiddle61's concerns addressed. Having said that, as far as translation goes, all you have to do is create satellite resources dlls for Cocktail with the translated strings and the standard buttons will automatically be translated. Cocktail is fully localizeable. It follows the standard .NET localization approach. We just don't ship multiple languages out of the box.
Back to Top
cypher View Drop Down
Newbie
Newbie
Avatar

Joined: 26-Nov-2012
Location: Deutschland
Posts: 20
Post Options Post Options   Quote cypher Quote  Post ReplyReply Direct Link To This Post Posted: 13-Jun-2013 at 3:20am
Thanks for the hint. i wasn't aware, that there is a chanse to localize the buttons. This is the first time i hear about satellite resources, and i have never implemented it so far.

i looked @ http://msdn.microsoft.com/en-us/library/sb6a8618.aspx but, that didn't help so much. 

I also read the http://drc.ideablade.com/devforce-2012/bin/view/Documentation/cocktail-dialog-manager section, and there isn't even a hint, that you can translate the default cocktail impelmentation of the buttons.

It would be really nice, if there is a small section for globalisation, localisation within the cocktail documentation, like it is in the devforce documentation. (localisation of validatoin error messages) http://drc.ideablade.com/devforce-2012/bin/view/Documentation/validate-localizing-error-messages

What do i have to do, to implement a satellite resource for the cocktail translations within my assembly (just give it the same namespace?)

thanks for your time and help
Back to Top
jbiddle61 View Drop Down
Newbie
Newbie


Joined: 18-Dec-2012
Location: Arizona
Posts: 24
Post Options Post Options   Quote jbiddle61 Quote  Post ReplyReply Direct Link To This Post Posted: 10-Jul-2013 at 3:57pm
Thanks for integrating these changes in Cocktaion 2.4!
Works perfectly!
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 10-Jul-2013 at 4:14pm
You're welcome.
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 10-Jul-2013 at 4:15pm
Originally posted by cypher

Thanks for the hint. i wasn't aware, that there is a chanse to localize the buttons. This is the first time i hear about satellite resources, and i have never implemented it so far.

i looked @ http://msdn.microsoft.com/en-us/library/sb6a8618.aspx but, that didn't help so much. 

I also read the http://drc.ideablade.com/devforce-2012/bin/view/Documentation/cocktail-dialog-manager section, and there isn't even a hint, that you can translate the default cocktail impelmentation of the buttons.

It would be really nice, if there is a small section for globalisation, localisation within the cocktail documentation, like it is in the devforce documentation. (localisation of validatoin error messages) http://drc.ideablade.com/devforce-2012/bin/view/Documentation/validate-localizing-error-messages

What do i have to do, to implement a satellite resource for the cocktail translations within my assembly (just give it the same namespace?)

thanks for your time and help

Sorry, I forgot to respond to this. See the following link for how to create a satellite assembly. We haven't had many globalization requests for Cocktail yet, but I agree a section in the documentation would be helpful. 

Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down