Social Icons

twitter google plus linkedin rss feed

Pages

Showing posts with label Windows Phone. Show all posts
Showing posts with label Windows Phone. Show all posts

15.1.15

Win RT Universal app with a DocumentDB

NoSql databases have been brought to my attention a couple of thousand times in the last months and given that I am not the tidiest of the database designers and also that NoSql databases are supposed to be designed to escale I have decided to give them a go.

My platform of choice is Microsoft and looks like we have all the tools we need: Windows Apps that work the same desktops and phones and DocumentDB in Azure, fantastic, let's begin.

Let's start with the database, as it takes a while to start it will give you time to work in parallel with the visual studio in the meantime.

As I already have my azure account and everything set up I went straight to the creation of the database account: preview management portal DocumentDB Creation.

The process is explained in detail here: http://azure.microsoft.com/en-us/documentation/articles/documentdb-create-account/ 

First you need to specify a couple of parameters you won't see the page in Spanish necessarily, in fact I don't know why it is not showing it to me in English to set up the new database.



And once you are done you will be taken to a home page of the azure portal while you wait...


And while we wait we can go to the Visual Studio and start creating the projects we need. I usually say things like easy, piece of cake etc. but surely not so often today.

First of all I have created an universal app in Visual Studio 2013 as I am so outstandingly good with the user experience it will be the natural option for me to create two user interfaces, one for tablets and one for phones...


Now let's create a "Class Library (Portable for Universal Apps) to manage the DocumentDB connections and queries:


Once the DLL project was created I added a folder called Models with a class DbUser and a class Device:
namespace DocDbBridge.Models
{
    public class DbUser
    {
        public string Email { get; set; }
        public string Name { get; set; }
        public Device[] Devices { get; set; }
    }
}


namespace DocDbBridge.Models
{
    class Device
    {
        public string Name { get; set; }
        public string Brand { get; set; }
    }
}

Finally I went to the main class (which I called Connection) and added the following usings:


using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

It doesn't work because we are missing the Microsoft Azure DocumentDB Client Library 0.9.2-preview. In order to get it I have set the DLL to use .Net Framework 4.5.1


Then got Newtonsoft.Json from Nuget:


That creates a packages.config file in the project. In it I have added a line for the Microsoft.Azure.Documents.Client and rebuilt the project. The packages.config file looks like this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Azure.Documents.Client" version="0.9.2-preview" targetFramework="portable-net451+win81+wpa81" />
  <package id="Newtonsoft.Json" version="6.0.8" targetFramework="portable-net451+win81+wpa81" />
</packages>

Finally, after the rebuild i have added a reference to the Microsoft.Azure.Documents.Client by browsing the project folders and finding the newly downloaded dll:


I have built the project again and it seems to be working, let's try to connect to the database now. Based in an example provided by Microsoft for the version 0.9.0 I have created a Connection class that goes like this:

