Second Stanza

July 26, 2010

MVVM – MEF – Reactive Data Grid – Part IV

Filed under: MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 7:01 am

In MVVM, the View is meant to be isolated from other parts of the model. The goal for the View is that It contains user interface and not code. This is a good goal, obviously, since it allows designers to work separately from developers and allows for unit testing other parts of the model separate from the design.

In MVVM, events that originate in the view are usually handled in the view model. The idea of event delegation through a command interface has become popular for addressing this issue within MVVM. For example, the Prism framework provides a DelegateCommand that can be bound from the View which in turn executes a method implemented within the View Model. The MVVM Light Toolkit expands event delegation more by supporting a wider array of events. These solutions allow the user to declaratively express how events should be handled.

And anybody who has used these knows the difficulties in addressing more complex eventing scenarios, beyond just doing something when the user clicks a button.

One of the benefits of the Reactive Extensions for .NET (Rx) is the ability to do interesting things with events. You can compose events from multiple sources.  You can filter and transform events. You can orchestrate different UI elements. Rx gives you a great tool for managing the asynchronous nature of event handling.I think the Rx extensions provides a better mechanism for dealing with UI events that the current MVVM solutions. Yes, a developer is putting code into the code behind for the View, but the benefits of Rx outweigh this in my mind.

In the MVVMGrid application, the columns to be displayed in the grid are stored in settings files on the web server, but these settings, in fact, map to a very specific configuration of the controls contained within the View. The View knows about these controls (the DataGridColumn controls) so it makes sense that the View should be the one to manipulate these controls.

Let’s examine how this is used in the MVVMGrid application.

First, the GridViewModel exposes a ViewSettings property that contains the settings for the view the user has currently selected. The GridView needs to know when this property changes so that it can update the view. The bindings expressed in the GridView use the INotifyPropertyChanged interface to determine when the value of a property has changed. The GridView itself can use this same interface to determine when the view needs updating.

        [Import]
        public GridViewModel ViewModel
        {
            get { return DataContext as GridViewModel; }
            set { DataContext = value; OnViewModelChanged(); }
        }

        private IDisposable notifyChangedSubscription;

        private void OnViewModelChanged()
        {
            if( notifyChangedSubscription != null )
            {
                notifyChangedSubscription.Dispose();
                notifyChangedSubscription = null;
            }
            if( ViewModel != null )
            {
                notifyChangedSubscription =
                    Observable
                        .FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
                            h => new PropertyChangedEventHandler( h ),
                            h => ViewModel.PropertyChanged += h,
                            h => ViewModel.PropertyChanged -= h )
                        .Where( evt => evt.EventArgs.PropertyName == "ViewSettings" )
                        .Subscribe( evt =>
                        {
                            UpdateColumns();
                        } );
            }
        }

When the ViewModel property is initially set, the OnViewModelChanged method subscribes (using the Rx extensions) to the PropertyChanged event. It filters out the other property changed notifications except for the one we need.  Finally, when the event occurs, the UpdateColumns method is called to update the view.

The UpdateColumns method uses a LINQ query to map from the column definitions in the view settings to the DataGridColumn controls contained within the data grid, showing or hiding the columns and setting the prescribed width.

        private static string GetColumnID( DataGridColumn gridColumn )
        {
            return (string)gridColumn.GetValue( FrameworkElement.NameProperty );
        }

        private void UpdateColumns()
        {
            int displayIndex = 0;
            ViewModel.ViewSettings.Columns
                .GroupJoin(
                    dataGrid.Columns,
                    vc => vc.ColumnID,
                    gc => GetColumnID( gc ),
                    ( vc, gc ) => Tuple.Create( vc, gc.FirstOrDefault() ) )
                .Concat(
                    dataGrid.Columns
                        .Where( gc => !ViewModel.ViewSettings.Columns
                                            .Where( vc => GetColumnID( gc ) == vc.ColumnID )
                                            .Any() )
                        .Select( gc => Tuple.Create( ViewColumnSettings.Null, gc ) ) )
                .ToList()
                .ForEach( t =>
                {
                    ViewColumnSettings viewCol = t.Item1;
                    DataGridColumn gridCol = t.Item2;

                    if( viewCol == null )
                        gridCol.Visibility = System.Windows.Visibility.Collapsed;
                    else
                    {
                        gridCol.Visibility = System.Windows.Visibility.Visible;
                        gridCol.Width = new DataGridLength( viewCol.ColumnWidth, DataGridLengthUnitType.Pixel );
                        gridCol.DisplayIndex = displayIndex++;
                    }
                } );
        }

