VisualTreeHelper Class in Silverlight

by mgordon 20. February 2009 08:37

It’s hard for me to believe I’d never encountered the situation before, but I was recently working on a part of a Silverlight application where I’d used a ListBox and specified an ItemTemplate within it containing a CheckBox and a TextBlock.  When a particular event fired, I wanted to iterate through all the ListBox Items, examine the Text property of the TextBlock and based on some simple logic, either check or uncheck the CheckBox.  My XAML for the ListBox looked like this:

<ListBox Margin="-5.057,34.225,0.116,0.121" Grid.Row="1" x:Name="lbRoles">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal" >
                <CheckBox Checked="CheckBox_Checked"></CheckBox>
                <TextBlock Margin="5,0,0,0" Text="{Binding Path=RoleName}" FontWeight="Bold" 
Width="Auto" HorizontalAlignment="Stretch"
LineStackingStrategy="BlockLineHeight" LineHeight="15"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

So, basically, I was binding the ListBox to a collection and populating the TextBlock’s Text property from the binding.  My first task was to figure out how to access the ListBoxItem’s since The ListBox’s Items property would return the collection it was bound to and not the generated Items. I did some research and came across a newsgroup thread, here that contains an example of using the VisualTreeHelper class

In that thread, there is method defined like so:

private void GetChildren(UIElement parent, Type targetType, ref List<UIElement> children)
{
    int count = VisualTreeHelper.GetChildrenCount(parent);
    if (count > 0)
    {
        for (int i = 0; i < count; i++)
        {
            UIElement child = (UIElement)VisualTreeHelper.GetChild(parent, i);
            if (child.GetType() == targetType)
            {
                children.Add(child);
            }
            GetChildren(child, targetType, ref children);
        }
    }
}


You’ll notice that the method requires you to specify a type of control you’re interested in and also that it uses recursion to walk through every control in the visual tree beneath the specified parent.  I quickly found there is a vast hierarchy of controls created for the ListBox beyond what you’ll see in your XAML so it makes sense to recurse the tree of controls and only return what you’re needing.  I added this method to my class and used it for the above described problem like this:

List<UIElement> items = new List<UIElement>();
GetChildren(lbRoles, typeof(ListBoxItem), ref items);

foreach (ListBoxItem item in items)
{
    List<UIElement> children = new List<UIElement>();
    GetChildren(item, typeof(CheckBox), ref children);
    CheckBox cb = (CheckBox)children[0];

    children.Clear();
    GetChildren(item, typeof(TextBlock), ref children);
    TextBlock block = (TextBlock)children[0];

    if (_currentUserRoles.Contains(block.Text))
    {
        cb.IsChecked = true;
    }
    else
    {
        cb.IsChecked = false;
    }
}

So, using VisualTreeHelper and the helper method, I’m able to iterate through all the ListBoxItems and within each, to get a reference to the CheckBox and TextBlock within it.

Tags:

Silverlight | .Net

Using The ASP.Net Membership, Role And Profile Providers

by mgordon 17. February 2009 06:25

I’ve used these providers quite a bit over the past few years with good success.  As a simple overview, the Membership provider offers functionality that allows you to authenticate users against a store of users, the Role provider lets you authorize users to perform actions on your site based on roles they have been assigned and the Profile provider allows the saving of additional user details.  By using these providers, you can relieve yourself of having to roll your own functionality for these purposes.  I have found the Scott Mitchell’s tutorial located here to be an invaluable resource, but I have also picked up some tricks along the way.  Below, I’ll explain in a brief way how to set these providers up to use SQL Server as the data store. 

aspnet_regsql

First of all, we’ll need a place to store our information.  A utility is provided with the 2.0 version of the framework called aspnet_regsql.exe that will create all the required database objects for you.  The first decision you need to make is whether this information will live in your application’s database or in a separate database.  If your intention is to use the same user base across several applications, I’d elect to create a separate database for this information, otherwise I’d place the objects along side my application data.

Run the utility and you’ll be presented with the following screen.

regsql_screen1

Click Next> to continue the wizard.

regsql_screen2

Select the top radio button since we’re adding the database objects, not removing them.  Click Next>.

regsql_screen3

Select the SQL Server to install the objects to, credentials to authenticate (NOTE: The credentials used must have rights to create tables, views, stored procedures, etc on the specified database) and database.  If the database already exists, the objects will be created in it.  If the database specified does NOT exist, it will be created and then the objects will be created in it.  Click Next>.

regsql_screen4

This screen gives you an opportunity to review your choices.  If the information is correct, click Next>, otherwise click <Previous and correct your choices.  Clicking Next, here, creates the objects in the specified database.  Objects for all providers are created in the database.  You select the ones you wish to incorporate into your application by specifying them in the web.config file.  Let’s look at how to set up the providers.

Setting Up The Providers

The first thing we’ll need to do, is tell the providers where to find their data.  We do this by specifying a connection string in web.config.

<connectionStrings>
    <add name="Connection" connectionString="Data Source=ProviderDatabase; 