using DocDbBridge.Models;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace DocDbBridge
{
    public class Connection : IDisposable
    {
        string endPoint = "https://chan.documents.azure.com:443/";
        string authKey = "Weird string with a lot of meaningless characters";

        DocumentClient client { get; set; }
        Database database { get; set; }

        DocumentCollection collection { get; set; }

        public Connection()
        {
             client = new DocumentClient(new Uri(endPoint), authKey);
             database = ReadOrCreateDatabase("QuickStarts");
             collection = ReadOrCreateCollection(database.SelfLink, "Documents");
             CreateDocuments(collection.SelfLink);
        }


        public DbUser QueryDocumentsLinq(string UserEmail)
        {
            // The .NET SDK for DocumentDB supports 3 different methods of Querying for Documents
            // LINQ queries, lamba and SQL


            //LINQ Lambda
            //return client.CreateDocumentQuery(collection.SelfLink).Where(u => u.Email == UserEmail).AsEnumerable().FirstOrDefault();
            return client.CreateDocumentQuery(collection.SelfLink).ToList().Where(u => u.Email == UserEmail).FirstOrDefault();
        }

        public DbUser QueryDocumentsSQL(string SqlQuery)
        {
            //3. SQL
            //var query = client.CreateDocumentQuery(collection.SelfLink, "SELECT * " +
            //                                                           "FROM UserDbs u " +
            //                                                           "WHERE u.email='andrew@stratex.com'");

            var query = client.CreateDocumentQuery(collection.SelfLink, SqlQuery);

            return query.AsEnumerable().FirstOrDefault();
        }

        public void Dispose()
        {
            Cleanup(database.SelfLink);
            client.Dispose();
        }

        #region Private Methods
        private Database ReadOrCreateDatabase(string databaseId)
        {
            // Most times you won't need to create the Database in code, someone has likely created
            // the Database already in the Azure Management Portal, but you still need a reference to the
            // Database object so that you can work with it. Therefore this first query should return a record
            // the majority of the time

            Database db = client.CreateDatabaseQuery().ToList().Where(d => d.Id == databaseId).FirstOrDefault();

                //db = client.CreateDatabaseQuery()
                //                .Where(d => d.Id == databaseId)
                //                .AsEnumerable()
                //                .FirstOrDefault();

            // In case there was no database matching, go ahead and create it. 
            if (db == null)
            {
                //Console.WriteLine("2. Database not found, creating");
                db = client.CreateDatabaseAsync(new Database { Id = databaseId }).Result;
            }

            return db;
        }

        private DocumentCollection ReadOrCreateCollection(string databaseLink, string collectionId)
        {
            DocumentCollection col = client.CreateDocumentCollectionQuery(databaseLink).ToList().Where(c => c.Id == collectionId).FirstOrDefault(); ;

                //col = client.CreateDocumentCollectionQuery(databaseLink)
                //                .Where(c => c.Id == collectionId)
                //                .AsEnumerable()
                //                .FirstOrDefault();

            // For this sample, if we found a DocumentCollection matching our criteria we are simply deleting the collection
            // and then recreating it. This is the easiest way to clear out existing documents that might be left over in a collection
            //
            // NOTE: This is not the expected behavior for a production application. 
            // You would likely do the same as with a Database previously. If found, then return, else create
            if (col != null)
            {
                //Console.WriteLine("3. Found DocumentCollection.\n3. Deleting DocumentCollection.");
                client.DeleteDocumentCollectionAsync(col.SelfLink).Wait();
            }

            //Console.WriteLine("3. Creating DocumentCollection");
            return client.CreateDocumentCollectionAsync(databaseLink, new DocumentCollection { Id = collectionId }).Result;
        }

        private void CreateDocuments(string collectionLink)
        {
            // DocumentDB provides many different ways of working with documents. 
            // 1. You can create an object that extends the Document base class
            // 2. You can use any POCO whether as it is without extending the Document base class
            // 3. You can use dynamic types
            // 4. You can even work with Streams directly.
            //
            // This sample method demonstrates only the first example
            // For more examples of other ways to work with documents please consult the samples on MSDN. 

            // Work with a well defined type that extends Document
            // In DocumetnDB every Document must have an "id" property. If you supply one, it must be unique. 
            // If you do not supply one, DocumentDB will generate a unique value for you and add it to the Document. 
            var task1 = client.CreateDocumentAsync(collectionLink, new DbUser
            {
                Email = "chan@stratex.com",
                Name = "Test user",
                Devices = new Device[]
                {
                    new Device { Name="Lumia 920", Brand="Nokia"},
                    new Device { Name="Surface 3", Brand="Microsoft"},
                }
             });

            var task2 = client.CreateDocumentAsync(collectionLink, new DbUser
            {
                Email = "andrew@stratex.com",
                Name = "Andrew",
                Devices = new Device[]
                {
                    new Device { Name="Lumia 925", Brand="Nokia"},
                    new Device { Name="Surface 3", Brand="Microsoft"},
                }
            });

            
            // Wait for the above Async operations to finish executing
            Task.WaitAll(task1, task2);
        }

        private void Cleanup(string databaseId)
        {
            client.DeleteDatabaseAsync(databaseId).Wait();
        }
        #endregion
    }
}

As you might have noticed you also need the URL and the Auth Key, you can get them from the azure portal which by now will probably have your database up and running:



After that I have added a reference to the DocDbBridge DLL in my app project:


I have, after that, executed my app, by the way, this is the code... Impressive


        private void Button_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                using (Connection conn = new Connection())
                {
                    var user = conn.QueryDocumentsLinq("andrew@stratex.com");

                    if (user != null)
                        Result.Text = user.Name;
                    else
                        Result.Text = "User not found.";
                }
            }
            catch (Exception ex)
            {
                Result.Text = "Exception found";
            }
        }

And it has failed miserably because it needs again the Newtonsoft.Json package... I have installed it on the W8.1 app project.

After that I executed the project again, clicked my marvellous button and I got Exception: Microsoft.Azure.Documents.BadRequestException: Syntax error, unexpected end-of-file here:



client.CreateDatabaseQuery()
                                .Where(d => d.Id == databaseId)
                                .AsEnumerable()
                                .FirstOrDefault();

//And here:
client.CreateDocumentCollectionQuery(databaseLink)
                                .Where(c => c.Id == collectionId)
                                .AsEnumerable()
                                .FirstOrDefault();

It looks like there's something not working quite right in this release... I have changed the line to: 

Database db = client.CreateDatabaseQuery().ToList().Where(d => d.Id == databaseId).FirstOrDefault();

This is a huge issue because it means that you need to retrieve all of them first and then query the database... Completely Unusable! :( Could it be because I am using a piece of software that is in preview in a platform that it's not supported?... some would say yes...

Again in the Linq query the same issue, I had to change from: 

return client.CreateDocumentQuery<DbUser>(collection.SelfLink).Where(u => u.Email == UserEmail).AsEnumerable().FirstOrDefault();

To

return client.CreateDocumentQuery<DbUser>(collection.SelfLink).ToList().Where(u => u.Email == UserEmail).FirstOrDefault();

And I got again the same error when tried to execute the queries with SQL.

Wrapping up:
Upsides: It kind of works and that's really cool.
Downsides: What is a database if you cannot query? I would say... Not Ideal. 

But then again this is a preview software in an unsupported platform.... And it works more or less. Just imagine how cool would the software be in a couple of iterations!

No comments:

Post a Comment

25.4.13

Virtual Box Fails after installing WP8 SDK

After installing the WP8 SDK I round that I couldn't open my VBox VMs any more. I was getting the error:

VT-x is not available. (VERR_VMX_NO_VMX)
 Result Code: E_FAIL (0x80004005) Component: Console Interface: IConsole {db7ab4ca-2a3f-4183-9243-c1208da92392}
It was completely unexpected because i had the WP7 SDK installed and everything was working fine.

Looks like the new SDK needs Hyper-V to run the emulators and having the Hyper-V services initiated won't let Virtual Box use the virtualization hardware.

The solution is a quick and dirty:
bcdedit /set hypervisorlaunchtype off
And a restart to be able to work with your VBox again.

If you want to start your Hyper-V services again you just have to run
bcdedit /set hypervisorlaunchtype auto
And restart.

After saving what I was doing I have converted the vdi to vhdx and started using Hyper-V for my development virtual machine. Now I feel less heretic.

No comments:

Post a Comment

21.4.13

Battery and Shortcut Tiles for WP8

As you know I had my WP8 device some weeks ago. One of the things that annoy me of Windows Phone is  having to go to Settings>Whatever>What you want and I do it a couple of times a day every day. That's being annoyed twice every day and that makes a total of 730 times annoyed every year.

I thought, Great! I am going to make an app with shortcut tiles and I am going to have finally an useful app in the store, one that makes sense.

While I was downloading the SDK I thought it could be good to have an app that tells you about the battery level in a tile. "Someone should have done this, I'll go to the marketplace and grab one" And I was right.

In the marketplace I found Battery, a great app that shows a chart with the battery life, has statistics about everything, tells you the battery level beautifully and even the estimated amount of time until the next charge. Fantastic app! this guy has thought of everything! But, what's that? Oh not again Yes. Shortcut tiles.

The app also provides a set of shortcut tiles. And they work beautifully. WiFi, Location, Bluetooth, Flight Mode and Mobile Network. And my plan was to create shortcut tiles for WiFi and Flight Mode only...

Obviously I am not going to create the app for the shortcuts, there are plenty in the market. But if you read this post and have a WP8 give this app a go. It helps you work faster with the phone and I think the tiles make it a bit geekier. Now I feel happy of using those shortcuts and that makes me happy 730 times a year.


Will I come up with a good idea for an app?.

No comments:

Post a Comment

5.9.12

Dependency Property with Call-back and default value snippet

For me this is the way Microsoft should have created the DependencyProperty snippet from the beginning.
I use this one a lot and it has a placeholder for the default value and the call-back method I am even assigning the instance that called the call-back to a property in the snippet.
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>Dependency Property With CallBack</Title>
   <Shortcut>propdpCallBack</Shortcut>
   <Description>Creates a dependency property with it's callback method</Description>
   <Author>Jose Sanchez</Author>
   <SnippetTypes>
    <SnippetType>Expansion</SnippetType>
    <SnippetType>SurroundsWith</SnippetType>
   </SnippetTypes>
  </Header>
  <Snippet>
   <Declarations>
    <Literal>
     <ID>PublicName</ID>
     <Default>PropertyName</Default>
     <ToolTip>Public name of the depencency property</ToolTip>
    </Literal>
    <Literal>
     <ID>type</ID>
     <Default>string</Default>
     <ToolTip>Type of the property</ToolTip>
    </Literal>
    <Literal>
     <ID>ParentClassType</ID>
     <Default>UserControl</Default>
     <ToolTip>Type of the parent class of the dependency property</ToolTip>
    </Literal>
    <Literal>
     <ID>DefaultValue</ID>
     <Default>string.Empty</Default>
     <ToolTip>Default value of the dependency property</ToolTip>
    </Literal>
    <Literal>
     <ID>CallBackName</ID>
     <Default>PropertyCallBackName</Default>
     <ToolTip>Name of the callback method that will be triggered when the depencency property has changed</ToolTip>
    </Literal>
   </Declarations>
   <Code Language="csharp"><![CDATA[public $type$ $PublicName$
        {
            get { return ($type$)GetValue($PublicName$Property); }
            set { SetValue($PublicName$Property, value); }
        }

        // Using a DependencyProperty as the backing store for $PublicName$.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty $PublicName$Property =
            DependencyProperty.Register("$PublicName$", typeof($type$), typeof($ParentClassType$), new PropertyMetadata($DefaultValue$, new PropertyChangedCallback($CallBackName$)));


 private static void $CallBackName$(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            $ParentClassType$ Changed$ParentClassType$ = (obj as $ParentClassType$);

            //TODO: Implement some actions in Changed$ParentClassType$
        }]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>
I hope you find it as useful as I do.

By the way, you can find more info about how creating and importing your code snippets here.

No comments:

Post a Comment

12.1.12

About Windows Phone 7 and SharePoint authentication

Connecting the phone to our SharePoint environment was my Christmas project, and it took me a while... In the end I managed to do it and I thought it could be a good idea to share the joy. Besides, I have read a lot of blogs about this matter all of them? and I want to add my contribution to it.

Let's go to the matter. I created a class called SPAuthBridge from the code in the SDK that can be used to set up the connections to the SharePoint server.
using System;
using System.IO;
using System.Net;
using System.Text;

namespace PhoneUtils.Code
{
    public class SPAuthBridge
    {
        #region Properties
        public CookieContainer cookieJar = new CookieContainer();

        string SiteUrl, User;
        string Password; //This should be securestring, but I don't think it's available in WP7
        #endregion

        #region Constructors
        public SPAuthBridge(string SiteUrl, string User, string Password)
        {
            this.SiteUrl = SiteUrl;
            this.User = User;
            this.Password = Password;
        }
        #endregion

        #region Public Methods
        public void Authenticate()
        {
            try
            {
                if (string.IsNullOrEmpty(SiteUrl)) throw new ArgumentOutOfRangeException("The SPAuthBridge was not properly initialized");

                System.Uri authServiceUri = new Uri(string.Format("{0}/_vti_bin/authentication.asmx", SiteUrl));

                HttpWebRequest spAuthReq = HttpWebRequest.Create(authServiceUri) as HttpWebRequest;
                spAuthReq.CookieContainer = cookieJar;
                spAuthReq.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/Login";
                spAuthReq.ContentType = "text/xml; charset=utf-8";
                spAuthReq.Method = "POST";

                //add the soap message to the request
                spAuthReq.BeginGetRequestStream(new AsyncCallback(spAuthReqCallBack), spAuthReq);
            }
            catch
            {
                TriggerOnAuthenticated(false);
            }
        }
        #endregion

        #region Private Methods
        private void spAuthReqCallBack(IAsyncResult asyncResult)
        {
            string envelope =
                    @"<?xml version=""1.0"" encoding=""utf-8""?>
                    <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
                      <soap:Body>
                        <Login xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                          <username>{0}</username>
                          <password>{1}</password>
                        </Login>
                      </soap:Body>
                    </soap:Envelope>";

            UTF8Encoding encoding = new UTF8Encoding();
            HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
            Stream _body = request.EndGetRequestStream(asyncResult);
            envelope = string.Format(envelope, User, Password);
            byte[] formBytes = encoding.GetBytes(envelope);

            _body.Write(formBytes, 0, formBytes.Length);
            _body.Close();

            request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
        }

        private void ResponseCallback(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                Stream content = response.GetResponseStream();

                if (request != null && response != null)
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        using (StreamReader reader = new StreamReader(content))
                        {
                            //Put debugging code here
                            string _responseString = reader.ReadToEnd();
                            reader.Close();
                        }
                    }
                }

                //authentication complete
                TriggerOnAuthenticated(true);
            }
            catch
            {
                TriggerOnAuthenticated(false);
            }
        }
        #endregion

        #region Events
        public delegate void OnAuthenticatedHandler(bool Success);

        public event OnAuthenticatedHandler OnAuthenticated;

        protected virtual void TriggerOnAuthenticated(bool Success)
        {
            if (OnAuthenticated != null)
                OnAuthenticated(Success);
        }
        #endregion
    }
}