I grant that this may not be the best example of the benefit of using the Rx extensions within the View, but it shows the basic idea. The bottom line is that I think that there are some great benefits of using Rx in this way and perhaps we should take a second look our View implementations.

Agree/Disagree with me? Please add your comments!

Advertisement

July 25, 2010

MVVM – MEF – Reactive Data Grid – Part III

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 12:18 am

There are a number of ways to retrieve data from a web service. Silverlight provides this functionality through:

The Reactive Extensions for .NET (Rx) builds upon this by providing functionality that allows for composing asynchronous and event-based applications. The MVVMGrid application uses the Rx extensions for the grid services and in certain event handling. I’ll focus on the grid services first.

An application service makes a call out to a web service and provides a response to the application. It must also provide some mechanism to notify the application that a service has failed for some reason. In the MVVMGrid application, the GridService class exposes its methods using IObservable collections. So instead of using an event or an asynchronous interface, a client to the service simply calls the Subscribe method to obtain the response from the service or a failure status.

Let’s examine the grid data service. The grid’s data is generated randomly by the application, but could have easily been retrieved from a web service.

        public IObservable<GridDataCollection> GetGridItems()
        {
            return
                Observable
                    .Repeat( 0, 1 )
                    .Select( _ =>
                    {
                        GridDataCollection gridDataCollection = new GridDataCollection();
                        gridDataCollection.AddRandomData( 100 );
                        return gridDataCollection;
                    } )
                    .SubscribeOn( Scheduler.ThreadPool );
        }

Here

  • The integer 0 is repeated once in order to get an IObservable collection started.
  • A GridDataCollection object is created, 100 randomly-generated GridData items are inserted into it, and then it is selected.
  • The subscription is moved to a worker thread so that the generation of these elements does not occur on the UI thread (which could make the Silverlight application less responsive).

In the GridViewModel, the OnImportsSatisfied method is used to initiate the process of obtaining data from the services.

        private void SetExceptionMessage( Exception ex )
        {
            ErrorMessageOne = String.Concat( "Error: ", ex.GetType().FullName );
            ErrorMessageTwo = ex.Message;
        }

        public void OnImportsSatisfied()
        {
            ...

            Service
                .GetGridItems()
                .ObserveOnDispatcher()
                .Subscribe( d =>
                {
                    DataItems = new ObservableCollection<GridData>( d );
                },
                SetExceptionMessage );
        }

The GetGridItems method of the service object returns an IObservable collection.  Before subscribing, I ensure that the subscription handlers will be invoked on the UI thread (since I will be changing user interface elements). Finally, I subscribe to the IObservable interface and provide it two of the three possible handlers for handling the response: the OnNext handler and the OnError handler (the OnCompleted handler is not required in this case). On a successful response, the DataItems property on the GridViewModel is populated with the items to display. On an error response, I simply set the error message properties which will, in turn, be displayed below the grid.

Now let’s look at a more complicated example.

The “views” for the data grid are defined in XML files located on the web server. The GridService class is used to fetch both the list of all views as well as the individual views.

        private const string ListFileName = "/Data/Settings.xml";
        private readonly Uri WebSiteUri = HtmlPage.Document.DocumentUri;

        public IObservable<ViewSettingsCollection> GetViewSettingsCollection()
        {
            Uri listFileUri = new Uri( WebSiteUri, ListFileName );
            WebRequest httpReq = HttpWebRequest.Create( listFileUri );
            return
                Observable
                    .FromAsyncPattern<WebResponse>( httpReq.BeginGetResponse, httpReq.EndGetResponse )()
                    .Select( r =>
                    {

                        XDocument rspDoc;
                        using( Stream rspStream = r.GetResponseStream() )
                            rspDoc = XDocument.Load( rspStream );

                        return
                            rspDoc
                                .Element( "settings" )
                                .Elements( "items" )
                                .Select( items =>
                                    new ViewSettingsCollection {
                                        DefaultViewSettingsID = (int?)items.Attribute( "default" ),
                                        Items = new List<ViewSettingsCollectionItem>(
                                            items
                                                .Elements( "item" )
                                                .Select( item => new ViewSettingsCollectionItem {
                                                    ID = (int)item.Attribute( "id" ),
                                                    Description = (string)item.Attribute( "desc" ),
                                                    Link = (string)item.Attribute( "link" )
                                                })
                                        )
                                })
                                .Single();

                    } );
        }