initial catalog=ProviderDB;
uid=user; password=pass"/> </connectionStrings>

All the providers we specify in web.config will reference this connection string section.

Now, we need to start configuring our providers.  We so this in the <system.web> section of the web.config file.

<roleManager enabled="true">
  <providers>
    <remove name="AspNetSqlRoleProvider"/>
    <add connectionStringName="Connection" applicationName="/MyWebApp" 
name="AspNetSqlRoleProvider"
type="System.Web.Security.SqlRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
"/> <remove name="AspNetWindowsTokenRoleProvider"/> <add applicationName="/MyWebApp" name="AspNetWindowsTokenRoleProvider"
type="System.Web.Security.WindowsTokenRoleProvider,
System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
"/> </providers> </roleManager> <profile> <providers> <remove name="AspNetSqlProfileProvider"/> <add name="AspNetSqlProfileProvider" connectionStringName="Connection"
applicationName="/MyWebApp"
type="System.Web.Profile.SqlProfileProvider, System.Web,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
"/> </providers> </profile> <membership defaultProvider="CustomizedProvider"> <providers> <clear/> <add name="CustomizedProvider" type="System.Web.Security.SqlMembershipProvider"
connectionStringName="Connection" applicationName="/MyWebApp"
minRequiredPasswordLength="5" minRequiredNonalphanumericCharacters="0"
enablePasswordRetrieval="false" enablePasswordReset="true"
requiresUniqueEmail="true" maxInvalidPasswordAttempts="4"
passwordAttemptWindow="10" requiresQuestionAndAnswer="false"/> </providers> </membership> <authentication mode="Forms"> <forms loginUrl="Login.aspx"/> </authentication> <authorization> <deny users="?"/> </authorization>

In the above, I’m specifying that I want to use the Role, Profile and Membership providers.  Notice the use of <remove> and <clear> tags.  The way the config files work is that they start at the machine level (machine.config) and get settings from there.  Then the web.config for the root web site is combined with that and so on down the hierarchy until the level at which your application sits.  There is a chance that a conflicting configuration will exist at some level above your site that will prevent the providers from working properly.  The <remove> and <clear> tags remove any existing settings so our providers remain as the only ones defined.

Next, notice how the each of the providers requires an attribute, connectionstringname.  This should always be the name of the connection string section that points to the database where your provider objects were created.  In our case, above, we created a connection string section named “Connection”, so that’s the section we specify to each of the providers.  Each provider also requires an attribute of applicationName.  Two things to remember, here.  First, this needs to be the actual name of your site and the second is to always preface this name with a forward slash if the application is sitting somewhere below the root of the site.  Since it’s possible for you to use the same provider database for multiple sites, the site name is used by the providers to determine which set of settings apply.  If you forget the forward slash for a site below the root, you’ll find that you get duplicate application records in your database…one with and one without a slash for the application name.

The Membership provider allows quite a lot of configuration.  The example, above, specifies many of the allowable settings but not all.  I highly recommend you look at the documentation to see all that can be specified.

Since we are going to use the Membership provider to log our users into the site, we need to set up forms authentication.  This is being done in the authentication and authorization tags.  Here, we’re saying we’re not allowing anyone to access the site unless they have logged in and if they have not yet logged in, we redirect them to the Login.aspx page.  On this page, you can simply drag on the login control from the toolbox.  No further setup is required.  The control is smart enough to get all it needs from the web.config file.  When the user logs in, the Membership provider will automatically be used and the user will be authenticated against the database.

Since we specified the Login.aspx page as the login page, users automatically get access to it without having to be logged in.  What if we wanted to use some of the other controls provided with ASP.Net to manage our account like password recovery or changing passwords?  We can do this by creating the pages for the purpose at hand and dragging the appropriate control onto it.  All controls of this type will know how to use the provider automatically.  However, if a user should be able to access the page without having logged in, first (password recovery, for example), we’ll need to allow for this.

As we have configured the site, users can only access Login.aspx without having logged in and all other content is inaccessible to non-authenticated users.  We need to make an exception of the pages in question so the rules don’t apply to them.  To do this, we create a <location/> section in web.config. Just below the <system.web/> section, add the following.

<location path="RecoverPassword.aspx">
  <system.web>
    <authorization>
      <allow users="*"/>
    </authorization>
  </system.web>
</location>

This section, in effect, says, “For this one page, allow all users".”

Tags:

.Net | Productivity | Sql Server 2005

ActiveX Killbits Problem with Reporting Services Printing

by mgordon 13. February 2009 08:01

We just found and corrected a problem with printing from the reporting services web interface.  When the user clicked the print button, they received a popup message saying ""Unable to load client print control”.  As we found out, the problem was created when the users’ desktops installed a hotfix from Microsoft, KB956391.  We had two choices upon finding this out; we could remove the patch from every workstation or install a subsequent hotfix to Sql Server.  We opted for the latter and installed the hotfix located here.  Printing seems to work perfectly, now.

Tags:

Database | Sql Server 2005

Powered by BlogEngine.NET 1.5.0.7
Theme by Extensive SEO