The way to use this class is fairly simple, you create the SPAuthBridge object, you call the Authenticate method and you are ready to go, something like this:

        StratexWP7SoapClient StratexWP7 = new StratexWP7SoapClient(); //This is my web service
        SPAuthBridge SharePointAuth;

        public MainPage()
        {
            InitializeComponent();

            (...)

            SharePointAuth = new SPAuthBridge(SiteUrl, Username, Password);
            SharePointAuth.OnAuthenticated += new SPAuthBridge.OnAuthenticatedHandler(SharePointAuth_OnAuthenticated);

            if (!string.IsNullOrEmpty(Password))
                SharePointAuth.Authenticate();
            else
                MessageBox.Show("The application should be configured before use.");
        }

        void SharePointAuth_OnAuthenticated(bool Success)
        {
            if (!Success)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                    { MessageBox.Show("There was an error on the authentication procedure. Please check the configuration."); });

                return;
            }

            StratexWP7.CookieContainer = SharePointAuth.cookieJar; //This is all you have to do to connect your web service. \m/ O.O \m/

            HookEvents();

            RequestData();
        }

It looks great when it works…

clip_image002clip_image002[4]
(By the way the charts are from AmCharts)

No comments:

Post a Comment

1.7.11

Debugging applications in the Phone after updating to “Mango”