An HttpWebRequest object is used to fetch the XML file from the Data folder on the web server. The FromAsyncPattern method is used to wrap the BeginGetResponse and EndGetResponse methods of the request object. When the response is received, LINQ queries are used to create a new ViewSettingsCollection object. The new instance of the ViewSettingsCollection object is then selected for the observable collection.

Back in the GridViewModel, the service is called to fetch the ViewSettingsCollection object.

            Service
                .GetViewSettingsCollection()
                .ObserveOnDispatcher()
                .Subscribe( vsc =>
                {
                    SettingsCollection = vsc;
                    SelectedSettings = SettingsCollection.GetDefaultSettings();
                },
                SetExceptionMessage );

Again, the subscription handlers will be invoked on the UI thread and the OnNext handler will simply set properties on the GridViewModel. Notice that the SelectedSettings property is also set. When this property is changed, either here or when the user changes the selected view in the drop-down box, the setter for this property calls the grid service to get the details of the view.

        public ViewSettingsCollectionItem SelectedSettings
        {
            get { return selectedSettings; }
            set
            {
                selectedSettings = value;
                OnSelectedSettingsChanged();
                OnPropertyChanged( "SelectedSettings" );
            }
        }

        ...

        private void OnSelectedSettingsChanged()
        {
            if( SelectedSettings != null )
            {
                Service
                    .GetViewSettings( SelectedSettings )
                    .ObserveOnDispatcher()
                    .Subscribe( vs =>
                    {
                        ViewSettings = vs;
                    },
                    SetExceptionMessage );
            }
        }

This allows us to set up the columns to display in the data grid.  I’ll examine this issue next.

July 24, 2010

MVVM – MEF – Reactive Data Grid – Part II

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 11:12 pm

The MVVMGrid application is architected using the Model-View-ViewModel (MVVM) pattern. So here are the parts:

  • GridView.xaml – this is the view containing the data grid. Visual elements are bound to data contained within the GridViewModel. The GridView knows about the properties that the GridViewModel exposes.
  • GridViewModel – this is the view model which is used as the DataContext of the GridView. The GridViewModel knows nothing about the GridView class.
  • GridData, ViewSettings, etc. – these are classes that define the data model that the application uses.
  • GridService – this is the class that provides the services to fetch the grid data and the view information.

