lundi 21 décembre 2015

Creating an expandable TextBlock control for WinRT, click to expand text

In this example we will look at how to create an expandable text block for a Windows 10 application.  I want a control on which I tell it its default height before it is expended and of course the text that need to be shown, a control that has a XAML code that looks like this:

 <controls:ExpandableTextBlock 
                Margin="0,0,0,0"
                CollapsedHeight="30"
                Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis lobortis libero vel justo sollicitudin auctor. Nulla blandit pulvinar augue ac feugiat. sit amet dolor. Suspendisse mattis dolor eu nulla volutpat, vitae accumsan sapien commodo.Vivamus id turpis sem."
            
                />

And that looks like this when its collapsed:


And like this when it is expended:



Let get coding:

First we will need to create a UserControl named ExpandableTextBlock, this control will contain one grid that holds 2 text blocks on that will hold our text and the other one that will hold our button or text that tells the user to click if he wishes to see the text.

Here is the XAML code:


 <Grid x:Name="LayoutRoot" Tapped="LayoutRoot_OnTap">
     <Grid.RowDefinitions>
          <RowDefinition Height="Auto" />
          <RowDefinition Height="Auto" />
      </Grid.RowDefinitions>

      <TextBlock  Grid.Row="0" 
                  x:Name="CommentTextBlock"  
                  HorizontalAlignment="Left" 
                  TextWrapping="Wrap"  
                  Height="Auto" 
                  Width="280"/>

      <StackPanel Grid.Row="1" 
                  Orientation="Horizontal" 
                  HorizontalAlignment="Right"
                  x:Name="ExpandHint" 
                  Visibility="Collapsed" 
                  Margin="0,5,0,0">
          <TextBlock  Text="View More" />
           <TextBlock Margin="10,0,10,0"
        Text="+" />
      </StackPanel>
</Grid>

We will need to create two DependencyProperty called Text and  CollapsedHeight which will allow you to pass the Text and the Height of your textblock to the UserControl.


        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(string), typeof(ExpandableTextBlock), new PropertyMetadata(default(string), OnTextChanged));

        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
       
        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var ctl = (ExpandableTextBlock)d;
            ctl.CommentTextBlock.SetValue(TextBlock.TextProperty, (string)e.NewValue);
            ctl.CommentTextBlock.SetValue(TextBlock.HeightProperty, Double.NaN);

            ctl.CommentTextBlock.Measure(new Size(ctl.CommentTextBlock.Width, double.MaxValue));

            double desiredheight = ctl.CommentTextBlock.DesiredSize.Height;
            ctl.CommentTextBlock.SetValue(TextBlock.HeightProperty, (double)63);

            if (desiredheight > (double)ctl.CommentTextBlock.GetValue(TextBlock.HeightProperty))
            {
                ctl.ExpandHint.SetValue(StackPanel.VisibilityProperty, Visibility.Visible);
                ctl.MaxHeight = desiredheight;
            }
            else
            {
                ctl.ExpandHint.SetValue(StackPanel.VisibilityProperty, Visibility.Collapsed);
            }

            //Setting length (width) of TextBlock
            var boundsWidth = Window.Current.Bounds.Width;
            ctl.CommentTextBlock.SetValue(TextBlock.WidthProperty, boundsWidth);
        }

        public static readonly DependencyProperty CollapsedHeightProperty = DependencyProperty.Register(
            "CollapsedHeight", typeof(double), typeof(ExpandableTextBlock), new PropertyMetadata(default(double), OnCollapsedHeightChanged));


        public double CollapsedHeight
        {
            get { return (double)GetValue(CollapsedHeightProperty); }
            set { SetValue(CollapsedHeightProperty, value); }
        }

        private static void OnCollapsedHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var ctl = (ExpandableTextBlock)d;
            ctl.CollapsedHeight = (double)e.NewValue;
        }