I have just updated my phone to Windows Phone 7.1 codename “Mango”. I can’t say much about it because I haven’t had the time to test it properly, by now I’ll just say it’s faster.

 

Some of the features are not yet implemented and they have a “Coming soon!” but that’s not what I want to say here today.

 

After the upgrade I couldn’t debug my applications on my phone as before. The emulator was being launched (and this was causing all my vBox Virtual Machines to crash!)

 

After a while looking around I realised that it was easier than I thought… There’s a dropdown list up there for you to select how you want to debug:

image

You just have to chose Windows Phone Device and in my case unlock again the phone with the Developer Phone Registration tool and you are ready to go!

No comments:

Post a Comment

21.4.11

Debugging Windows Phone 7 applications

Finally I managed to upload and debug my application in my phone, but it wasn’t that easy…
First of all I found the instructions in this page http://msdn.microsoft.com/en-us/library/ff941089(v=vs.92).aspx#BKMK_Register
 
There, it says that you have to register in the App Hub before you are able to do anything with your phone (and it costs USD100…) the textual words are:

“You should register for membership as a Windows Phone developer before you begin creating applications because some development tasks, such as installing your application on a physical phone for testing, require App Hub membership”

Well this means you have to pay $100 only to be able to test the application… Pay before, decide later. I can’t say I like the philosophy.
 
