October 2006 - Posts

So what does InvalidateVisual do? The SDK documentation says it "invalidates the rendering of the element, and forces a complete new layout pass. OnRender is called after the layout cycle is completed".  Just what I needed when it was time to implement scrolling for a custom control shown below - or so I thought.

populated bands unscrolled

The custom horizontal scrollbar at the bottom of the control is bound to an ICommand whose implementation is shown below:

	
class ScrollCommand : BaseCommand
{
    public ScrollCommand(Controller controller)
	: base(controller)
    {
    }

    public override void Execute(object parameter)
    {
	double value = (double)parameter;
	controller.horScroll = value;
	controller.designer.InvalidateVisual();
    }
}

The Execute method will force the UIElements Field1, Field2 and Field3 to be repositioned in their respective canvas container relative to the value of the scrollbar by calling the host control's InvalidateVisual method.  This provides the illusion of scrolling. It was a dud.

I was beginning to doubt this whole WPF Binding thing when I chanced upon the Measure method. The SDK describes this as "Updates the DesiredSize of a UIElement. Parent elements call this method from their own MeasureCore implementations to form a recursive layout update. Calling this method constitutes the first pass of a layout update in the layout engine".  It looked very promising, so i replaced the call to InvalidateVisual with Measure as show below:

	
    public override void Execute(object parameter)
    {
	double value = (double)parameter;
	controller.horScroll = value;
	controller.designer.Measure(new Size());
    }

Needless to say it worked!

populated bands scrolled
Posted by smash | with no comments
Filed under: , ,

After reading this post by Karsten, I created a WPF XBAP project that displays the ClassicReports designer on a Frame as shown below.

<Page x:Class="IApp.Page3"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Page3"
    >
    <DockPanel>
        <ToolBarTray DockPanel.Dock="Top">
            <ToolBar>
                <Button Name="newButton">
                    <Image Source="images\new.png" />
                </Button>
                <Button Name="openButton" >
                    <Image Source="images\open.png" />
                </Button>
                <Button  Name="saveButton" >
                    <Image Source="images\save.png" />
                </Button>            
            </ToolBar>
        </ToolBarTray>
        <Frame Name="frame1"></Frame>
    </DockPanel>
</Page>

Below is the c# code behind for the XAML above. Notice that the designer control must first be hosted in a WindowsFormsHost before it can be displayed in WPF. 

namespace IApp
{
    public partial class Page3 : System.Windows.Controls.Page
    {
        JDJ.ClassicReport.ReportDesigner.ReportDesigner designer;

        public Page3()
        {
            InitializeComponent();
            WindowsFormsHost host = new WindowsFormsHost();
            designer = new JDJ.ClassicReport.ReportDesigner.ReportDesigner();
            host.Child = designer;
            frame1.Content = host;
            openButton.Click += new RoutedEventHandler(openButton_Click);
            saveButton.Click += new RoutedEventHandler(saveButton_Click);
            newButton.Click += new RoutedEventHandler(newButton_Click);
        }

        void newButton_Click(object sender, RoutedEventArgs e)
        {
            designer.NewReport();
        }

        void saveButton_Click(object sender, RoutedEventArgs e)
        {
            designer.Save();
        }

        void openButton_Click(object sender, RoutedEventArgs e)
        {
            designer.OpenReport();
        }

    }
}

This is how the above page looks like. Notice the address I used in runnning the XBAP application. That's my actual IP address as of this writing.

XBAP ClassicReports Designer

When I clicked on the Open button the dialog box appears.

XBAP ClassicReports Designer Open File Dialog

So after selecting the Sample Report File, the designer then displays the report layout on the design surface. Now I can modify the report to my heart's content.

XBAP ClassicReports Designer with Sample Report

I almost forgot that I am actually running this rich client application inside a browser! 

Posted by smash | with no comments
Filed under: , , , ,

The ListCollectionView is a WPF wrapper for an IList. When you add or remove something from the IList, the ListCollectionView must be notified of the change by calling its OnCollectionChanged method so it can sync with the IList.

The problem is, OnCollectionChanged is a protected method i.e. we can't access it from outside of a ListViewCollection object. This means we have to create a new class that inherits from ListCollectionView so we can have the means to call OnCollectionChanged whenever we add or remove something from the IList object we are wrapping e.g.

class StudentListView(ListCollectionView):
	def constructor(theClass as Class): 
		super(theClass.StudentList) 
		theClass.StudentListChanged += OnStudentListChanged
	
	def OnStudentListChanged(sender as object, e as StudentListChangedEventArgs):
		if e.ChangeMode == Class.ChangeMode.Add:
			args = NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,e.Student,e.Index)
		else
			args = NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove,e.Student,e.Index)
		OnCollectionChanged(args)

So whenever the parent Class object fires its StudentListChanged event after adding or removing a Student from its StudentList, then the StudentListView object gets to call the OnCollectionChanged method it inherited from ListCollectionView, allowing it to remain in sync with the underlying StudentList object.

Posted by smash | with no comments
Filed under: , ,

About two nights ago, I learned how to use an external XAML in  a WPF application. It was so simple. Just read the XAML, parse it, and then wire the elements to your code.  Now if this is how we are supposed to code in WPF, then I might just as well stick to WinForms.

Fortunately XAML was not designed like Winforms or even Webforms.  The FrameworkElement in WPF has a DataContext property which is an object type. This means we can feed it any kind of object that fits our purpose.  The great thing about this is, XAML has the capability to bind all the elements in the object tree to the root element's DataContext property.  All we have to do is bind an element's property or properties to the corresponding property or properties of the object concerned like so:

<StackPanel
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <ListBox
        Name="listBox1"
        IsSynchronizedWithCurrentItem="true"
        ItemsSource="{Binding Path=Employees}"/>
    <Button
        CommandParameter="{Binding ElementName=listBox1}"
        Command="{Binding Path=DeleteEmployee}"
        Content="Delete"/>
</StackPanel>

Assuming we have a class called CompanyViewController that exposes the properties Employees (ListCollectionView) and DeleteEmployee (ICommand), and the XAML is stored in EmployeeListView.xaml, the code to drive this XAML could simply look like this:

class EmployeeListView(Window):
	def constructor(company as Company):
		# parse the element tree via the XamlReader
		streamReader = StreamReader("EmployeeListView.xaml")
		xmlreader = XmlReader.Create(streamReader)		
		view as Panel= XamlReader.Load(xmlreader)
                
                # give the view something to chew on
		view.DataContext = CompanyViewController(company)
		
		# display the view in the window
		self.Content = view

If you are a TDD kind of guy, you have to feel that tingling sensation! 



                
Posted by smash | with no comments
Filed under: , , , ,

My first impression with XAML was that you get to play with it only if your building WPF apps in c#.  It seems the .NET3  c# compiler is the only one capable of compiling XAML markup and code into an executable file.  It was a big letdown for Boo aficionados like me since XAML is clearly the way to go when it comes to creating user interfaces in WPF. 

I was dead wrong. It seems any .NET language like Boo can still leverage XAML with a simple workaround as illustrated by Rob Relyea on his post about the 3 Coding Styles for Avalon Applications.

To see if Rob's workaround will work, I wrote the following markup in XamlPad and copy pasted it to a file called "Exer1.xaml" in my Boo project. I didn't want the Boo compiler to touch this markup at compile time so I set its Build action property to None and its Copy to output directory property to PreserveNewest. 

<StackPanel
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Margin="10">
    <DockPanel     Width="300" HorizontalAlignment="Left"    Margin="10 0 0 5">
        <StackPanel DockPanel.Dock="Left">
            <TextBox
                Name="textBox1"
                Width="230"
                FontSize="12"
                FontFamily="Tahoma"
                Margin="0 0 2 2"/>
        </StackPanel>
        <Button
            Name="addButton"
            DockPanel.Dock="Right"
            VerticalAlignment="Top"
            Width="60"
            Content="Add"/>
    </DockPanel>
    <DockPanel HorizontalAlignment="Left" Width="300" Margin="10 0 0 0">
        <StackPanel DockPanel.Dock="Left">
            <ListBox
                Name="listBox1"
                Width="230"
                Height="100"
                FontSize="12"
                FontFamily="Tahoma"
                Background="GhostWhite"
                Margin="0 0 2 2">            
            </ListBox>
        </StackPanel>
        <Button
            Name="delButton"
            DockPanel.Dock="Right"
            VerticalAlignment="Top"
            Width="60"
            Content="Delete"/>
    </DockPanel>
</StackPanel>

Create the markup with XamlPad

Next, I added the following Boo code to the project.  The logic of the program is simple. When you click on the Add button (addButton), whatever you enter in the TextBox (textBox1) is placed on the ListBox (listBox1) below it.  When you click on the Delete button (delButton), the selected item on the listbox will be removed.

namespace WinFx.Demo

import System
import System.Collections
import System.Windows
import System.Windows.Controls
import System.Windows.Controls.Primitives
import System.Windows.Input
import System.Windows.Media
import System.Windows.Markup
import System.Windows.Shapes
import System.IO
import System.Xml

class Exer1(Window):
	textBox1 as TextBox 
	listBox1 as ListBox
	addButton as Button
	delButton as Button
	
	def constructor():
		# parse the element tree via the XamlReader
		streamReader = StreamReader("Exer1.xaml")
		xmlreader = XmlReader.Create(streamReader)		
		view as Panel= XamlReader.Load(xmlreader)
		
		# get a reference to the named elements via the LogicalTreeHelper
		textBox1  = LogicalTreeHelper.FindLogicalNode(view, "textBox1")
		listBox1  = LogicalTreeHelper.FindLogicalNode(view, "listBox1")
		addButton  = LogicalTreeHelper.FindLogicalNode(view, "addButton")
		delButton  = LogicalTreeHelper.FindLogicalNode(view, "delButton")
		
		# wire up the elements to your code
		addButton.Click += AddButtonClick
		delButton.Click += DelButtonClick		
		
		# customize the window 
		self.Content = view
		self.WindowStartupLocation = WindowStartupLocation.CenterScreen
		self.Title = "XAML Meets Boo"
		self.Width = 500
		self.Height = 300
		
	protected def AddButtonClick(sender as object, e as RoutedEventArgs):
		listBox1.Items.Add(textBox1.Text)
		textBox1.Text = ""
		textBox1.Focus()
			
	protected def DelButtonClick(sender as object, e as RoutedEventArgs):
		index = listBox1.SelectedIndex
		if index >= 0:
			listBox1.Items.RemoveAt(index)
			
		
[STAThread]
def Main():
	app = Application()	
	Exer1().Show()
	app.Run()

Here's a screenshot of this in action.

Wiring XAML elements to code

The code above looks very much like a regular WinForm code except the contents of the window are defined in an external XAML file. Shades of ASP.NET? One of the neat things about this scenario is that I can retouch the XAML sometime later using Sparkle for some eyecandy effects and then run the same app again with no recompilation whatsoever. 

Posted by smash | 1 comment(s)
Filed under: , , ,