Discussing the nuts and bolts of software development

Monday, May 11, 2009

 

XBAP - Using the Popup Control as a Dialog Box

How many people have experienced the modal pop up in desktop applications? They are commonly used across a wide variety of applications, one could say, too commonly used. While not appropriate for all situations, sometimes you need to use a pop up.

In XBAP applications the ability to open a pop-up is very limited; in many cases you will use navigation and multiple pages instead of separate windows. In most cases this is sufficient, but sometimes you really need to use a pop-up, and when you do a simple work around is to use the Popup control offered by WPF.

First, you define the Popup in the markup, making sure to set its StaysOpen property to true so it will remain open until you close it. (There’s no point in using the PopupAnimation or AllowsTransparency properties, because they won’t have any effect in a web page.) Include suitable buttons, such as OK and Cancel, and set the Placement property to Center so the popup will appear in the middle of the browser window.

Code sample:

<Popup Name="dialogPopUp" StaysOpen="True" Placement="Center" MaxWidth="200">
    <Border>
        <Border.Background>
            <LinearGradientBrush>
                <GradientStop Color="AliceBlue" Offset="1"></GradientStop>
                <GradientStop Color="LightBlue" Offset="0"></GradientStop>
            </LinearGradientBrush>
        </Border.Background>
        <StackPanel Margin="5" Background="White">
            <TextBlock Margin="10" TextWrapping="Wrap">Please enter your name.
            </TextBlock>
            <TextBox Name="txtName" Margin="10"></TextBox>
            <StackPanel Orientation="Horizontal" Margin="10">
                <Button Click="dialog_boxOK_Click" Padding="3" Margin="0,0,5,0">OK</Button>
                <Button Click="dialog_boxCancel_Click" Padding="3">Cancel</Button>
            </StackPanel>
        </StackPanel>
    </Border>
</Popup>

At the appropriate time (for example, when a button is clicked), disable the rest of your user interface and show the Popup. To disable your user interface, you can set the IsEnabled property of some top-level container, such as a StackPanel or a Grid, to false. (You can also set the Background property of the page to gray, which will draw the user’s attention to Popup.) To show the Popup, simply set its IsVisible property to true.

Here’s an event handler that shows the previously defined Popup:


private void popupTriggerButton_Click(object sender, RoutedEventArgs e)
{
    DisableMainPage();
}
 
private void DisableMainPage()
{
    mainPage.IsEnabled = false;
    this.Background = Brushes.LightGray;
    dialogPopUp.IsOpen = true;
}

When the user clicks the OK or Cancel button, close the Popup by setting its IsVisible property to false, and re-enable the rest of the user interface:


private void dialog_boxOK_Click(object sender, RoutedEventArgs e)
{
    // Copy name from the Popup into the main page.
    lblName.Content = "You entered: " + txtName.Text;
    EnableMainPage();
} 
 
private void dialog_boxCancel_Click(object sender, RoutedEventArgs e)
{
    EnableMainPage();
}
 
private void EnableMainPage()
{
    mainPage.IsEnabled = true;
    this.Background = null;
    dialogPopUp.IsOpen = false;
}

Using the Popup control to create this workaround has one significant limitation. To ensure that the Popup control can’t be used to spoof legitimate system dialog boxes, the Popup window is constrained to the size of the browser window. If you have a large Popup window and a small browser window, this could chop off some of your content. One solution is to wrap the full content of the Popup control in a ScrollViewer with the VerticalScrollBarVisibility property set to Auto.

If a Popup isn’t suitable for you, you can try another more “interesting” approach for showing a dialog box. Using the Windows Form library from .NET 2.0 you can safely create and host any WinForm control in your WPF application. In this case, you can create and show an instance of the System.Windows.Forms.Form class (or any custom form that derives from Form), because it doesn’t require unmanaged code permission. In fact, you can even show the form modelessly, so the page remains responsive. The only drawback is that a security balloon automatically appears superimposed over the form and remains until the user clicks the warning message. You’re also limited in what you can show in the form. Windows Forms controls are acceptable, but WPF content isn’t allowed.

Labels: , , , , ,


Monday, March 09, 2009

 

Single Instance Applications in WPF

In some design scenarios launching multiple copies of the same WPF application is a problem, especially for document-based applications or server applications.

WPF does not provide a native solution for single instance applications; however there are several workarounds for this issue.

