This project is read-only.

Setting panel

Aug 29, 2012 at 7:17 AM

Is there any plans to have settings panel integration? At the moment I'm just using Callisto and hand rolling everything but it'd be pretty cool to have it all in the framework.

Aug 29, 2012 at 5:32 PM

Coincidentally not only have I recently been prototyping support of the settings pane in my own apps (I tend to prototype ideas in my own apps, then migrate them to full implementations in Okra when I get my head around it), but I've had another user contact me in the last week with the same question. I was thinking of having this as the next feature set for Okra and I'll add you as a +1 for this feature!

The plan is to allow developers to be able to define the settings pane view and view-models exactly as they would for any other page, and Okra will handle displaying them as part of the settings pane (with its own navigation stack). Nothing new to learn, and all the same MVVM goodness.

Probably a few weeks before I get chance to get this released, but watch this space...

Andy

Aug 30, 2012 at 7:12 AM

Sweet, I thin it's a pretty important feature, particularly since the store requires you to have at least a single settings page which contains contact information it's something every app will need (but whether or not that's really needing a full view model is up for debate :P)

Sep 17, 2012 at 2:45 AM

I've done a *really* simple implementation of this built on top of the Callisto SettingsFlyout control but ultimately I don't think it's the best solution as it lacks a lot of features (aka - it solved my problem but not the overall problem).

Here's how I'd go about it if I was doing something more generic (also combines some of the stuff I used, oh and it assumes MEF :P):

  • Have a SettingsFlyout built into Okra (this is needed since Callisto currently doesn't expose the Back event) that developers don't create, Okra creates it for them
  • Two attributes, SettingsPaneExport (for the View) and SettingsViewModelExport (for the ViewModel)
  • Create a new implementation of INavigationTarget called say SettingsNavigationTarget that creates an instance of the above SettingsFlyout and wires up the View/ ViewModel
  • Create another NavigationManager which inherits the existing NavigationManager but consumes the SettingsNavigationTarget
  • When a Back event fires it will navigate backwards through the Settings stack and if there's not it'll just show the settings pane
    • There'd need to be some way to detect when the settings panel is dismissed by the user just tapping away from it, not 100% sure how to do that, I'm sure Callisto does it though so that'd be a good starting point. When that event fires you'd clear out the Settings NavigationManager back stack
  • SettingsPaneExportAttribute should export a HideFromCharms property to allow you to have settings that can only be programmatically opened (essentially it's just opening a user control that looks like a settings pane)
  • In the Settings NavigationManager would have to implement IActivationHandler so that it can attach to the SettingsPane.GetForCurrentView().CommandsRequested event as that's the earliest time you can wire up the event handler (see http://www.aaron-powell.com/xaml/settings-suck)
  • Have a new interface ISettingsHandler which a View/ ViewModel can implement so that it can add settings panes that are specific to that View (so you can have contextual settings panes). This would just return an IEnumerable<string> that contains the names of the settings panes to add for just that page. It does require adding/ removing event handlers for a specific View which would become tricky

And that does my 2c :P

Sep 19, 2012 at 12:57 PM

+1 for this feature.

I was about to fork the project and knock something up so I could at least use MEF to give me a settings page with which I could at least do the wire up for manually.  Okra doesn't have an easy way to ask for a page directly without also triggering a navigation which is a shame.

Sep 26, 2012 at 1:10 PM

Back from holidays so hope to have more to say around here again...

@slace: Good to hear that you managed to get what you needed for the moment. Settings support is currently my #1 feature I'm working on so hopefully some official Okra support will start being released soon.

Thanks too for the list of ideas - always good to get another viewpoint on things. My current thinking (but up for discussion!) is,

  • SettingsFlyout in Okra - Yes - Currently in the private build, although I have factored this into a generic FlyoutPane and a SettingsChrome for flexibility.
  • New settings view/VM attributes & HideFromCharms property - This approach is close to the first prototype I worked on although I soon moved away from this. I initially had attributes that automatically added items to the settings charm. There are a number of disadvantages with this - e.g. the order of items in the list should be constant whereas from IoC they can be random, settings names should be localised whilst view names are constant. In the end I realised I'd be just rewriting the Win8 charms handling. My current approach is to let the app dev add items to the settings charm using the Win8 methods (I may add some simple helpers), then call a SettingsPaneManager.NavigateTo("XxxSettings") method after which Okra takes over to display the settings flyout, etc.
  • New NavigationTarget/Manager - Yes - Currently the private build has refactored much of the navigation into a NavigationBase class with an additional SettingsPaneManager.
  • Handling of the "Back" event - Yes - Get most of this free with the new NavigationBase, and will behave as you describe (NB: Closing when tapping away is easy - set Popup.IsLightDismissEnabled and watch for the Closed event - think those are the right names).
  • Contextual settings list based upon current view - This is against the UX guidelines which specify that the list of settings panes should always be constant (see here).

Andy

Sep 26, 2012 at 1:11 PM

@rbanks54: Thanks for the support for this feature. Regarding getting a page directly you can always import the IViewFactory and use this - the MEF implementation handles all the view/VM discovery and wireing up.

Sep 27, 2012 at 7:53 AM

Cool, I'll be waiting to see how it goes and give feedback where I can.

And you know you can create a branch then push to that in your master repository and open a pull request yourself (against your own repository), basically implementing this - http://scottchacon.com/2011/08/31/github-flow.html

That way it wouldn't be hidden and we could bug you more about it :P

Sep 27, 2012 at 9:46 PM

I've just pushed the first preview of the Okra settings support into the CodePlex source repository. Note that this currently doesn't contain the SettingsChrome UI for the settings but I'll get that in soon. I've also added some extension methods for INavigationBase (new base class of INavigationManager) that return various types of commands to consume in view-models etc.

To give you an idea how to set-up the settings support I am doing this currently in the AppBootstrapper as follows,

public class AppBootstrapper : OkraBootstrapper
{

        [Import]
        public ISettingsPaneManager SettingsPaneManager { get; set; }

        protected override void SetupServices()
        {
            // Attach to the settings pane

            SettingsPane.GetForCurrentView().CommandsRequested += SettingsPane_CommandsRequested;

            ResourceLoader resourceLoader = new ResourceLoader();
        }

        void SettingsPane_CommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
        {
            ResourceLoader resourceLoader = new ResourceLoader();

            args.Request.ApplicationCommands.Add(SettingsPaneManager.GetNavigateToSettingsCommand(resourceLoader.GetString("GeneralSettingsCommandLabel"), "Settings"));
            args.Request.ApplicationCommands.Add(SettingsPaneManager.GetNavigateToSettingsCommand(resourceLoader.GetString("AccountsCommandLabel"), "Accounts"));
            args.Request.ApplicationCommands.Add(SettingsPaneManager.GetNavigateToSettingsCommand(resourceLoader.GetString("AboutCommandLabel"), "About"));
        }
}

The "Settings", "Accounts" and "About" pages are view/viewmodel combinations exported in the usual way. Let me know what you think of this implementation...

Regards,

    Andy

Sep 27, 2012 at 11:53 PM

Cool, I'll have a look at it today.

But back on the automatic creation of the charms list, the way I am going about this (handling ordering and naming) is:

  • Having an order property on the Export attribute, the items are ordered by that and then alphabetically (so if you don't specify an order it'll be alphabetised anyway)
  • The charm text is convention based on the page name then from the resource dictionary, I make the assumption that it's PageName + CommandText
Oct 1, 2012 at 12:54 PM

Yep - That will work.

In fact it could still sit as a separate component alongside the current Okra settings support. Since you can use a list of Lazy or ExportFactory imports in MEF this could enumerate all metadata for the views & view-models, determine if the specified view/VM implemented the SettingsExport, and add the relevate items to the settings pane. If they were Lazy imports then you wouldn't even need to instantiate any of the objects (may be more difficult to do in an IoC-agnostic way).

It all seems a little over-engineered to me however. Is there a particular reason you want to use attribute based discovery of settings panes? Adding it by hand only takes one event handler and a line for each entry. For large enterprise apps where multiple teams are working together I can see the value here, but Win8 apps are much smaller and self-contained.

Andy

PS. I'm trying to push forward my own app development for the next couple of weeks to try to publish prior to Win8 release so haven't had chance to get the SettingsChrome class into the Okra codebase - to give you an idea it's just a ContentControl that exposes Title, Icon and BackButtonCommand properties that link to the relevant places in the template.

Feb 13, 2013 at 1:48 AM
There is also those flyouts, any MVVM way to handle these with Okra?
http://msdn.microsoft.com/en-us/library/windows/apps/hh465341.aspx
Feb 14, 2013 at 1:18 PM
Hi,

Okra doesn't have any specific support for flyouts such as these, however they are fairly easy to implement within an Okra application. The first question really is the depth of MVVM support you require in your flyouts. I tend to think of this as three levels of increasing complexity,
  • Reuse of parent page's view-model - In this case all you need to an additional view that is displayed within a popup control, and any binding/commands are present in the page view model. In fact this would be suitable for all the examples in the link you mentioned. For example the "You are about to delete all 200 photos in your album" flyout would simply bind to a NumberOfPhotos property and a DeletePhotos command - no need for a specific view-model.
  • A simple "wrapper" view-model - Whilst I find the above suitable for most circumstances, there are some occasions where a light-weight view-model is required. In this case I still tend to keep the bulk of the logic in the page view-model still, however I have a separate view-model for the flyout that takes the parent view-model as a constructor. My view is normally a UserControl which will do something like the following in the OnLoaded event (this does create some tight coupling between the view and VM, but I'm not averse to this in simple cases like this),
private void OnLoaded(...)
{
    this.DataContext = new MyFlyoutViewModel(this.DataContext);
}
  • Full MVVM support - At the extreme you could fully define a view and view-model, label them with [PageExport(...)] and [ViewModelExport(...)], then use Okra to create them for display in a flyout. This would take a bit more work and I have never needed this level of functionality.
In general I find the first approach works most of the time, with the second option if I really have to. What do you think for your use-case?

I have some controls I use in my own projects that I'll see if I can share and I'll try to copy some sample code to exemplify some of this also, but will get back to you on that.

Andy
Feb 19, 2013 at 1:40 PM
Edited Feb 19, 2013 at 1:40 PM
Hi again,

I've pulled out an example of a "wrapper" view-model from source control to give you an idea of how I manage this for a flyout. In this case my main page is for browsing a particular folder in a file system, and has an associated BrowseFolderViewModel. When the user clicks a "New folder" button then a flyout appears with two fields - title and description. These two properties are combined into a NewFolderArgs object that is syncronised with the corresponding properties in a NewFolderViewModel. The NewFolderArgs property then can be bound to the CommandParameter of the button in the UI. Note that if there was only a title property required (i.e. no description) then we wouldn't even need a NewFolderViewModel. Instead the flyout would inherit the parent BrowseFolderViewModel and bind the CommandParameter directly to the corresponding TextBox.
    public class NewFolderViewModel : NotifyPropertyChangedBase
    {
        // *** Fields ***

        private readonly BrowseFolderViewModel parentViewModel;

        private string title = "New folder";
        private string description;

        // *** Constructor ***

        public NewFolderViewModel(BrowseFolderViewModel parentViewModel)
        {
            this.parentViewModel = parentViewModel;
        }

        // *** Properties ***

        public string Description
        {
            get
            {
                return description;
            }
            set
            {
                SetProperty(ref description, value);
                OnPropertyChanged(() => NewFolderArgs);
            }
        }

        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                SetProperty(ref title, value);
                OnPropertyChanged(() => NewFolderArgs);
            }
        }

        public NewFolderArgs NewFolderArgs
        {
            get
            {
                return new NewFolderArgs(Title, Description);
            }
        }

        // *** Commands ***

        public ICommand NewFolderCommand
        {
            get
            {
                return parentViewModel.NewFolderCommand;
            }
        }
    }
Then in the view code-behind I wrap the parent view-model and assign the NewFolderViewModel as the new DataContext,
    public sealed partial class NewFolderFlyout : UserControl
    {
        public NewFolderFlyout()
        {
            this.InitializeComponent();
            this.Loaded += OnLoaded;
        }

        void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = new NewFolderViewModel((BrowseFolderViewModel)this.DataContext);
        }
    }
I does impose some tight-coupling between the view and view-model, but for simple instances like this with very little extra logic it is simple and works well.

Andy