in the OnTextChanged method you can see that we check to see if we need to show the hint that there is more text or not, as you can imagine it is pointless to show to the user an element saying that there is more text is there is not really more text.

and lastly we now need to create the méthode linked to the LayoutRoot_OnTap event, this methode will have to check to see if the content is fully visible or not and if not then show the whole content.


       private void LayoutRoot_OnTap(object sender, TappedRoutedEventArgs tappedRoutedEventArgs)
        {
           if ((Visibility)this.ExpandHint.GetValue(StackPanel.VisibilityProperty) == Visibility.Visible)
            {
                //transition
                this.CommentTextBlock.SetValue(TextBlock.HeightProperty, Double.NaN);

                this.ExpandHint.SetValue(StackPanel.VisibilityProperty, Visibility.Collapsed);
            }
        }

And there you have it, when the user clicks on the text block it will show the reste of the text.

Possible improvements:

You could add a third DependencyProperty that would allow you to manage the width of your text block thus instead of having the textblock taking the whole width of the screen like this:

//Setting length (width) of TextBlock
var boundsWidth = Window.Current.Bounds.Width;
ctl.CommentTextBlock.SetValue(TextBlock.WidthProperty, boundsWidth);

You could use a DependencyProperty to set the width.


Or you could also add another DependencyProperty which could manage the style of the TextBlock like this:



       public static readonly DependencyProperty TextStyleProperty = DependencyProperty.Register(
           "TextStyle", typeof(Style), typeof(ExpandableTextBlock), new PropertyMetadata(default(Style), OnTextStyleChanged));

        public Style TextStyle
        {
            get { return (Style)GetValue(TextStyleProperty); }
            set { SetValue(TextStyleProperty, value); }
        }

        private static void OnTextStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var ctl = (ExpandableTextBlock)d;
            ctl.CommentTextBlock.SetValue(StyleProperty, (Style)e.NewValue);
        }


Happy coding.

you can find the Github code here


mardi 15 décembre 2015

Using Live Visual Tree and Live Property Explorer to customize the Media Player (XAML, C#)

In this series of tutorials on how to customize your MediaPlayer, we have seen how to customize a button in part I. Now we will see how we can use Live Visual Tree and Live Property to make our lives easier when we are trying to design our MediaPlayer element. 
The rest my my article on the Dailymotion blog  here.

lundi 14 décembre 2015

Using the new .NET Facebook SDK in Windows 10 apps (UAP) C# and setting up you Facebook developer account

I was migrating one of my applications that was using the old Facebook SDK and that did not work at all in my new UAP application.  Search the web i found that Facebook had developed a new SDK for UAP apps.  Here is a small tutorial of what i had to do to make this SDK work with my current code.

What you need to have:
- FacebookId
- cloned FacebookSDK: https://github.com/Microsoft/winsdkfb
- Blank UAP app

I am using Visual Studio 2015 and a UAP application so I had to add this project to my application:
FBWinSDK\FBSDK-UWP\FBSDK-UWP\FBSDK-UWP.vcxproj
and then add the reference to my blank app.

your application should look as follows:



 Next we are going to need to get the Id of our application :

string SID = WebAuthenticationBroker.GetCurrentApplicationCallbackUri().ToString();

you will need to add this using of this code to work:

using Windows.Security.Authentication.Web;

Next we need to get the Facebook session, we will need our Facebook Id and our Windows Application id as follow:

FBSession sess = FBSession.ActiveSession;
sess.FBAppId = "<Facebook App ID>";
sess.WinAppId = "<Windows or Windows Phone Store ID depending on the target device>";

you will need to add this using of this code to work:

using Facebook;

And lastly we need to get the login prompt and set the permitions, the permitions are to be set in a list of strings:

// Add permissions required by the app 
        private static readonly List<string> PermissionList = new List<String>() { "user_about_me", "email", "publish_actions" };

And here is the login code:

 FBPermissions permissions = new FBPermissions(PermissionList);

        
                // Login to Facebook
                FBResult result = await sess.LoginAsync(permissions);

                if (result.Succeeded)
                {
                    // Login successful
                    return sess.AccessTokenData.AccessToken;
                }
                else
                {
                    // Login failed
                    return null;

                }

In the application that you can find on Github, you will fnd evrything on one page:


    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        // Add permissions required by the app 
        private static readonly List<string> PermissionList = new List<String>() { "user_about_me", "email", "publish_actions" };



        public async Task<string> LogIntoFacebook()
        {
            //getting application Id
            string SID = WebAuthenticationBroker.GetCurrentApplicationCallbackUri().ToString();

            // Get active session
            FBSession sess = FBSession.ActiveSession;
            sess.FBAppId = Dailymotion.Core.Constants.FacebookAppId;
            sess.WinAppId = SID;

            //setting Permissions
            FBPermissions permissions = new FBPermissions(PermissionList);

            try
            {
                // Login to Facebook
                FBResult result = await sess.LoginAsync(permissions);

                if (result.Succeeded)
                {
                    // Login successful
                    return sess.AccessTokenData.AccessToken;
                }
                else
                {
                    // Login failed
                    return null;

                }
            }
            catch (InvalidOperationException ex)
            {
                //error handling
                return null;
            }
            catch (Exception ex)
            {
                //error handling
                return null;
            }
        }

        private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            //get the access token to login
            var result = await LogIntoFacebook();
        }
    }