The most commonly used solution is to check whether another instance of the application is already running when the Application.Startup event fires. This is easily done using a system wide mutex (provided by the operating system to allow interprocess communication). While this is easily done, it limits the developer’s options by not providing a way for the new instance to communicate with the already running instance. Mainly this will provide only a simple way of limiting the number of running instances to one, while a separate system will be required to handle the new calls (usually through remoting or Windows Communication Foundation).

The recommended and more useful approach is to use the built-in support that’s provided in Windows Forms and originally intended for Visual Basic applications. This approach handles the messy plumbing behind the scenes. This means using an old style application class as wrapper for the WPF application. The wrapper will handle the instance management and will communicate the request to the already running instance of the WPF application.

Solution steps:

1) Add a reference to the Microsoft.VisualBasic.dll assembly.

2) Add a new custom class derived from the Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase class.

3) The IsSingleInstance must be set to true in the constructor. This enables a single instance application.

4) Override the OnStartup() method to create the WPF application object. (Note: The OnStartup() method is triggered when the application starts)

5) Overide the OnStartupNextInstance() method to handle future instances. (Note: The OnStartupNextInstance() method is triggered in when another instance of the application starts up)

6) Define a Main entry point for the application and create the wrapper object

7) Create the WPF application class

Sample of the application wrapper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualBasic.ApplicationServices;
using System.Windows;
 
namespace SingleInstanceApplication
{
    //The Main entry point class
    public class Startup
    {
        [STAThread]
        public static void Main(string[] args)
        {
            SingleInstanceAppWrapper wrapper = new SingleInstanceAppWrapper();
            wrapper.Run(args);
 
        }
    }
 
    //The old-style application wrapper
    public class SingleInstanceAppWrapper : WindowsFormsApplicationBase
    {
        public SingleInstanceAppWrapper()
        {
            // Enable single-instance mode.
            this.IsSingleInstance = true;
        }
 
        // Create the WPF application class.
        private WPFApplication _app;
 
        //Override OnStartup() method to create the WPF application object
        protected override bool OnStartup(
             Microsoft.VisualBasic.ApplicationServices.StartupEventArgs e)
        {
            _app = new WPFApplication();
 
            _app.Run();
 
            return false;
 
        }
 
        // Override OnStartupNextInstance() to handle multiple application instances.
        protected override void OnStartupNextInstance(
             Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs e)
        {
            //In case of command line arguments, send them to the WPF application object    
            if (e.CommandLine.Count > 0)
            {
                _app.HandleCommandLine(e.CommandLine);
            }
 
 
        }
    }
}

As you can see, the sample above contains the Main entry point for the application. This is required because the wrapper must be created first.
If Visual Studio is used, by default the App.xaml application definition style is used. This will not work with the wrapper because the App.xaml approach already has a Main entry point.
Remove App.xaml and App.xaml.cs from the project and create a new class for the entry point.


The only thing left to do is to create the WPF application definition:



public class WPFApplication : System.Windows.Application
{
 
    //Override the OnStartup() method
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        base.OnStartup(e);
        
        //Load the main window
        Window _MainWindow = new Window();
        _MainWindow.Show();
 
        //Set the MainWindow property for the WPF application object
        this.MainWindow = _MainWindow;
 
    }
 
    //Method used to handle command line arguments
    public void HandleCommandLine (System.Collections.ObjectModel.ReadOnlyCollection<string> e)
    {
        //Code to handle command line arguments from other instances goes here
        
    }
 
}

Because the wrapper approach does not contain a XAML application definition (App.xaml), if you need to load application level resources, the following code can be placed the OnStartup() of the WPF application definition to load the resources from a resource dictionary:



 
        Application.Current.Resources.MergedDictionaries.Add(
            Application.LoadComponent(
                new Uri("AssemblyName;component/ApplicationResourceDictionary.xaml",
                UriKind.Relative)) as ResourceDictionary);

The above code will work for dynamic resource definitions. The static resource lookup process will fail because the source for the application resources dictionary need to be specified. In that case the code to load the resources will be replaced with the following:



Application.Current.Resources.Source = 
    new Uri("/AssemblyName;component/ApplicationResourceDictionary.xaml", UriKind.Relative);

Note: AssemblyName is a placeholder for the actual assembly name. The Uri for the resource dictionary location can be changed as required.

Labels: , , , , ,


Friday, September 12, 2008

 

Bridge SharePoint - User Profiles and User Profile Properties (part 2 of 2)

As we have found out in part 1, SharePoint can manipulate data using the user profile management objects and can accept data from external sources. Using these two features apart we have two limited tools with limited usage, but using them together gives us a powerful, flexible and efficient method of storing and using data from any internal or external resource.