Well, in the end my boss registered to the APP HUB with his personal Live ID. Huge mistake. We knew we could add up to three development phones, but no one told us that all of them should be linked to the same Live ID…  We thought you would be able to invite your developers to the App Hub or something like that, but no. And there’s no turning back, you can’t link your subscription to a different Live ID once you are registered… Not happy with this one either. Want to know how we solve it? With a great dose of trustfulness on his side.
 
Once you have your Live ID registered to the App Hub everything is easier…
 
I got to unlock and register the phone using  the Developer Phone Registration tool http://msdn.microsoft.com/en-us/library/ff769508(VS.92).aspx… Cool… Now what?
Well, now you have everything set up.
 
If you want to run your application in your phone anytime you want you have to deploy it with the “Application Deployment” tool as described in http://msdn.microsoft.com/en-us/library/gg588378(v=vs.92).aspx
 
Or if you prefer, you can hit F5 on your Visual Studio get the application running on my phone, and finally… Debug!

No comments:

Post a Comment

Creating a Expander Control for Silverlight for Windows Phone

I was migrating a solution to WP7 when I realized that there was not expander control available… I downloaded then the Silverlight  for Windows Phone Toolkit but realized that there wasn’t an expander there either.
 
I really couldn’t conceive a new version of the tool without expanders so I have created a rudimentary Expander for WP7. It's composed by a Grid, a StackPanel and a TextBlock.

It is very limited and of course it's not supposed to be a substitute of the good old Expander (it's focused to the specific problem I was trying to solve), but maybe it can help someone else out there.

The XAP code of the control is:
<Grid x:Name="LayoutRoot">
  <Border VerticalAlignment="Top" BorderBrush="#FF929EB0" BorderThickness="1" CornerRadius="2" Background="#FFE9E9E9" Margin="5,2">
            <Grid d:LayoutOverrides="Width">
    <Grid.RowDefinitions>
     <RowDefinition Height="Auto" MinHeight="20"/>
     <RowDefinition Height="Auto" MinHeight="5"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="Header" TextWrapping="Wrap" d:LayoutOverrides="Width" Foreground="#FF656565" Margin="15,2,0,0" VerticalAlignment="Top"/>
    <StackPanel x:Name="ChildrenPanel" VerticalAlignment="Top" d:LayoutOverrides="Width" Margin="0,2,0,0" Grid.Row="1"/>
   </Grid>
  </Border>
 </Grid>
The trick is adding a TextBlock in the header of the "expander" and subscribe to the MouseLeftButtonUp event of the TextBlock to control the folding and unfolding. I have also added a Folded property so I can externally set up the status of the expander.

If the expander is folded I add the items to the stack panel, if the expander is unfolded I remove the items. Sounds really simple, but I couldn't make it work. The StackPanel will remain the same size it was when it has all the items in it, but empty… without any Children… too ugly.

After a while trying to fake the behaviour changing the Height of the StakPanel I found the solution.

I was trying to delete all the items in the StackPanel at once with Children.Clear and that was driving it nuts. The solution was to remove the items inside the children one by one.

The code for the Expander class is here.
public partial class Expander : UserControl
 {
        bool _Folded;
        public bool Folded
        {
            get { return _Folded; }
            set
            {
                _Folded = !value;
                ExpanderSwitch();
            }
        }
        List ChildrenList;


  public Expander()
  {
   // Required to initialize variables
   InitializeComponent();

            ChildrenList = new List();

            Header.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(UserActivity_MouseLeftButtonUp);
  }

        void UserActivity_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            ExpanderSwitch();
        }

        private void ExpanderSwitch()
        {
            if (Folded)
            {
                foreach (var child in ChildrenList)
                    ChildrenPanel.Children.Add(child);
            }
            else
            {
                if (ChildrenList.Count == 0)
                    foreach (var item in ChildrenPanel.Children)
                        ChildrenList.Add(item);

                while (ChildrenPanel.Children.Count > 0)
                    ChildrenPanel.Children.RemoveAt(0);
            }

            _Folded = !_Folded;
        }
 }