We use the Managed Extensibility Framework (MEF) to stitch these pieces together. First, we need to create a CompositionContainer to handle the imports and exports of the various components. In this application, we only need to look in the one assembly for components, so only a single AssemblyCatalog is needed.

    public partial class App : Application
    {
        private CompositionContainer partsContainer;

        [Import]
        public UserControl MainView { get; set; }

        public App()
        {
            this.Startup += this.Application_Startup;
            this.Exit += this.Application_Exit;
            this.UnhandledException += this.Application_UnhandledException;

            InitializeComponent();
        }

        private void Application_Startup( object sender, StartupEventArgs e )
        {
            partsContainer = new CompositionContainer( new AssemblyCatalog( typeof( App ).Assembly ) );
            partsContainer.SatisfyImportsOnce( this );

            RootVisual = MainView;
        }

Normally in Silverlight, we would set our RootVisual to a new UserControl we create in the application startup. Here, we allow MEF to find our root visual for us. How does it know which UserControl to create? The App class defines an Import attribute with a type of UserControl. The SatisfyImportsOnce method goes into the catalog we’ve supplied to it and finds a UserControl that is marked for export. Our GridView control is marked with the Export attribute and thus MEF is able to find it.

    [Export(typeof(UserControl))]
    public partial class GridView : UserControl
    {
        [Import]
        public GridViewModel ViewModel
        {
            get { return DataContext as GridViewModel; }
            set { DataContext = value; OnViewModelChanged(); }
        }


Similarly, our GridViewModel is imported into our GridView, setting it as the DataContext for the view and thus enabling the data binding to work. The GridViewModel then imports the GridService.

    [Export]
    public class GridViewModel : INotifyPropertyChanged, IPartImportsSatisfiedNotification
    {
        [Import]
        public GridService Service { get; set; }

Notice that the GridViewModel implements a couple of important interfaces, INotifyPropertyChanged and IPartImportsSatisfiedNotification. The first interface is used in data binding.  When properties on the GridViewModel change, the PropertyChanged event is fired. This allows data bindings to know that the data that is bound has changed and the user interface element needs to be updated.

The second interface is related to MEF. If an exported component implements this interface, then MEF will call the OnImportsSatisfied method on the object when all of the imports for the object have been satisified and the object is safe to use. In our application, we use this method to begin retrieving the data from our services that will be used in the data grid.

The source code for this project can be downloaded from CodePlex.

MVVM – MEF – Reactive Data Grid – Part I

Filed under: .NET Development, MEF, MVVM, Rx Extensions, Silverlight — Tags: — dfbaskin @ 10:27 pm

I want to explore some of the issues with MVVM, MEF, and Reactive Extensions in developing .NET applications. I’ve created an example Silverlight application that employs these technologies in some interesting ways. I’ll use this example as the basis for our discussion.

First, an overview.

The application displays a simple data grid. The grid itself has a defined set of columns that it can display (bound to the data elements that it knows the data source contains). The actual columns that are displayed and the order of these columns can be modified by selecting a different “view” for the data grid. The user selects the view through a drop-down box above the grid.

MVVMGrid

For this example application, the data is built using random values for few data types: strings, boolean, and DateTime. The views are requested from the server and update the visibility, display order, and width of the grid columns. Even though actual web services are not used, this application provides a simulated service interface to obtain these data elements.

This project was built using Visual Studio 2010. The source code can be downloaded from CodePlex.

Here are the links to the posts in this series:

June 19, 2010

WCF Client Functionality and Silverlight

Filed under: Silverlight, WCF — dfbaskin @ 10:22 pm

I’ve been looking into Windows Communications Foundation (WCF) functionality, specifically focusing on client features for .NET and Silverlight clients, as a foundation for creating business applications.

So why wouldn’t I just use the WebClient or HttpWebRequest classes? These classes are simple to understand and use. And they don’t introduce the complexity of the WCF framework.

This is true, of course, but WCF offers flexibility that business applications require to implement additional management and interoperability features. Specifically, WCF offers a host of extensibility points that business applications can make use of, such as custom validation, service logging, and data manipulation.

This flexibility is also important where business applications have to rely on outside services. Third-party REST and SOAP services are more ubiquitous and integrating with these services is a common business requirement. These services are a "black box" and your application must manage change that is beyond your control.

Here are a few links to information related to WCF client functionality:

So I hope to post some code examples soon exploring some aspects of WCF client programming.

Sequential Asynchronous Workflows in Silverlight Using Rx Framework

Filed under: .NET Development, Rx Extensions, Silverlight — Tags: , — dfbaskin @ 4:44 pm

Jeremy Likness has an excellent post called Sequential Asynchronous Workflows in Silverlight using Coroutines. It seems like this example would also work well using the Reactive (Rx) Extensions Framework.

Here’s the source for an Rx handler of the RandomNumberService:


        /// <summary>
        ///     Once we have the references, start the workflow (the Reactive Way!)
        /// </summary>
        public void OnImportsSatisfied()
        {
            int alpha = 0, red = 0, green = 0, blue = 0;

            int[] maxValuesRequired = new int[]{ 128, 255, 255, 255 };
            var colorWorkflow =
                Observable
                    .ToObservable( maxValuesRequired )
                    .SelectMany( m =>
                        Observable
                            .FromAsyncPattern<int, int>(
                                ( maxValue, callback, state ) => {
                                    GetRandomNumberResult asyncResult = new GetRandomNumberResult();
                                    Service.GetRandomNumber( maxValue, v =>
                                    {
                                        asyncResult.SetResult( v );
                                        callback( asyncResult );
                                    } );
                                    return asyncResult;
                                },
                                ( asyncResult ) => {
                                    return (int)asyncResult.AsyncState;
                                }
                            )( m )
                    )
                    .ObserveOnDispatcher();
            
            int colorIndex = 0;
            colorWorkflow
                .Subscribe( result =>
                {
                    switch( colorIndex++ )
                    {
                    case 0:
                        alpha = result + 128;
                        break;
                    case 1:
                        red = result;
                        break;
                    case 2:
                        green = result;
                        break;
                    case 3:
                        blue = result;
                        break;
                    }
                },
                ex =>
                {
                },
                () =>
                {
                    RectangleFill = new SolidColorBrush(
                        new Color
                        {
                            A = (byte)alpha,
                            B = (byte)blue,
                            G = (byte)green,
                            R = (byte)red
                        } );
                } );
        }

I am wrapping the service call so that it follows the asynchronous service pattern, but this is the more common method of acessing services in a Silverlight application anyway.

Updated July 24, 2010:

I corrected the code by moving the setting of the RectangleFill property to the subscription’s completed method rather than in the Finally method. The latter is more designed for cleaning up after the subscription is completed.

July 25, 2009

Security Exception on Silverlight Client when Calling WCF Service

Filed under: Silverlight, WCF — Tags: — dfbaskin @ 12:14 pm

I kept getting a SecurityException when calling from my Silverlight client to a WCF service. I had double-checked WCF settings (I used basicHttpBinding, which is what Silverlight supports). I had created clientaccesspolicy.xml and crossdomain.xml files on the root of my web site. The meta data came up correctly for my service. Via Fiddler, I saw that the clientaccesspolicy.xml file was being retrieved successfully. But the call to the actual service never happened (the SecurityException was generated first).

While researching this error, I found this post. The solution worked for me, but it also bugged me. I found it hard to believe that the clientaccesspolicy.xml file just simply didn’t work.

And I was right. I did manage to get the clientaccesspolicy.xml to work. According to this MSDN article, the contents of the clientaccesspolicy.xml file might look like this:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

The problem turns out to be the encoding. When I removed the attribute, encoding=”utf-8″, the Silverlight client happily called the service with no problem.


Ok, scratch that. I tried to reproduce the original problem by adding the encoding back to the clientaccesspolicy.xml, and … it works. I tried resetting the IIS server and clearing the browser cache, and now I can’t get it to fail again.

So, I’m still scratching my head about this.

May 2, 2009

Silverlight Color Picker Control

Filed under: Silverlight — Tags: — dfbaskin @ 2:21 pm

I’m not sure why a color picker was not included in Silverlight since it seems like such a common control. However, I needed one so I took a look at my options. Plenty of examples exist so I decided to take the work started by Page Brooks (here, here, and here) and incorporate the Modal Popup functionality.

I ended up with a PopupColorPicker control. Here’s a picture of it in all of its popped-up glory:

Popup Color Picker Control

This control uses the Hue/Saturation/Value color space to translate colors to and from RGB values (through the HSVColor structure that is similar to the Color structure). The control also includes selecting a value for the opacity.

The control initially displays a color display box/button, which opens the Popup panel to select the color, and a text box, which a user can enter in an ARGB value directly. When the user clicks the color display button, the Popup panel is displayed (as shown above). On the left the user selects the hue, in the middle the saturation and value, and on the right the opacity.

The source code can be downloaded from CodePlex.

Hope this is useful!

Silverlight Modal Popup

Filed under: Silverlight — Tags: — dfbaskin @ 1:18 pm

The Popup control in Silverlight is not, as you might think, like a modal dialog box where other portions of the application are disabled while the dialog is displayed. It is more like an overlay where the contents of the Popup control are displayed on top of everything else in the control. However, lower level controls are still active.

If you need something more like a modal dialog box, you can simulate this in Silverlight by including a rectangle in your Popup that spans over the entire display area of the control.

I’ve taken this idea and encapsulated it into a ModalPopup control. The control can be used similarly to the Popup control, such as in the following example:

<stz:AutoSizeUserControl x:Class="Demo.ModalPopupDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:stz="clr-namespace:Stanza.Silverlight.Controls;assembly=Stanza.Silverlight.Controls" Width="800" Height="600" MinWidth="300" MinHeight="200" ExtraHeight="140" ExtraWidth="20" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
	<Grid x:Name="LayoutRoot" Background="White">
		<Grid>
			<Rectangle Stroke="#FF0000FF" RadiusX="32" RadiusY="32" StrokeThickness="2">
				<Rectangle.Fill>
					<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
						<GradientStop Color="#FF000000"/>
						<GradientStop Color="#FFFFFFFF" Offset="1"/>
					</LinearGradientBrush>
				</Rectangle.Fill>
			</Rectangle>
			<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
				<Button Width="140" Height="26" x:Name="showDefaultBtn" Content="Show (Fixed)"/>
				<Button Width="140" Height="26" x:Name="showRelativeBtn" Content="Show (Relative)"/>
				<Button Width="80" Height="26" x:Name="showFullScreen" Content="Full Screen"/>
			</StackPanel>
		</Grid>
		<stz:ModalPopup x:Name="popupPanel" Height="200" Width="220" BackgroundOpacity="0.25" BackgroundFill="Green" Left="200" Top="100" d:IsHidden="True">
            <Border x:Name="popupPanelGrid" BorderThickness="4,4,4,4" Background="Green" BorderBrush="Red" Width="220" Height="200">
				<Grid>
					<Grid.RowDefinitions>
						<RowDefinition/>
						<RowDefinition/>
					</Grid.RowDefinitions>
					<Grid.ColumnDefinitions>
						<ColumnDefinition/>
						<ColumnDefinition/>
					</Grid.ColumnDefinitions>
					<Ellipse Margin="10" Grid.RowSpan="2" Grid.ColumnSpan="2">
						<Ellipse.Fill>
							<RadialGradientBrush GradientOrigin="0.25,0.25">
								<GradientStop Offset="0.25" Color="White" />
								<GradientStop Offset="1.0" Color="Red" />
							</RadialGradientBrush>
						</Ellipse.Fill>
					</Ellipse>
					<Button Width="70" Height="26" x:Name="closeBtn" Content="Close" Grid.Row="1" Grid.Column="1"/>
				</Grid>
            </Border>
		</stz:ModalPopup>
	</Grid>
</stz:AutoSizeUserControl>

The popup panel itself can be opened and closed using code like:

...
    showRelativeBtn.Click += new RoutedEventHandler( showRelativeBtn_Click );
    closeBtn.Click += new RoutedEventHandler( closeBtn_Click );
...

    void showRelativeBtn_Click( object sender, RoutedEventArgs e )
    {
        Point pt = showRelativeBtn.GetAbsolutePosition();
        pt.X += showRelativeBtn.Width / 2;
        pt.Y += showRelativeBtn.Height / 2;
        popupPanel.OpenPopup( pt );
    }

    void closeBtn_Click( object sender, RoutedEventArgs e )
    {
        popupPanel.ClosePopup();
    }

The GetAbsolutePosition() method is an extension method that allows you to get the position of the UIElement object relative to the entire Silverlight display area.

The source code can be downloaded from CodePlex.

Resizing a Silverlight Control within the Browser

Filed under: Silverlight — Tags: — dfbaskin @ 12:27 pm

A common user interface technique in a web page is to expand the contents of the page to the size of the browser window. There are solutions for doing this in both CSS or Javascript. A Silverlight control may also be resized in this way and since a Silverlight control can access the Browser’s DOM (unless permission is explicitly denied), resizing can be handled within the control itself.

To do so, the control will need to be notified of the browser’s OnResize event, which can be attached using the following code:

...
HtmlPage.Window.AttachEvent( "onresize", new EventHandler<HtmlEventArgs>( BrowserWindow_Resized ) );
...

void BrowserWindow_Resized( object sender, HtmlEventArgs e )
{
    AdjustControlSize();
}

Then, you can determine the new size of the browser window using a technique similar to what might be used in Javascript:

        public static bool IsIECSSCompatibleMode
        {
            get
            {
                bool isCompatibleMode = false;
                string mode = HtmlPage.Document.GetProperty( "compatMode" ) as String;
                if( ( mode != null ) && ( String.Compare( mode, 0, "CSS1", 0, 4, StringComparison.InvariantCultureIgnoreCase ) == 0 ) )
                    isCompatibleMode = true;
                return isCompatibleMode;
            }
        }

        public static double BrowserWindowWidth
        {
            get
            {
                object w = HtmlPage.Window.GetProperty( "innerWidth" );
                if( w != null )
                    return Convert.ToDouble( w );
                else if( IsIECSSCompatibleMode )
                {
                    w = HtmlPage.Document.Body.Parent.GetProperty( "clientWidth" );
                    if( w != null )
                        return Convert.ToDouble( w );
                }
                else
                {
                    w = HtmlPage.Document.Body.GetProperty( "clientWidth" );
                    if( w != null )
                        return Convert.ToDouble( w );
                }
                return double.NaN;
            }
        }

        public static double BrowserWindowHeight
        {
            get
            {
                object h = HtmlPage.Window.GetProperty( "innerHeight" );
                if( h != null )
                    return Convert.ToDouble( h );
                else if( IsIECSSCompatibleMode )
                {
                    h = HtmlPage.Document.Body.Parent.GetProperty( "clientHeight" );
                    if( h != null )
                        return Convert.ToDouble( h );
                }
                else
                {
                    h = HtmlPage.Document.Body.GetProperty( "clientHeight" );
                    if( h != null )
                        return Convert.ToDouble( h );
                }
                return double.NaN;
            }
        }

I’ve encapsulated this technique into a Silverlight control called AutoSizeUserControl. This control is derived from the standard UserControl and automatically resizes itself based on changes to the size of the browser window. It can be used simply as a replacment for the UserControl:

<stz:AutoSizeUserControl x:Class="Demo.AutoSizeDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:stz="clr-namespace:Stanza.Silverlight.Controls;assembly=Stanza.Silverlight.Controls" Width="800" Height="600" MinWidth="300" MinHeight="200" ExtraHeight="164" ExtraWidth="20">
    <Grid x:Name="LayoutRoot" Background="White">
		<Rectangle Stroke="#FF000000" RadiusX="16" RadiusY="16" StrokeThickness="4" >
			<Rectangle.Fill>
				<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="#FF000000"/>
					<GradientStop Color="#FFFFFFFF" Offset="1"/>
				</LinearGradientBrush>
			</Rectangle.Fill>
		</Rectangle>
		<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Width="250" Height="120">
			<Grid.RowDefinitions>
				<RowDefinition/>
				<RowDefinition/>
				<RowDefinition/>
				<RowDefinition/>
			</Grid.RowDefinitions>
			<Grid.ColumnDefinitions>
				<ColumnDefinition/>
				<ColumnDefinition/>
				<ColumnDefinition/>
				<ColumnDefinition/>
			</Grid.ColumnDefinitions>
			
			<TextBlock Text="Browser Window" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.ColumnSpan="2"/>
			<TextBlock Text="Width:" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
			<TextBox Text="0" x:Name="browserWidthBox" Grid.Column="1" IsReadOnly="True" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="40"/>
			<TextBlock HorizontalAlignment="Center" Text="Height:" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"/>
			<TextBox Text="0" x:Name="browserHeightBox" Grid.Column="1" Grid.Row="2" IsReadOnly="True" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="40"/>
			
			<TextBlock Text="Silverlight Control" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.ColumnSpan="2"/>
			<TextBlock Text="Width:" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
			<TextBox Text="0" x:Name="controlWidthBox" Grid.Column="3" IsReadOnly="True" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="40"/>
			<TextBlock HorizontalAlignment="Center" Text="Height:" Grid.Row="2" Grid.Column="2" VerticalAlignment="Center"/>
			<TextBox Text="0" x:Name="controlHeightBox" Grid.Column="3" Grid.Row="2" IsReadOnly="True" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="40"/>
			
			<Button Grid.Row="3" Content="Full Screen" Grid.ColumnSpan="2" Grid.Column="1" Width="80" Height="24" x:Name="fullScreenButton"/>
			
		</Grid>
    </Grid>
</stz:AutoSizeUserControl>

Source code can be downloaded from CodePlex.

Older Posts »

Create a free website or blog at WordPress.com.