It’s time to see this solution to our problems in action.

Code example of getting/setting the profile property from a C# application

The concept is the same for any source of information (web services, web applications, etc.):
Get the user’s profile->Get the required profile property->Get/Set profile property value

Code example: (note that to access profile properties, this code must run with elevated privileges)

string value;
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{

// Change the site address for different deployment environments
// Note: WIN2003STD is a place holder for your environment, usually your server name
SPSite site = new SPSite("http://WIN2003STD/");
SPWeb web = site.OpenWeb();

// Get the profile manager object for the site
UserProfileManager profileManager = new UserProfileManager(ServerContext.GetContext(site));

// Use the username from the User Information Item to get the full profile of an user
UserProfile user_profile = profileManager.GetUserProfile(System.Web.HttpContext.Current.User.Identity.Name);

// Get the required profile property value from the profile
value = user_profile["MyProperty"].Value.ToString();


});
}
catch (Exception e)
{
return "Error getting user info: " + e.Message.ToString();
}

user_profile["MyProperty"].Value gets or sets the profile property value.

This is a very convenient way of using profile properties to store required information for each user. Properties can be set as read-only or not to appear in the user’s profile, which gives you even more control.

System.Web.HttpContext.Current.User.Identity.Name returns the username (the login username) and it is used in the code example above to get the profile of the currently logged-in user.

Tools You Never Knew You Had… (and what to do with them now that you’ve wised up!)

With this simple solution to getting/setting a user’s profile property value, endless opportunities are now at hand. This small code snippet helps developers control, validate and use values stored for each user, without corrupting the database or resorting to other more complex and error-prone solutions.
For those not needing to use an external application to get information from the users, a custom SharePoint web application can be developed and deployed on SharePoint. This way all the controls offered by ASP .NET or custom controls can be used to perform required operations on the data before it’s stored.

External applications running on the server can access this information the same way, so a bridge between SharePoint and applications like web sites, game servers, messaging apps, etc. can be easily created.

SharePoint Complications, As Usual

Watch out that the profile property might be set as read-only in SharePoint. Even if it’s the case however, the above code should still be able to access the profile because it’s running with elevated privileges. So if there is a situation in which the user is allowed to see the data but only modify it by using a service (like a web application or web service), this is a good way of doing it.

Another gotcha is that the code above can only be used on the SharePoint server machine. This is due to the framework that SharePoint uses. To get information or to change data from a network on internet location, a SharePoint custom web service or web application can come in handy. Other ways of passing information will work too, like server-client applications, as long as the part running the above code is on the SharePoint server machine.

So we now have a way to store data and manipulate it according to our needs. We can control it, we can validate it and most important of all, we decide how the user interacts with the data.

We’re now ready to start doing some serious SharePoint development!

Labels: , , , , ,


Thursday, July 10, 2008

 

Compile Time Semantic Checking

C# is a TypeSafe language, what about SemanticSafe?

In a project I'm working on we have a lot of database records ids being passed around as integers. Today I needed to add a new record id to this list. Adding the new item to the function signature is easy. The hard part comes in making sure the right value is being assigned.
Public void MyFunction(int TableA_ID, int TableB_ID, String someOtherValue);

int A_ID = 5;
int B_ID = 4;
MyFunction(B_ID, A_ID, "Hello World");

So what happens when I compile? Nothing, it works fine. Yet that line where I called the function has just corrupted the entire database because those database ID's were transposed. Which kinda sucks.

It's one thing to guarantee type safety, what what about semantic safety? An idea popped into my head to use the existing type-checking to accomplish this.
    public struct UserID
{
private int value;
public static implicit operator int(UserID source)
{ return source.value; }

public UserID(int intValue)
{
value = intValue;
}
}

private void TryToFail(UserID test) { }

UserId works = new UserId(5);
int fails = works;

TryToFail(works) //compiles fine
TryToFail(fails) //Doesn't compile

Even though both works and fails contain the exact same value, the function can be made to fail if the values are put in the wrong order. I used an implicit operator so that I didn't lose the convince of having a simple integer, neato.

Obviously this technique is not something you would want to use for every possible field as there is one extra step involved in getting the value. But if you only did this for the record IDs I think it would be enough gain to make it worthwhile.

I would like to think of a way to do this so that it has compile time checking, but once compiled only contains the primitive value. Any ideas?

Labels: ,


This page is powered by Blogger. Isn't yours?