C#, Metro, UI, WinRT

TCD.Controls – Flyouts for XAML/C#

Hey there,

Let’s start with the most important news:

TCD.Controls is now available on NuGet (click here if you don’t know how this works, or why you want to use it)

From now on, I won’t link *.zip packages with the sources anymore, because NuGet really make life easier. If you want access to the source files, just drop me an email Zwinkerndes Smiley


There’s been one more or less important change to the Flyout control you should know about: It now has a property called “IsIgnoreLightDismissal”. To understand its purpose, imagine the following scenario (video below)

You have a Button control that opens a FilePicker/FolderPicker, asking the user to select a File/Folder. This Button is hosted inside a Flyout control.

Now when the user taps the Button, the Picker will popup, stealing the focus from your applications Window. Flyouts are required to hide on App-switch by the design guidelines, which is perfectly fine in most cases. BUT: The user does expect the Flyout to be open when he returns from the Picker, which is obviously not the case, as the Flyout hid, just like the guidelines require.

 

How do we solve this problem?

Well due to the IsIgnoreLightDismissal property you can do this quite easily: Before showing the Picker, set IsIgnoreLightDismissal = true. After the picker returned set the property back to false.

 

Background:

The Window-change, caused by the Picker results in the Flyout being ‘Light-Dismissed’. (LightDismiss is the same as tapping beside the Flyout.) In the events handling WindowActivation and Closing of the Flyout, a regular Window-change and a Picker-caused Window-change look the same, that’s the reason why you have to set IsIgnoreLightDismissal back to false when the Picker returns.

 

One more problem

I don’t know how many of you (though I’d like to know!) use my SettingsContractWrapper to integrate your with the settings contract, but I found myself in the highly unlikely scenario of having a FolderPicker, that had to be invoked by a control in the settings panel. The Flyout would hide just like explained above, but the problem is even more tricky: The instance of the Flyout, hosting my custom SettingsPanel control is created when the user selects the corresponding entry in the settings charm. Now the Button_Click event in the SettingsPanel, that opens the Picker was required to set the parental Flyout’s IsIgnoreLightDismissal property, remember? Q: How do we notify the content control of the Flyout (in this case a SettingsPanel) about its parental Flyout control? A: The SettingsEntry contructor has an optional callback, that is invoked by the SettingsContractWrapper the moment the Flyout is created, providing the Flyout as a parameter. The SettingsPanel on the other hand has a “public void ProvideParentalFlyout(Flyout parentalFlyout)” that got set as the callback in the SettingsEntry constructor.

Finally the SettingsPanel will know about the Flyout is is hosted in, so before the Picker is shown the Button_Click event has access to the Flyout.IsIgnoreLightDismissal property.

The FlyoutAndSettingsSample demonstrates the difference:

[youtube=http://www.youtube.com/watch?v=DpISDb2dH8w&w=448&h=252&hd=1]

 

Summary

TCD.Controls on NuGet

FlyoutAndSettingsSample, demonstrating simple, as well as advanced scenarios

I’d like to hear what you think – especially if you find a bug!

cheers,

~theCake

54 comments

  1. Sven

    Hey theCake,

    since my last comment I subsribed to your blog and your last post (this one) caught my attention. I didn’t have a closer look at your Flyout control yet but I’d like to know whether there are any advantages over using a Popup control (Windows.UI.Xaml.Controls.Primitives)?

    Best regards

    1. thecake

      Good morning^
      The Flyout is built on top of a Popup control – the Popup ist just a very primitive control allowing all kings of “Windows” to appear. (I think MessageDialog, ContextMenu are Popups as well)
      In Javascript Apps there’s a built in Flyout control, but they forgot to include it in the xaml default controls, so somebody had to come up with a custom one. (My one is built on top of Tim Heuers..)

      For me the main advantage is the easy construction during runtime and the SettingsContractWrapper, which I use frequently. In the next versions of TCD.Controls, I’ll polish the Flyout to look like the native ones (colored top) and make them more XAML-compatible.
      cheers^

      1. thecake

        The new package with colorable header is now online (version 1.2.0)

  2. Zakkar

    Hi ,
    I don’t know if this is a bug or not. I ‘m opening the flyout control which inside I have a list view filled with items.
    When I click inside this listview(an item) I want to create a different listview with different items.
    So what I did is the below

    StackPanel s = new StackPanel();

    List MyBrowserList = new List();
    MyBrowserList = item as List;

    if (MyBrowserList == null)
    {
    s.Children.Add(this.CreateTheListViewSubFolder(MyPassBrowsers));

    }

    Myf = new TCD.Controls.Flyout(
    new SolidColorBrush(Colors.White),//the foreground color of all flyouts
    (Brush)App.Current.Resources[“AppBarItemPressedForegroundThemeBrush”],//the background color of all flyouts
    new SolidColorBrush(Color.FromArgb(202, 202, 202, 0)),//the theme brush of the app
    “Browsers…”, FlyoutDimension.Narrow,//switch between narrow and wide depending on the check box
    s);

    Myf.Show();

    So far so good. The flyout is displayed with the items I want .
    But when I click one Item , the same code runs again , I get the flyout populates with the new listview items but….
    If I tap, out of the flyout area I have to tap twice in order for the flyout to disappear.
    For eg.
    the first time the Level is 1. The second time the level is 2. If I want for the flyout to disappear and I have created the flyout twice (even though it is a local variable) I have to tap twice or three times(it depends on how many times I have populate the flyout) in order for the flyout to disappear.
    I tried to put a tapped event where i do the below
    Myf.Hide(CloseReason.LightDismissal);
    Myf = null;
    but nothing.

    Still I must tap second and third time (depends how many times I have created).
    Also if I ‘ve noticed that everytime I call the Show method , if the flyout is created , it creates a new layer-flyout over the flyout. It doesn’t destroy the previous one it justs displays it over the previous one and i believe that is the reason why I have to tap several times.

    Do we have a solution for this ? Is it something that I need to do ?
    Is it possible to completely destroy the flyout if it is opened already ? And if it is possible how can I do that ?
    Appreciate if you could help me on this.
    thank you

    1. thecake

      The problem is, that you did not repopulate the Flyout, but created a new Flyout, without closing the old one.
      The appropriate usage would be to make a UserControl, that does all the repopulating stuff and just sits in the Flyout.
      Though I admit, that there’s no logical cause, why the Flyout does not expose a ‘Content’ property and thereby allow changes to it’s content, after being constructed. (Currently you can just set the content once, on creation.)
      -> I just published an update to the NuGet package, that does have this feature.
      cheers

      1. zakkar

        Thank you. Exposingthe content helped .
        I have one more question for you.
        How can I trap the Back button in my own event ? When the flyout appears in the header you have a back button. by default. How can I trap its event ?
        thank you

        1. thecake

          The event is not publicly exposed, but the Flyouts OnClosing event has a parameter that tells you if the BackButton was used/clicked. What exactly are you trying to achieve? (Just published another update to NuGet, fixing several bugs..)

          1. zakkar

            Hi thecake ,
            I will use the exposed property Content to fill the flyout with different listviews. It worked with the content property but when I am in a subfolder for example I want to use the Back button to go up a level.
            But pressing the back button closes the entire flyout.
            Note the I have the listview already loaded with the proper data.
            Although if I am in the first level , if I click an item I can go to the second level populating a different listview , I cannot go back a level (pressing the back button) or even load dynamically in the content the new listview with the previous data.
            I have already used the onclosing event were I did that job , loading the proper list view but when I assign the new list view I cannot get it done.
            That is why I have asked for back button event. I thought I was doing something wrong.

            You can try it and tell me if you can reproduce it.
            load a list view with some items and use the content property. then call the Show method.
            You will get your display.
            Now press something from your list view and load in your flyout a different list view(different items).
            Press the goiback button. For me it is closing the flyout.
            Programming the onclosing event and populating a different list view didn’t work either.
            Is there any update layout , or refresh layout that I should call ?
            I’m running out of ideas .
            Any suggestions ?

          2. thecake

            I’ve modified the OnClosing event – you can now use the Cancel property of System.ComponentModel.CancelEventArgs to prevent it from closing.
            cheers

          3. zakkar

            thank you.
            It worked . I’m OK now.

  3. Alexej

    Thank you! It works good. But there are also stealing focus when you click some element on flyout ( close button for example ) and flyout is closed, then user should click twice on interface to do something ( change ckeckbox state in your example for example ).
    In case my app always looses focus after closing flyout…
    Can I solve it somehow?

    1. thecake

      Hey,
      You can do “this.Focus(…)” on the page to get your focus back.
      I’ll have a look into this and try to fix it in the next release^
      ~theCake

  4. Alexej

    Thank you! Version 1.4.2 works better. But still have no idea how to return focus back to BlankPage. this.focus(..) doesn’t help. Here is your code:
    public void Hide(CloseReason reason)
    {
    OnClosingEvent(this, reason);
    Window.Current.Activated -= OnWindowActivated;
    Canvas.SetLeft(flyoutPopup, Window.Current.Bounds.Width);
    }
    Can I somehow return focus back to BlankPage from here?

    1. thecake

      The only way to release a focus from a control is to give it to another control. You can await the Flyout.ShowAsync() method and then do someControl.Focus(…)

  5. seetharam

    Thank you for a great articale , “Operation is not valid due to the current state of the object” i am getting this error when the flyout is closing , this is what the exception shows :-

    System.InvalidOperationException: Start may not be called on a task that was already started.
    at System.Threading.Tasks.Task.Start(TaskScheduler scheduler)
    at System.Threading.Tasks.Task.Start()
    at TCD.Controls.Flyout.Hide(CloseReason reason)
    at TCD.Controls.Flyout.OnPopupClosed(Object sender, Object e)

    1. thecake

      Yes, I encountered this bug two days ago and fixed it in the latest update. please update your NuGet package 🙂

  6. seetharam

    first of thanks for the reply , can i know the latest package of nuget package avaliable for TCD Controls , is it Version 1.4.11 ?

    1. thecake

      Hey, yes it’s 1.4.11 as listed on https://nuget.org/packages/TCD.Controls
      The bug should be gone, but I’ll need to have a more detailed look at it..

  7. seetharam

    I have a updated to latest package but no luck ,as per my observation what i understood is that it works fine in licensed version of windows 8 but not in release preview . i might be wrong also , i request you to check it out once , since this post is very helpful to everyone 🙂

    1. thecake

      I checked it once again and pushed an update. I don’t have W8RP anymore and I can’t reproduce the exception. Please try if it still occurs in v1.4.12 and write me an email to mailto://thecakedev@hotmail.com – what line is the exception thrown? and in what context – backbutton/light dismissal…

    2. thecake

      I’m having trouble replying to your email – getting Delivery Status (Failure) all the time..
      That should solve your problem:

      flyout.Hide(CloseReason.Other);
      await Task.Delay(1); //that’s the trick
      //time consuming service call

  8. Rich

    I have the Flyout working with a Dropdown Control. When the SelectionChanged Event fires, I want to make a progressBar visible while I perform a WS update of the Win8 App. How can I find the ProgressBar on the Flyout. I was attempting to use FindName but it keeps coming back Null

    for example
    ProgressBar PB1 = (ProgressBar)this.FindName(“RunProgress”);

  9. Rich

    I have the Flyout working with a Dropdown control. I’m just having troubles attempting to update the flyout control from the Dropdown changedSelection event.

    When the changedSelection event fires, I want to start a Progress bar on the flyout while I update the dataset of the App. How can I locate the ProgressBar control on the flyout? I was attempting locate it through the .parent methods but it kept coming back null.

    1. thecake

      You can use flyout.FindName(“headerGrid”) to get the upper part of the Flyout control. Then you can add a ProgressBar to it.
      Take a look at this sample: http://dl.dropbox.com/u/7813771/Blog/CodeSamples/FlyoutProgressBar.zip

      It actually looks pretty cool – I got to remember this =p

  10. zakkar

    Hi thecake ,
    I have a question for the flyout control.
    Is it possible to have in the header something like a pin where the user can select if the flyout must be dockable or not ?
    For example the default behaviour of the flyout is to close when I click anywhere else.
    Can I prevent the flyout from closing and close it only when the user presses a button for example ?
    thank you

  11. thecake

    You can disable automatic closing of the flyout with its IsIgnoreLightDismissal property.
    Take a look at the comment above (http://kuchenzeit.wordpress.com/2012/07/17/tcd-controls-flyouts-for-xamlc/#comment-355) for instructions on how to get the header Grid.
    cheers

    1. zakkar

      thank you

  12. zakkar

    Hi thecake ,
    Can I make the back button of the header Invisible ? Or How can I trap the click event of this button ?
    thank you

    1. zakkar

      Found out how to handle it. Never mind.

  13. Lieve

    Hello,
    I implemented the settings control. In my MainPage.xaml I make the settings entry like this:

    SettingsEntry entry1 = new SettingsEntry(“Language”, new LanguageSettingsControl(), FlyoutDimension.Narrow);

    //set up the entries with the settings contract wrapper
    SettingsContractWrapper wrapper = new SettingsContractWrapper(
    new SolidColorBrush(Colors.White),//the foreground color of all flyouts
    new SolidColorBrush(Colors.White),//the background color of all flyouts
    new SolidColorBrush(Colors.Blue),//the theme brush of the app
    new BitmapImage(new Uri(“ms-appx:/Assets/SmallLogo.png”)),
    entry1);

    It works nice, but if I navigates away from my MainPage, and I’m navigate back to my MainPage, the entry (Language) duplicates on my settings charm. So each time I navigate back to my MainPage the settings entry stack with all the same entries.

    I build the settings entry in the codebehind of my MainPage.xaml in the constructor.

    Can somebody help me with this?

    Thanks

    1. thecake

      oh that’s an easy one (I had it once too..)
      You may only use the SettingsContractWrapper once in your apps lifecycle. Just call it from your App constructor in App.xaml.cs to make sure it’s not called multiple times.
      happy easter^

      1. Lieve

        Thanks, it works!
        Happy easter

  14. Lieve

    Once you have created the SettingsContactWrapper is it possible to change the Labels of the different settingenties? For example when the user changes the UI Language I want to display the settingsscreen in users local language.

    Thanks

    1. thecake

      No, currently not. But I can take a look at it later today and create an overload method that takes a void(string) and a string and when the settings pane opens calls the void(string) (your GetString method) with the string parameter.
      (Similar to what I’ve done in http://kuchenzeit.wordpress.com/2013/01/10/localizing-enums-in-a-combobox/)
      Would that be okay?

      1. Lieve

        Oh that will be great, tanks

  15. Lieve

    Is there any chance to get a look at the source?

    Tanks

    1. thecake

      I didn’t have the time to get it done yet.. sry..
      But I think I can do it on Monday. I’ll send you a copy of the source too.

  16. josc

    I’m using the SettingsContractWrapper to setup the settings charm.
    I need some code to run when the settings flyout closes. Is it possible to provide that event using the SettingsContractWrapper?

    1. thecake

      I’ve implemented both features you suggested:
      You can localize the SettingsEntries either by using a overload constructor (pass your GetString method as the first parameter and the localization string as the second) or by setting the GetStringMethod and GetStringParameter properties of the SettingsEntry instance.

      The SettingsContractWrapper instance now has a even ‘OnSettingsClosed’ that will fire every time a Flyout closes and pass the associated SettingsEntry instance, so you can discriminate ‘Info/About’ and ‘Settings’

      have a nice day!

  17. Lieve

    Hi,
    is there any chance to ask if the settings charm is open?

  18. DD

    Hi,

    I am facing an issue where the flyout opens up in another screen even though its not called.
    I might be missing something but i have not been able to figure it out.

    I have a flyout screen which adds a student information,
    which also includes adding an image using the file picker

    I use the following code:
    ParentFlyout.IsIgnoreLightDismissal = true;
    FileOpenPicker fop = new FileOpenPicker {SuggestedStartLocation = PickerLocationId.PicturesLibrary};
    fop.FileTypeFilter.Add(“.jpg”);
    fop.FileTypeFilter.Add(“.jpeg”);
    fop.FileTypeFilter.Add(“.png”);
    StorageFile file = await fop.PickSingleFileAsync();
    ParentFlyout.IsIgnoreLightDismissal = false;

    Once i click on save, i save the data and clear out all the controls and doesnot close the flyout, which allows the user to add the next student.
    I add two students. Then go to another screen where i add the students to a dorm. While adding details to a dorm i also provide the option to select an image (using the same code as above), once the image is selected the Add Student flyout pops up, even though i havent called it. When i debug and check the call stack for Add Student, it shows external code.

    A pattern i have noticed is this happens only when i add two students and during which the back button of the flyout doesnt work and i tap out of the flyout screen to close the flyout.

    1. thecake

      uhm..
      the ‘other screen’ to select an image – is it in the same Flyout?
      could you make a screenvideo of the problem? (for example with http://screencast-o-matic.com/)
      from what I understand, it sounds like the ‘AddStudent’ method is called again..

      1. DD

        No other screen is a different screen, dorm screen. Yes i had assumed that the Add Student method was getting called again, but i am not able to figure out from where. Because i am not doing it from the dorm screen.
        And when i keep a break point on the load method of AddStudent, and check the call stack it says External method.
        I will try to make a screen video and post it.

      2. DD

        Also one thing i have noticed in Add Student screen is the back button in the flyout doesnt work at times. How can check if the back button is clicked.

      3. DD

        This is my code to open the flyout.

        private async void Add_OnClick(object sender, RoutedEventArgs e)
        {
        AddStudent addStudent = new AddStudent ();
        var flyout = new Flyout(
        new SolidColorBrush(Colors.White),
        new SolidColorBrush(Colors.Black),
        new SolidColorBrush(Colors.DarkGreen),
        “Add Student”,
        FlyoutDimension.Wide,
        addStudent );

        addStudent.ParentFlyout = flyout;
        flyout.OnClosing += f_OnClosing;
        await flyout.ShowAsync();
        }

        void f_OnClosing(object sender, CloseReason reason, System.ComponentModel.CancelEventArgs cancelEventArgs)
        {
        // do nothing
        }

      4. thecake

        I was able to reproduce the issue…
        The problem is with the show/hide logic, which is pretty complicated.. As it’s 23:00 and I got a busy day tomorrow, I can’t fix this right now, but I will update the NuGet package as soon as I can! (tomorrow, or the day after..)
        greetings

      5. thecake

        Fixed it! TCD.Controls 1.5.1 now online.
        (disclaimer: I only did a bit of testing.. but your issue seems to be resolved.)

        1. DD

          Thanks.. Will you know how it worked out.

  19. Gianfranco

    Thanks a lot. It’s great control. How to pass value in another xaml? There is an example

    MyXaml x= new MyXaml ()
    TCD.Controls.Flyout f = new TCD.Controls.Flyout(
    new SolidColorBrush(Colors.White),
    (Brush)App.Current.Resources[“ApplicationPageBackgroundThemeBrush”],
    new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)),
    “My xaml title”,
    FlyoutDimension.Wide,
    MyXaml
    );
    await f.ShowAsync();

    Ho to set parameters? I need send an ID in another xaml for to modify form. Could you help please?

    1. thecake

      I usually use a global (static) Settings class. Take a look at this example: https://dl.dropboxusercontent.com/u/7813771/Blog/CodeSamples/RWTHMensaSettings.cs
      You can basically copy/paste it into your project and just rename (Ctrl+H) the names and their types.

      In your other control just subscribe to the SettingsClass.OnSignificantChange event..

      cheers

      1. Gianfranco

        Ok but I don’t how to user it. I have list item and when I select row in app bar I press button and send ID parameters to flyout control. In Windows Phone is more easier.

        I retrieve ID

        myList mov = (myList )my_listview.SelectedItem;
        int ID = myList.ID;

        I create control flyout….and I’d like pass parameter ID.
        MyXaml x= new MyXaml ()
        TCD.Controls.Flyout f = new TCD.Controls.Flyout…..

        Now I can user App.cs but is not normal procedure use global variabile in this case. There is another way? I used NavigationService in Windows Phone with querystring and is more useful.:(

        1. thecake

          You want to pass the parameter to the MyXaml control, which is inside the Flyout, right?
          When you create the Flyout, you need to remember the MyXaml control.

          For example:

          MyXaml myxamlcontrol = new MyXaml(...);

          void CreateAndShowFlyout()
          {
          Flyout flyout = new Flyout(..., myxamlcontrol...);
          flyout.Show();
          }

          void SelectionChanged(...)
          {
          ....
          int id = myList.ID
          myxamlcontrol.ChangeSelection(id);
          }

          and in MyXaml control:

          class MyXaml
          {
          ...
          public void ChangeSelection(int id)
          {
          ....
          }
          }

          I don’t know what you’re trying to do with the Flyout, but if you want to use it for settings then you should use some static settings class^^

Leave a Reply

Your email address will not be published. Required fields are marked *