An Avanade Blogging Community

Welcome to An Avanade Blogging Community Sign in | Join | Help
in Search

Mark Tinderholt on Silverlight and WPF

  • My New Blog

    Alas, I have decided to retire this blog. The editors for this site are just far to clunky when compared with Live Writer. I have decided to pick up shop and move here.

    If you are interested in my ramblings on Microsoft Surface, WPF, and Silverlight come and check it out!

  • Creating Business Applications with Silverlight...as easy as 1, 2, 3.

    On my current project we are building an enterprise line of business application using Silverlight. What drew us to this platform was the lightweight framework that packed a serious punch when it came to developer productivity. Of course, we started very early on with Silverlight 2.0 Beta 1 (the first real release in my opinion) and overcame many challenges one would face with any immature platform but we hung in there and had faith that Scott Gu and his team would eventually get it right. It was a big bet but we're happy to say that it has paid us back in more ways than one.

    Today, we have a robust application and a lightweight MVP implementation that completely decouples the view from UI processing and business logic.

    Our designer friendly views are accessible from Blend which makes it easy for application maintenance developers to make aesthetic changes without worry.

    Every call is asynchronous. Every "page" snaps into view without a postback or any waiting thanks to proactive caching on the client.

    The experience is far superior to even the most rigorous of AJAX applications and to be honest, most of it was free thanks to out of the box Silverlight.

    However, with all the technical superiority of Silverlight there is an 800 pound gorilla in the room: Developer Productivity. To most developers, WPF/Silverlight topics are foreign, let alone ways to do things.

    Also, in our hypertext world we have created, there are certain things that developers have become acustom to the browser doing for them. Something as simple as swapping out content when a link is clicked. This may not seem like a very daunting technical feat (just clear the children on your content container and replace it the active view on a button click event) but I wouldn't consider wiring up content swapping on click events a very scalable solution nor does this solution account for the transferance of DataContext (another foreign concept to the land of ASP.NET).

    So with the onset of Silverlight, the browser has shed some responsibility..so who will step up and claim that responsibility? Unfortunately, you, the developer, will have to. Unless, you build or use a framework that takes on that responsibility for you. At Avanade, we have created the Silverlight Accelerator Framework to do just that.

    As a result, for our developers to create a business form in Silverlight it is as simple as:

    1) Create a Model class to manage state

    2) Create a Presenter class to manage state transitions

    3) Create a View (simple Silverlight User Control) to display the state to the user

    With these three steps you can create a fully data bound component that integrates into the navigational system of the Silverlight Accelerator. This eliminates the integration issues that you might see from an OOB Silverlight solution and turns a business form into what it should be: an atomic unit of functional scope.

  • Visual State Manager: Some Practical Uses

    So I just fell in love...with the Visual State Manager in Silverlight 2.0 Beta 2.

    This thing is sweet. You can cleanly manage the visual appearance for various states. Not only does this tool make it ridiculously simple to create animations that alter the state it also provides a tool that makes it easy to review these states and how they look.

    The OLD Way:

    1. Wire Up Mouse Event
    2. Obtain Reference of for each state transition from resources
    3. Cast resources as Storyboards
    4. Determine the appropriate future state
    5. Begin the appropriate Storyboard

    The NEW Way

    1. Wire Up Mouse Event
    2. Determine the appropriate future state
    3. Tell Visual State Manager to go to the appropriate state

    Not only is the new way cleaner, it also makes it easy to invoke state transitions from virtually anywhere, all you need is a reference to the state's target and the state name. No more carrying references to those storyboards all over the place...YAY!

  • Migrating Apps from Silverlight Beta 1 to Beta 2

    1. Removed Windows.Controls.* from project ClientBin
    2. Added reference to System.Net so that WebClient would be recognized
    3. Changed accessibility for OnApplyTemplate from protected to public on all custom controls
    4. Changed PropertyChangedCallback to PropertyMetadata.
    5. Removed FontSize and FontFamily Dependency Properties (where implemented).
    6. Removed reference to System.Windows.Controls.Data and readded…weirdness
    7. Changed DataGridTextBoxColumn to DataGridTextColumn
    8. Set the DefaultStyleKey on all custom controls to be typeof(CustomControlClass)

     

    I migrated a line of business application has 4 functional views, 3 custom controls, and several datagrids in just under 2 hours.

     

    Also, upgrading to Beta 2 eliminated a weird error that I received when trying to open my solution in Blend 2.5 March. It would give a fatal error with -2142382439820943 (can't remember the actual number)...this was after I copied the solution from one local account's home directory to another's. So now I have designer access back!

     

    Thanks MS! :-)

  • Silverlight Beta 2 Right Around the Corner

    Looks like we should see it this Friday!

    Some highlights:

    • Tab Control
    • Improved Cross Domain Support
    • Upload support for Web Client
    • "Push" Support

    Oh boy! :-)

  • Rich Business Applications with Silverlight

    There has been a big thread on the Avanade Silverlight Community about this very subject. I have not had a chance to respond as I'm juggling three sales pursuits. I found this webcast from Pete Brown about why Silverlight is a great platform for business LOB applications. March 19th, check it out!
  • Final Version of Expression Blend 2.0 Released!

    I'm a few days late, but here it is!

  • Creating Custom Controls in Silverlight: The DropDownList

    Many of you may have noticed that Microsoft did not include a DropDownList in the Beta 2 of Silverlight. So I thought I would take a stab at it.

    1. The Parts Contract

    The first thing that you need to think about when creating a custom control is what are the visual elements that you need to expose to people that are going to reskin your control. This is called the "Parts Contract". The parts contract consists of specific names and types that must be defined with in the templates of your control in order for it to continue to work properly.

    In this case, the DropDownList, I identified two main logical components:

    1. The Selection Indicator
    2. The Drop Down List

    The visual tree for each of these parts could be very diverse but as long as these parts are exposed we know that our control will operate as expected.

    In my parts contract I wanted to give clients of my custom control access to not only the logical elements but also the containing elements. This will further refine the isolation of behavior and visual between different Parts Elements.

    Therefore, I will define the following parts elements:

    1. RootElement
    2. SelectionContentContainer
    3. SelectionContent
    4. ItemsContainer
    5. ItemsListBox

    Within the control logic of my custom control each of these parts will play a specific role in the control's appearance & behavior.

    2. Inherit from Control

    In this case I am inheriting from ListBox, but that is because I want to leverage some of the existing properties, events, and methods within this class. Many custom controls will inherit from Control, ItemsControl, or the like. As long as you do not inherit from UserControl your control will be able to have new templates applied to it.

    public class DropDownList : ListBox

    {

    ...

    }

    3. Define Default Visuals

    Now that I have an idea of how clients will interact with my control I need to define the default visuals that my control will use out of the box. This will give developers a starting place and a reference for templating my control.

    In Silverlight, all default visuals are defined in a resource file called "generic.xaml". This file needs to have a Build Action of "Resource".

    <ResourceDictionary

    xmlns="http://schemas.microsoft.com/client/2007"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:Avanade.Silverlight.Controls;assembly=Avanade.Silverlight.Controls"

    >

    <Style TargetType="local:DropDownList">

    <Setter Property="Template">

    <Setter.Value>

    <ControlTemplate TargetType="local:DropDownList">

    ...

    It is important to note that when defining the local namespace we need to include the assembly information. WPF developers will notice that the {x:Type ...} markup expression is not supported in Silverlight.

    4. Define Control Logic

    This is where all the magic happens. A Control goes through several stages before it is finally rendered to the screen. The stage that we are particularly interested in is when a new template is applied. During this stage we are able to access all of the Parts that we are expecting from our template and wire them up with the logic that will make everything work.

    By overriding OnApplyTemplate we can wire up the control logic that will make our DropDownList act as expected. The GetTemplateChild method will return the DependencyObject with the given name or null if it can't be found.

    protected override void OnApplyTemplate() {

       base.OnApplyTemplate();

       layoutRootElement = this.GetTemplateChild("RootElement") as FrameworkElement;

       selectionContainerElement = this.GetTemplateChild("SelectionContentContainer") as FrameworkElement;

       selectionContent = this.GetTemplateChild("SelectionContent") as FrameworkElement;

       itemsContainer = this.GetTemplateChild("ItemsContainer") as FrameworkElement;

       itemsListBox = this.GetTemplateChild("ItemsListBox") as ListBox;

       itemsListBox.SelectionChanged += new SelectionChangedEventHandler(itemsListBox_SelectionChanged);

       IsItemsContainerVisible = false;

    // Update the margin based on the height of the control

       double offset = itemsContainer.Height;

       itemsContainer.Margin = new Thickness(0, 0, 0, -offset);

       itemsListBox.ItemsSource = ItemsSource;

       selectionContainerElement.MouseLeftButtonDown += new MouseButtonEventHandler(selectionContainerElement_MouseLeftButtonDown);

    }

    5. Done!

    That's it! Not so bad, eh? No the fun is just begining. If you did it right, you should be able to have all the flexability that we enjoy with the wonderful control library that Microsoft has provided us in Beta 1 with our very own concoctions.

    Who knows, maybe you will see my DropDownList control in Beta 2 :-)

    Update: I designed a new skin for my control. See below...

  • Silverlight and the case of the Ginormous App.xaml

    So I am loving Silverlight 2.0 beta 1. Fantastic release. 1.1 got me to the stadium and 2.0 really knocked the ball out of the park. Coming from WPF, however, Silverlight can leave you...let's say...unsatisfied. It's the little things really, like being able to create BasedOn Styles or use Element Binding, that tend to make that little guy in my head keep saying..."Oh, crap, why can't I do that?"

    One of the biggest things I've noticed is how ridiculously large my App.xaml files are getting these days. I almost always re-skin the controls...several times. And just doing a single variation of the OOB controls can really cramp your style (no pun intended).

    The lack of BasedOn Styles doesn't help, but I can work around that, what I really need is the ability to use Resource Dictionaries. This would at least alleviate the amount of duct tape that I have to wrap around me head before doing Silverlight work.

    Seriously, there isn't even a quick way to navigate the App.xaml or any Xaml file for that matter. Heck, at least back in VB6 we could select the class and or function we wanted from a little drop down menu.

    So the way I see it, Microsoft needs to do one of two things:

    1. Bring Resource Dictionaries to Silverlight

    2. Modify the XAML editor in VS or Blend to have a quick resource drop down so I can shortcut to the style I am looking for. ALT-F does not cut it

    Just my $0.02. Back to finding that style I was looking for...

  • BUG: Center DataGridCell Content in Silverlight

    So I have been banging my head against a wall on this one. It appears that this is a bug. I am trying to apply a Horizontal Alignment of text within a given DataGridColumn's cells. I am able to set the horizontal alginement on the Column Header to be Center but not on the individual elements.

    <Style x:Key="centeredCell" TargetType="swcd:DataGridCell">

       <Setter Property="HorizontalAlignment" Value="Center" />

       <Setter Property="HorizontalContentAlignment" Value="Center" />

       <Setter Property="TextAlignment" Value="Center" />

    </Style>

    ...and here is the column definition...

    <swcd:DataGridTextBoxColumn DisplayMemberBinding="{Binding ProductCode}" CellStyle="{StaticResource centeredCell}">

       <swcd:DataGridTextBoxColumn.Header>

          <HyperlinkButton Content="Product" TextDecorations="Underline" HorizontalAlignment="Center" />

       </swcd:DataGridTextBoxColumn.Header>

    </swcd:DataGridTextBoxColumn>

    This seems like it should not be difficult.

    Anybody else having any luck with this?

    UPDATE: There is a workaround. You can create your own Cell Template for each column, but who wants to do that honestly? :-)

  • How to eliminate the RowHeader within a Silverlight DataGrid

    So I am not a huge fan of the default templates for the Silverlight Controls but, hey at least we have controls now right?

    I wanted to eliminate that bulky cell to the left of each row to allow for my DataGrid's to be a little more concise. Below you will find a complete Style that will accomplish this. You can copy & paste & use in your Silverlight apps.

    <Style x:Key="GridWithoutRowHeaders" TargetType="swcd:DataGrid">

       <Setter Property="RowBackground">

          <Setter.Value><SolidColorBrush Color="#FFF2F2F2"/></Setter.Value>

       </Setter>

       <Setter Property="AlternatingRowBackground">

          <Setter.Value><SolidColorBrush Color="#FFFFFFFF"/></Setter.Value>

       </Setter>

       <Setter Property="Template">

          <Setter.Value>

             <ControlTemplate TargetType="swcd:DataGrid">

                <Border BorderBrush="#FF000000" BorderThickness="1">

                   <Border BorderBrush="#FFFFFFFF" BorderThickness="1">

                      <Border BorderBrush="#FFA4A4A4" BorderThickness="1">

                         <Grid Name="RootElement">

                            <Grid.Resources>

                               <!-- Focus related animations -->

                               <Storyboard x:Name="Normal State">

                                  <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1" />

                               </Storyboard>

                               <Storyboard x:Name="Unfocused State">

                                  <DoubleAnimation Duration="0:0:0" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="0" />

                               </Storyboard>

                               <ControlTemplate x:Key="TopLeftHeaderTemplate" TargetType="swcd:DataGridColumnHeader">

                                  <Grid Name="RootElement">

                                     <Grid.Background>

                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0.28,0.71">

                                           <GradientStop Color="#FFF9FAFA" Offset="0"/>

                                           <GradientStop x:Name="StopColor2" Color="#FFEDF1F4" Offset="0.37259"/>

                                           <GradientStop x:Name="StopColor3" Color="#FFE2E8EF" Offset="0.372881"/>

                                           <GradientStop x:Name="StopColor4" Color="#FFC3C9CD" Offset="1"/>

                                        </LinearGradientBrush>

                                     </Grid.Background>

                                     <Grid.RowDefinitions>

                                        <RowDefinition Height="*" />

                                        <RowDefinition Height="*" />

                                        <RowDefinition Height="Auto" />

                                     </Grid.RowDefinitions>

                                     <Grid.ColumnDefinitions>

                                        <ColumnDefinition Width="*"/>

                                        <ColumnDefinition Width="Auto"/>

                                     </Grid.ColumnDefinitions>

                                     <Line Stretch="Fill" Grid.Column="1" Grid.RowSpan="2" X1="0" X2="0" Y1="0" Y2="1" StrokeThickness="1" Stroke="#FFA4A4A4" />

                                     <Line Stretch="Fill" Grid.ColumnSpan="2" Grid.Row="2" X1="0" X2="1" Y1="0" Y2="0" StrokeThickness="1" Stroke="#FFA4A4A4" />

                                     <!--<Line Stretch="Fill" Grid.RowSpan="2" X1="0" X2="0" Y1="0" Y2="1" StrokeThickness="1" Stroke="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}"/>-->

                                     <Rectangle Stretch="Fill" Width="39" StrokeThickness="2" Stroke="#FFFFFFFF" Grid.RowSpan="2"/>

                                     <Path Margin="1,1,1,0" Stretch="Fill" Data="F1 M 547.239,124.863L 430.795,124.863L 430.795,135.106C 447.845,138.848 467.753,140.997 489.017,140.997C 510.281,140.997 530.188,138.848 547.239,135.106L 547.239,124.863 Z ">

                                        <Path.Fill>

                                           <LinearGradientBrush StartPoint="0.5125,-0.0864589" EndPoint="0.5125,1.00202">

                                              <GradientStop Color="#B3FFFFFF" Offset="0"/>

                                              <GradientStop Color="#3CFFFFFF" Offset="1"/>

                                           </LinearGradientBrush>

                                        </Path.Fill>

                                     </Path>

                                  </Grid>

                               </ControlTemplate>

                               <ControlTemplate x:Key="TopRightHeaderTemplate" TargetType="swcd:DataGridColumnHeader">

                                  <Grid Name="RootElement">

                                     <Grid.Background>

                                        <LinearGradientBrush StartPoint="0,0" EndPoint="0.28,0.71">

                                        <GradientStop Color="#FFF9FAFA" Offset="0"/>

                                        <GradientStop x:Name="StopColor2" Color="#FFEDF1F4" Offset="0.37259"/>

                                        <GradientStop x:Name="StopColor3" Color="#FFE2E8EF" Offset="0.372881"/>

                                        <GradientStop x:Name="StopColor4" Color="#FFC3C9CD" Offset="1"/>

                                        </LinearGradientBrush>

                                     </Grid.Background>

                                     <Grid.RowDefinitions>

                                        <RowDefinition Height="*" />

                                        <RowDefinition Height="*" />

                                        <RowDefinition Height="Auto" />

                                     </Grid.RowDefinitions>

                                     <Grid.ColumnDefinitions>

                                        <ColumnDefinition Width="Auto"/>

                                        <ColumnDefinition Width="*"/>

                                     </Grid.ColumnDefinitions>

                                     <!--<Line Stretch="Fill" Grid.RowSpan="2" X1="0" X2="0" Y1="0" Y2="1" StrokeThickness="1" Stroke="{TemplateBinding SeparatorBrush}" Visibility="{TemplateBinding SeparatorVisibility}"/>-->

                                     <Line Stretch="Fill" Grid.RowSpan="2" X1="0" X2="0" Y1="0" Y2="1" StrokeThickness="1" Stroke="#FFA4A4A4" />

                                     <Line Stretch="Fill" Grid.ColumnSpan="2" Grid.Row="2" X1="0" X2="1" Y1="0" Y2="0" StrokeThickness="1" Stroke="#FFA4A4A4" />

                                     <Rectangle Stretch="Fill" StrokeThickness="2" Stroke="#FFFFFFFF" Grid.Column="1" Grid.RowSpan="2"/>

                                     <Path Grid.Column="1" Margin="1,1,1,0" Stretch="Fill" Data="F1 M 547.239,124.863L 430.795,124.863L 430.795,135.106C 447.845,138.848 467.753,140.997 489.017,140.997C 510.281,140.997 530.188,138.848 547.239,135.106L 547.239,124.863 Z ">

                                        <Path.Fill>

                                           <LinearGradientBrush StartPoint="0.5125,-0.0864589" EndPoint="0.5125,1.00202">

                                              <GradientStop Color="#B3FFFFFF" Offset="0"/>

                                              <GradientStop Color="#3CFFFFFF" Offset="1"/>

                                           </LinearGradientBrush>

                                        </Path.Fill>

                                     </Path>

                                  </Grid>

                               </ControlTemplate>

                            </Grid.Resources>

                            <Grid.Background>

                               <SolidColorBrush Color="Transparent" />

                            </Grid.Background>

                            <Grid.RowDefinitions>

                               <RowDefinition Height="Auto" />

                               <RowDefinition Height="*" />

                               <RowDefinition Height="Auto" />

                            </Grid.RowDefinitions>

                            <Grid.ColumnDefinitions>

                               <ColumnDefinition Width="Auto" />

                               <ColumnDefinition Width="*" />

                               <ColumnDefinition Width="Auto" />

                            </Grid.ColumnDefinitions>

                            <!-- Row 0 -->

                            <!--<swcd:DataGridColumnHeader Name="TopLeftCornerHeaderElement" Template="{StaticResource TopLeftHeaderTemplate}" />-->

                            <Canvas Name="ColumnHeadersPresenterElement" Grid.Column="1"/>

                            <swcd:DataGridColumnHeader Name="TopRightCornerHeaderElement" Grid.Column="2" Template="{StaticResource TopRightHeaderTemplate}" />

                            <!-- Row 1 -->

                            <!--<Canvas Name="RowHeadersPresenterElement" Grid.Row="1"/>-->

                            <Canvas Name="CellsPresenterElement" Grid.Column="1" Grid.Row="1">

                               <Canvas.Resources>

                                  <SolidColorBrush x:Key="DefaultVerticalGridlinesBrush" Color="#FFACACAC"/>

                                  <SolidColorBrush x:Key="DefaultHorizontalGridlinesBrush" Color="#FFACACAC"/>

                               </Canvas.Resources>

                            </Canvas>

                            <Rectangle Name="FocusVisualElement" Stroke="#FF400000" StrokeDashArray=".2 4" HorizontalAlignment="Left" VerticalAlignment="Top" StrokeDashCap="Round" IsHitTestVisible="false" Opacity="0" Grid.Column="1" Grid.Row="1"/>

                            <ScrollBar Name="VerticalScrollbarElement" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="18" />

                            <!-- Row 2 -->

                            <!--<Rectangle Fill="#FFE7E7E7" Grid.Row="2"/>-->

                            <ScrollBar Name="HorizontalScrollbarElement" Orientation="Horizontal" Grid.Column="1" Grid.Row="2" Height="18" />

                            <Rectangle Fill="#FFE7E7E7" Grid.Column="2" Grid.Row="2" />

                         </Grid>

                      </Border>

                   </Border>

                </Border>

             </ControlTemplate>

          </Setter.Value>

       </Setter>

    </Style>

  • WPF Solution Structure

    Came accross this good article by Paul Stovell about structuring a WPF solution.

  • How to navigate to a URL in Silverlight 2.0b1

    Apparently this has changed since 1.1.

    This is how you do it in Silverlight 2.0:

    System.Windows.Browser.HtmlPage.Window.Navigate(myUri);

  • Silverlight 2.0b1 DataGrid Basics

    Scott Morrison has a great post on basic DataGrid usage in Silverlight. Check it out!

    He even used my favorite color in one of his samples: Lemon Chiffon! ;-)

  • "Freeze" that VisualBrush

    So you can't "freeze" a VisualBrush. But you can create an ImageBrush from a VisualBrush!

    Visual Brushes are great but sometimes you want to use exactly on the screen without it changing if you obscure it from view or apply render transformations! This is really handy for situations, say like a ScatterViewItem. I created a method that, given a FrameworkElement will produce a RenderTargetBitmap of it.

    Then using the RenderTargetBitmap you can generate an ImageBrush and now you can do whatever you feel like to "myControl" and it won't affect elements that use the ImageBrush.

     

    ...

    RenderTargetBitmap rtb = RenderBitmap(myControl);

    Rectangle r = new Rectangle();

    r.Fill = new ImageBrush(BitmapFrame.Create(rtb));

    ...

     

    public RenderTargetBitmap RenderBitmap(FrameworkElement element) {

       double topLeft = 0;

       double topRight = 0;

       int width = (int)element.ActualWidth;

       int height = (int)element.ActualHeight;

       double dpiX = 96; // this is the magic number

       double dpiY = 96; // this is the magic number

       PixelFormat pixelFormat = PixelFormats.Default;

       VisualBrush elementBrush = new VisualBrush(element);

       DrawingVisual visual = new DrawingVisual();

       DrawingContext dc = visual.RenderOpen();

       dc.DrawRectangle(elementBrush, null, new Rect(topLeft, topRight, width, height));

       dc.Close();

       RenderTargetBitmap bitmap = new RenderTargetBitmap(width, height, dpiX, dpiY, pixelFormat);

       bitmap.Render(visual);

       return bitmap;

    }

More Posts Next page »

This Blog

Post Calendar

<September 2010>
SuMoTuWeThFrSa
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

Syndication