Setting up your account


Next you will need to access you developer account on Facebook: https://developers.facebook.com/apps/

You will need to create a new app, you fill find you app Id here and then you will need to setup the Windows Store SID that you can find in your application and add it  here under the setting panel.



Update 1:
To make this work you also need to go to Facebook Developer website: https://developers.facebook.com
under: your app name -> Settings -> Advance:
And then under: Valid OAuth redirect URIs you need to add: https://www.facebook.com/connect/login_success.html
Save and you are good to go now! enter image description here


Github code: here

SDK; https://github.com/Microsoft/winsdkfb

More information here:
http://blogs.windows.com/buildingapps/2015/07/14/windows-sdk-for-facebook/

lundi 7 décembre 2015

Re-purposing Media Player Framework (3.0.0.1) buttons to do something else (XAML,C#)


In this small example I will explain why I re-purposed some media player buttons on my MediaPlayer.  At first I was looking into creating a whole new button and linking it with events using MVVM with a RelayCommand, I wanted to do this well of course.

To start I wanted to create my own button from scratch and bind it with an event, this quickly became difficult as I am not a great designer (looking at my apps you will quickly understand what I mean) and binding an event on events that are that deep (in a ControlTemplate) is not always the best idea or the easiest.  It does work and it is doable, however I was sometimes getting some issues on the tapped event which was not being fired (probably the tapped event was getting caught by some other button in the player).

So I started looking at my XAML code to see what I could do:

  <playerFramework:MediaPlayer
                x:Name="Player"
                Background="Transparent"
                IsPlayPauseVisible="True"
                Opacity="1"       
                />       

The C# code for my player looked as follow:

                    Player.Source = MySource;
                    Player.MediaOpened += Player_OnMediaOpened;
                    Player.MediaClosed += Player_MediaClosed;
                    Player.Play();

and then it hit me! why dont I just re-use some of the buttons on the player that I am not using.  This would allow my to not have to re code all of the different events linked to the button and also have the effects already coded for me like the on press event and mouse over events and more.

 In my example I decided to re purpose the full screen button to start,  all we need to do on the MediaPlayer is to show the FullScreenButton:

  <playerFramework:MediaPlayer
                x:Name="Player"
                VerticalAlignment="Bottom"
                HorizontalAlignment="Left"
                Background="Transparent"
                IsFullScreenEnabled="True"    <- this
                IsFullScreenVisible="True"    <- this
                IsPlayPauseVisible="True"
                Opacity="1"       
                />       



Then we just need to catch the isFullScreenChanged event like this:

        
private void InitPlayer()
{
        Player.Source = MySource;
        Player.MediaOpened += Player_OnMediaOpened;
        Player.MediaClosed += Player_MediaClosed;
        Player.IsFullScreenChanged += Player_IsFullScreenChanged;
        Player.Play();
}



 private void Player_IsFullScreenChanged(object sender, RoutedPropertyChangedEventArgs<bool> e)
        {
            //do what ever you want to do...
        }


And now you can do whatever you wish when the full screen button is clicked on the player.

Next we will edit the AppBarButton style so that we show our custom button, like I have written in previous articles you need to get the default style of the AppBarButton and edit it as you need. For this example we will just edit the element <Viewbox> comment its Ellipse and ContentPresenter and add an image:

    <Style x:Key="PlayerAppBarCloseButtonStyle" TargetType="AppBarButton">
        <!--<Setter Property="Foreground" Value="{ThemeResource AppBarItemForegroundThemeBrush}"/>-->
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Center" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="IsCompact" Value="True" />
        <Setter Property="Height" Value="40" />
        <Setter Property="Width" Value="40" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="AppBarButton">
                    <Grid x:Name="RootGrid" Background="Transparent">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="ApplicationViewStates">
                                <VisualState x:Name="FullSize" />
                                <VisualState x:Name="Compact" />
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal" />
                                <VisualState x:Name="PointerOver" />
                                <VisualState x:Name="Pressed" />
                                <VisualState x:Name="Disabled" />
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="FocusStates">
                                <VisualState x:Name="Focused" />
                                <VisualState x:Name="Unfocused" />
                                <VisualState x:Name="PointerFocused" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Viewbox Margin="{TemplateBinding Padding}">
                            <Grid Width="45" Height="45">
                                <Ellipse x:Name="BackgroundEllipse"
                                         Width="45"
                                         Height="45"
                                         Fill="#BBFFFFFF"
                                         UseLayoutRounding="False" />

                                <!--<Ellipse x:Name="BackgroundEllipse" Fill="{ThemeResource AppBarItemBackgroundThemeBrush}" UseLayoutRounding="False"/>-->
                                <!--<Ellipse x:Name="OutlineEllipse" Stroke="{ThemeResource AppBarItemForegroundThemeBrush}" StrokeThickness="2" UseLayoutRounding="False"/>-->
                                <!--<ContentPresenter x:Name="Content" AutomationProperties.AccessibilityView="Raw" Content="{TemplateBinding Icon}" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
                                                  Height="45"
                                                  Width="45"
                                  Opacity="1"
                                                  />-->
                                <Image Width="25"
                                       Height="25"
                                       Margin="10"
                                       Source="/Assets/VideoPage/close.png" />

                            </Grid>
                        </Viewbox>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Lastly we just need to link this style to our FullScreenButton which should be located in your ControlTemplate of the video media element as follow:


 <AppBarButton x:Name="FullScreenButton"
               Width="45"
               Height="45"
               Margin="8,0,8,0"
               Style="{StaticResource PlayerAppBarCloseButtonStyle}"               Visibility="{Binding IsFullScreenButtonVisible,
                            Converter={StaticResource VisibleIfConverter},
                              RelativeSource={RelativeSource Mode=TemplatedParent}}">
                      <local:MediaControls.Behavior>
                          <local:FullScreenButtonBehavior ViewModel="{Binding ViewModel, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                      </local:MediaControls.Behavior>
 </AppBarButton>

And there you have it, you have re purposed an AppBarButton to do what you need it to do.  I used the FullScreenButton but you could have used any other button that you are not using as long as it has a Changed event linked to it!

Happy coding.

As a great philosopher once said "Rien ne se perd, rien ne se crée, tout se transforme."  Antoine Lavoisier