And to use it you just have to add the control:
<Grid x:Name="LayoutRoot">
        <this:Expander x:Name="UserNews" />
    </Grid>
In code behind you can do something like this:
UserNews.Header.Text = GetHeader(News[0]);

            foreach (FrameworkNews news in News)
                UserNews.ChildrenPanel.Children.Add(new NewsDetail(news.Link, GetDetail(news), news.Where));

            UserNews.Folded = true;
If you have time you can add images and animations to the expander as well as a better "Folding Control"… Sadly I can't spend more time working on it, even though I'd love to :(

No comments:

Post a Comment

22.11.10

Code Snippets in C# and Why my TwoWay Bindings are not working?

Well, regarding to the problem with the TwoWay binding, today I’ve learnt that they don’t work magically; each property should implement the Property Change Notification, the INotifyPropertyChanged. It’s not that complex, It takes a little more time to declare the property this way (by the way, you have it perfectly explained here) and this leads us to the Code Snippets thing.

Creating code snippets is one of those things I’ve left to learn later a lot of times… until today that I had to create an object with more than thirty properties launching the PropertyChanged event and I couldn’t delay it further.
To increase my productivity I have wasted a little of time and I have created a code snippet for the properties implementing the INotifyPropertyChanged. I’ve called it propNot I’m this creative, I can’t help it
To do so, in Visual Studio, I went to Tools and from there to Code Snippets Manager. There I chose C# and then I copied the address of the My Code Snippets folder.

I created a .snippet file on that folder with this content
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>propNot</Title>
   <Shortcut>propNot</Shortcut>
   <Description>Code snippet for properties with NotifyPropertyChanged</Description>
   <Author>Chan</Author>
   <SnippetTypes>
    <SnippetType>Expansion</SnippetType>
    <SnippetType>SurroundsWith</SnippetType>
   </SnippetTypes>
  </Header>
  <Snippet>
   <Declarations>
    <Literal>
     <ID>privName</ID>
     <Default>privName</Default>
     <ToolTip>Private name of the property</ToolTip>
    </Literal>
    <Literal>
     <ID>pubName</ID>
     <Default>pubName</Default>
     <ToolTip>Public name of the property</ToolTip>
    </Literal>
    <Literal>
     <ID>type</ID>
     <Default>string</Default>
     <ToolTip>Type of the property</ToolTip>
    </Literal>
   </Declarations>
   <Code Language="csharp"><![CDATA[private $type$ $privName$;
        
    public $type$ $pubName$
    {
     get { return $privName$; }

     set
     {
      $privName$ = value;
      // Call NotifyPropertyChanged when the property is updated
      NotifyPropertyChanged("$pubName$");
     }
    }]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>
It’s tested and working, both the TwoWay binding and the Code Snippet.

Now that I know how to do them I think I’ll do them really usually. --EDIT-- I lied, I don't do them that usually because I am VERY lazy, but I do use this other snippet a lot:
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
 <CodeSnippet Format="1.0.0">
  <Header>
   <Title>INotify region with the necessary methods to implement the interface</Title>
   <Shortcut>inotifyregion</Shortcut>
   <Description>Creates region with the necessary methods to implement the interface INotifyPropertyChanged</Description>
   <Author>Jose Sanchez</Author>
   <SnippetTypes>
    <SnippetType>Expansion</SnippetType>
    <SnippetType>SurroundsWith</SnippetType>
   </SnippetTypes>
  </Header>
  <Snippet>
   <Declarations />
   <Code Language="csharp"><![CDATA[#region INotifyMethods
        // Declare the event
        public event PropertyChangedEventHandler PropertyChanged;

        // Create the OnPropertyChanged method to raise the event
        protected void NotifyPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion]]>
   </Code>
  </Snippet>
 </CodeSnippet>
</CodeSnippets>

No comments:

Post a Comment