Ryan Dawson on Longhorn

The software we think, but do not write

June 2005 - Posts

  • A lesser-seen value of XAML

    Filed under:

    So, I come to realizations every now and then...Here is one pertaining XAML.

    I have always know that XAML is a good visualization of source code because it allows you to inherently view the control hierarchy without having to look at another source. So, instantly you can glean the structure of your application.

    <Button>

          <Grid>

                <Image />

                <TextBlock />

          </Grid>

    </Button>

    But, more so, here is an even interesting point. In XAML you don't have to think about variable names (which in my case takes a lot of my time), because the structure of the XAML is self-documenting in that you don't need names. In the case you do, you create a very meaningful name and your off...

    PostTypeIcon
    2,022 Views
  • Creating custom controls, the real way

    Filed under:

       I know the question has been dodged, and I myself didn't have a firm grasp--but I am back to report on "the way" of creating a custom control.

       First of all, be aware that a custom control should be created only as a last option. For the sake of all the work that has gone into Avalon to make controls lookless, you should always evaluate the available controls before contunuing. I believe the true circumstance for a custom control is when you want to package certain ideas together with data. The key word being data. That is really the only reason you create a custom control--you want a common ancestor object without having to rewire the data hook-up everytime. A custom control is not for look-ability. Because, in that sense, you are destroying the lookless metaphor--which is undesirable. You always want that style-ability inherent in Avalon.

     

       Now you may recall the first step in creating an Avalon application with a custom control is to set the 'UICulture' property in the project settings. This is a temporary work-around for satisfying Avalon's taste for localized apps. To do this, navigate to the project settings file (It will be in the project directory, and called something like "<ProjectName>.csproj"). Open it up in Notepad and it should look like below. Add the bolded line.

       <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
            <PropertyGroup>
                 <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
                 <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
                ...
                <UICulture>en-US</UICulture>
            </PropertyGroup>
            ...
      </Project>

       

       The next step is to create class file (Widget.cs). The only difference between a regular class and this one is the 'partial' decorator. The 'partial' tells .NET that there is another part of this class in a separate fill.

          namespace ApplicationNamespace
          {
                public partial class Widget : ContentControl
                {
                }
          }

       Although you could inherit from a number of classes, ContentControl best satisfies the fact that we want a control with lookless behavior. In light of that, ContentControl is just a container. Essentially, this allows us the flexibility of the control to hold anything.

       Since XAML is the easiest language to describe Avalon elements, it would be a shame to stay in C#. So, go ahead and create a XAML file (Widget.XAML).

          <ContentControl x:Class="ApplicationNamespace.Widget"
                xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
                xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
                >

          </ContentControl>

       The above is the second half of the class declared in C#, which now enables us to fully work in XAML.

       Continuing on, let's add the custom control to the main Window (the stuff bolded is the important lines that hook it up):

          <?Mapping XmlNamespace="controls" ClrNamespace="ApplicationNamespace" ?>
          <Window x:Class="ApplicationNamespace.Window1"
                xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
                xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"

                xmlns:cc="controls"
                >
                <Grid>
                      <cc:Widget /> 
                </Grid>      

          </Window>

      

       Now, go ahead and set some properties on the custom control:

          <ContentControl x:Class="ApplicationNamespace.Widget"
                xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
                xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"       
                >

           <ContentControl.Style>

                  <Style>

                         <Setter Property="ContentControl.Width" Value="200" />

                         <Setter Property="ContentControl.Height" Value="200" />

                         <Setter Property="ContentControl.Background" Value="Red" />

                  </Style>

           </ContentControl.Style>

          </ContentControl>

       Run the application...You may notice that nothing is visible. The reason is that we did set the properties and we did create the custom control partial class in XAML, but it really isn't being connected with our real class in C#. The reason is that XAML really isn't a language and it must actually be converted into C#. To do this, we need to call InitializeComponent from the constructor of the Widget class:

          namespace ApplicationNamespace
          {
                public partial class Widget : ContentControl
                {

                      public Widget()

                      {

    InitializeComponent();

                      }
                }
          }

     

    Even now if you run the application the control won’t show up. The reason is that we may have specified dimensions and a background, but the control cannot set those on content that doesn’t exist, yet. To add a look, we should provide a default template—which is a way of adding a look to our lookless control. The template can be ripped out or styled differently at any time by the application.

     

    <ContentControl x:Class="ApplicationNamespace.Widget"

        xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

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

        Width="200" Height="200" Background="Red"

        >

        <ContentControl.Style>

                <Style>

                        <Setter Property="ContentControl.Width" Value="200" />

                        <Setter Property="ContentControl.Height" Value="200" />

                        <Setter Property="ContentControl.Background" Value="Red" />

                     <Setter Property="ContentControl.Template">

                            <Setter.Value>

                                   <ControlTemplate>

                                          <Grid Background="{TemplateBinding Property=Background}">

                                          </Grid>

                                   </ControlTemplate>

                            </Setter.Value>

                     </Setter>

                </Style>

        </ContentControl.Style>

    </ContentControl>

                      

    Going back to the class, let’s add some data through a property WidgetData—which returns a simple string “Red”. This is the data we are going to expose with our custom control (the essence of it):

       public partial class Setter Property="ContentControl.Template">

                                <Setter.Value>

                                        <ControlTemplate>

                                                <Grid Background="{TemplateBinding Property=Background}">

                                                 <TextBlock TextContent="{Binding Path=WidgetData,RelativeSource=/TemplatedParent}" />

                                                </Grid>

                                        </ControlTemplate>

                                </Setter.Value>

                        </Setter>

    I am binding the value to the TextBlock.TextContent property. The reason I have specified RelativeSource as ‘/TemplatedParent’ is that the Binding statement will not have any data context without RelativeSource. There are a couple of other values which can be automatically determined by Avalon at runtime. They all start with a forward slash ‘/’ and refer to common paths. ‘TemplatedParent’ gets us the main element that the ControlTemplate is modifying—which is the Widget class.

                      

        xmlns="http://schemas.microsoft.com/winfx/avalon/2005"

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

        xmlns:cc="controls"

        >

        <Window.Resources>

                <Style x:Key="WidgetStyle">

                        <Setter Property="Control.Template">

                                <Setter.Value>

                                        <ControlTemplate>

                                                <Grid Background="{Binding Path=WidgetData,RelativeSource=/TemplatedParent}" />

                                        </ControlTemplate>

                                </Setter.Value>

                        </Setter>

                </Style>

        </Window.Resources>

        <Grid>

                <cc:Widget Style="{StaticResource WidgetStyle}" />

               

        </Grid>

    </Window>

                         

    PostTypeIcon
    6,206 Views
  • Using BitmapVisualManager

    Filed under:

    I have had some trouble getting this working, and I am not sure why it is that cool (probably the same reason for my fascination with the VisualBrush), but here it is:

    <StackPanel Width="100" Name="stack4">

          <Button Name="button4" Click="button4Clicked" Background="Green" Height="25">

                <Button.Content>dsfasdfasdf</Button.Content>

          </Button>

          <Image Width="100" Height="25" Name="img" />

    </StackPanel>

     

    In code:

    private void button4Clicked(object sender, RoutedEventArgs e)

    {

          VisualOperations.GetChildren(this.stack4).Remove(this.button4);

          VisualOperations.SetOffset(this.button4, new Vector(0, 0));

          VisualOperations.SetTransform(this.button4, null);

          RenderTargetBitmap bitmap = new RenderTargetBitmap(100, 25, 1 / 96, 1 / 96, PixelFormats.Pargb32);

          BitmapVisualManager visual = new BitmapVisualManager(bitmap);

          visual.Render(this.button4);

          img.Source = bitmap as ImageSource;

          VisualOperations.GetChildren(this.stack4).Add(this.button4);

    }

                            

    You can obviously notice the stretching, and that is because I have a RenderTransform scaling by 6. So, the BitmapVisualManager renders the content at the original size.

                            
    PostTypeIcon
    2,504 Views
  • Rich Media Experiences Lacking

    Filed under:

       TV is neat because many things are hand coded with explicit pixel positions and absolutes that are composited together to get an intended effect that looks incredible and visually enjoyable.

       I once thought Avalon was also going to be neat because we could do similar things. Not the hand coding, though. Yet, I have found that applying animation transitions and niceties is hard--almost to the point where it is best to do it like TV. The grid panel is awesome, which you may remember is the opposite of what I thought before. But, It really allows for an incredible layout. Back to the point, though--when you want to add these custom animation transitions and effects, usually, no make that almost always, you have to break down those nice layouts and go to back to absolute positioning on the Canvas. You also have a big problem trying to get controls (and their shapes) clipped so as to not cover up functionality behind. This means you have to play a game of promoting and demoting things in the Z order--which I would think adds some un-needed overhead.

       The thing is that objects and shapes on screen should be able to fly around, link up, decouple, change Z order without having to change the panel children order, and transitioning between shapes easily. Exactly like things on TV. The channel I most want to mimic is MSNBC (the stock channel). They have cool things going on: background animations using 3D and blurring, polished look and feel, and presentation of both information and usefulness in the same package.

     

       I am not trying to rag on Avalon, because I love it. I mean, how could I criticize something that is superior to anything else in existence. I can't. But, on the same token it seems like trying to build these experiences that I want are harder than I would have thought.

       Here is something I want to do:

       Imagine I have a Window with a button in the middle (the button is gel styled). I want to click the button and have it so that the screen seamlessly transitions from being a button in the middle to a new form with many controls and information. I can imagine the button getting bigger until it is half the size of the new form, at which time the background fades from the button color to the new form color and the controls on the new form start to appear. This is a simple example of something I want to do (simple). But, I find this hard.

       Have the designers of Avalon thought of these scenarios? Like I said, I don't mean to criticize, but just bring it to the table...

    PostTypeIcon
    2,024 Views
  • Sizing Paths

    Filed under:

    I had a Path (System.Windows.Shapes.Path) that I wanted to show, but it wasn't exactly clear as to how I was going to size it because there aren't any dimension properties to modify.

    So, simple enough, I plopped it in-between some Viewbox tags, and I was able to size it to my requirements.

    <StackPanel Orientation="Horizontal">

       <Path Stroke="#000000" Fill="#000000" StrokeThickness="16" Data="M9,12.844v71l216,72l-217,72v71l290-144L9,12.844z"/>

       <Viewbox Width="30" Height="30">

          <Path Stroke="#000000" Fill="#000000" StrokeThickness="16" Data="M9,12.844v71l216,72l-217,72v71l290-144L9,12.844z"/>

       </Viewbox>

    </StackPanel>

    You can see the difference:

                                
    PostTypeIcon
    1,768 Views