Welcome to MSDN Blogs Sign in | Join | Help

Back from holiday, and just a short post to let you know that Blacklight has been updated for Silverlight 2 RC0.

To get the release candidate for Silverlight 2 go here: http://silverlight.net/GetStarted/sl2rc0.aspx

To view the RC0 compatible version of the Showcase, you can go to: http://mightymeaty.members.winisp.net/blacklight.silverlight.rc0/

You can still see the Beta 2 version of the Showcase here, however: http://mightymeaty.members.winisp.net/blacklight.silverlight/

I have updated release v0.1 to also have the RC0 ready source code too. So head over to http://www.codeplex.com/blacklight for the bits.

Martin

Hey - very exciting news today... I finally have a CodePlex project set up. From the site...

'Blacklight is a UX focused code sharing project. Microsoft has released a bunch of technologies that allow designers and developers to work closely together to make beautiful software. This project is a collection of controls, samples, visual assets and ideas that has been put together by User Experience designers and developers to both show you what the technology is capable of (from a UX point of view), and give you code and samples that you can use in your own projects.'

We are currently delivering stuff in Silverlight, but have plenty more technologies to follow in the near future.

We also have a snazzy showcase that you can view the content through too. So, without further adue, here are the links to get to the stuff...

See Blacklight on CodePlex here.

See the Blacklight showcase site right here.

 [SCREENSHOT OF THE SHOWCASE SITE]

I hope to do releases on a monthly basis, each release introducing new controls and samples. I will also be posting about new content right here on my blog too,so stay tuned!

Right off to Remix 08 tomorrow, and then on vacation for a week! Woohoo.

Laters, Martin.

Hi all - great news, an updated version of the Patient Journey Demonstrator, the project I am currently working on, has been released. Check it out at http://www.mscui.net/PatientJourneyDemonstrator.

In this release we fixed lots of bugs, as well as added some new features around capturing input from the user. Below is a list of some of the new features and a video walkthrough of the whole demonstrator...

Presence Indicators

Presence and availability is a really important theme in health care. Everywhere where we display an employee’s name, we show their presence...

Updated Navigation Bar

The updated navigation bar allows you to move backwards and forwards through different demonstrators too.

Silverlight Patient Banner

We have incorporated the new Silverlight patient banner, from the Microsoft Health CUI controls library.

Menu Pearl

We have a new menu pearl that will allow you access to some of the new features within the demonstrator - in this release, the ability to add a consultation!

Creating a Consultation

In the primary care demonstrator, you can now create a new consultation, prescribe medications and record observations. You can then save this to the patient record and see the activity list and meds list update!

SNOMED Terminology Lookup

We have taken a subset of the SNOMED database of clinical terms, and made these searchable when creating a consultation. Try typing ‘Chest Pain’.

Search and Prescribe Medications

Search for medications and prescribe to see the meds list view update.

Styled Meds List View

We have styled the Microsoft Health CUI Meds List View control to fit in visually with the demonstrator.

Drug details on Patient Chart’s table view

We have added columns to the table view in Patient Charts for showing the drugs the patient is taking.

Visual Makeover of the ECG charts

We are looking at different ways of representing the deep zoom, digital ECG data (in the Secondary Care Demonstrator). One of those explorations was to make the full result set look more like the live ECG. This is an easter egg, and requires you to press F8 once, as soon as the Secondary Care Demonstrator has loaded!

Annotating the Angiogram

We have added a bunch of features that allow you to annotate the ECG video in the secondary care demonstrator...

Including a tool bar for measuring, inking, drawing marquees, zooming in and going full screen...

A collapsible list of the annotation history...

The ability to attach a text note to each annotation...

A frame by frame selector allowing you to see what annotations are already on the video and provide navigation to those frames...

Walkthrough Video

Check out the video below for a complete walkthrough of the demonstrator - or better still - check it out at http://www.mscui.net/PatientJourneyDemonstrator.

Download the video here.

Keep checking here for future Patient Journey Demonstrator updates :)

Martin

UPDATE: Get the latest Dragging, docking, expanding panel code from Blacklight, our new CodePlex project! 

In Part 1, we looked at how we construct a Dragging, docking, expanding panel, and added the ‘dragging’ functionality by placing the panel in a Canvas. In Part 2 we looked at the host panel that controls the grid layout and the docking functionality. In this part, we are going to look at how we add the maximising functionality.

 

We will start with some additional code we need to add the DragDockPanel. The toggle button for toggling a panel’s maximised state is already in the template (see Part 1). We now need to get the toggle button out, and hook up the checked and unchecked events...

    ToggleButton maximizeToggle =

        this.GetTemplateChild("PART_MaximizeToggle") as ToggleButton;

 

    if (maximizeToggle != null)

    {

        maximizeToggle.Checked +=

            new RoutedEventHandler(maximizeToggle_Checked);

        maximizeToggle.Unchecked +=

            new RoutedEventHandler(maximizeToggle_Unchecked);

    }

Next, we add a private variable that will be used as a flag to store whether a control was minimised by the toggle button, or minimised programmatically...

    private bool ignoreUnCheckedEvent = false;

We now add a public member to get / set whether the panel is maximised or not.

    private bool isMaximized = false;

    public bool IsMaximized

    {

        get { return this.isMaximized; }

        set

        {

            this.isMaximized = value;

            ToggleButton maximizeToggle =

                this.GetTemplateChild("PART_MaximizeToggle")

                as ToggleButton;

 

            if (maximizeToggle != null)

            {

                this.ignoreUnCheckedEvent = true;

                maximizeToggle.IsChecked = this.isMaximized;

            }

        }

    }

In the setter for IsMaximized, we store the value locally, and also update the toggle’s button checked state. This is where we use ignoreUnCheckedEvent. When we programmatically set the checked state of a toggle button, the Checked / UnChecked event is also raised from the button, so here we set a flag to say that we have programmatically set the property so we can ignore the next event that is raised.

Let’s take a look at the event handler for the toggle button’s checked event...

    void maximizeToggle_Checked(object sender, RoutedEventArgs e)

    {

        // Bring the panel to the front

        Canvas.SetZIndex(this, currentZIndex++);

 

        this.ignoreUnCheckedEvent = false;

 

        // Fire the panel maximized event

        if (this.Maximized != null)

            this.Maximized(this, e);

    }

The handler firstly brings the panel to the front, sets the flag to false again, and finally fires the panel’s own Maximized event for the host to pick up and respond too.

The unchecked handler is similar...

    void maximizeToggle_Unchecked(object sender, RoutedEventArgs e)

    {

        if (!this.ignoreUnCheckedEvent)

        {

            this.IsMaximized = false;

 

            // Fire the panel minimized event

            if (this.Minimized != null)

                this.Minimized(this, e);

        }

        else

        {

            this.ignoreUnCheckedEvent = false;

        }

    }

Here, we check the flag, and if we don’t ignore the event, we set IsMaximized to false, and raise the panel’s own Minimized event. Otherwise we just reset the flag to false.

That’s it for DragDockPanel. Let’s take a look at DragDockPanelHost where we handle the layout rearranging...

Firstly, we add a private member to store the maximised panel...

    private DragDockPanel maximizedPanel = null;

When a panel is maximised, the other panels move to a stack on the right hand side. We have a public property that allows you to set how wide you wish to make that stack...

    private double minimizedColumnWidth = 250.0;

    public double MinimizedColumnWidth

    {

        get { return this.minimizedColumnWidth; }

        set { this.minimizedColumnWidth = value; }

    }

In Part 2, in the DragDockPanelHost Loaded event handler, we hooked up the events necessary to handle the panel dragging. In the same place, we can add handlers to the panels’ minimised and maximised events...

    panel.Maximized +=

        new EventHandler(dragDockPanel_Maximized);

 

    panel.Minimized +=

        new EventHandler(dragDockPanel_Minimized);

The 2 handlers are quite simple - that bulk of the work takes place in our layout methods. The maximised handler looks like this...

    void dragDockPanel_Maximized(object sender, EventArgs e)

    {

        DragDockPanel maximizedPanel =

            sender as DragDockPanel;

 

        // Store max'ed panel

        this.maximizedPanel = maximizedPanel;

 

        // Loop through children to disable dragging

        foreach (UIElement child in this.Children)

        {

            DragDockPanel panel =

                child as DragDockPanel;

 

            panel.DraggingEnabled = false;

 

            if (panel != this.maximizedPanel)

                panel.IsMaximized = false;

        }

 

        // Update sizes and layout

        this.AnimatePanelSizes();

        this.AnimatePanelLayout();

    }

When a panel is maximised, we keep a reference to the maximised panel, loop through all of the children disabling dragging, and setting all of the other panels IsMaximized property to false, and finally, updating the layout with the animating methods.

The minimised handler is very similar...

    void dragDockPanel_Minimized(object sender, EventArgs e)

    {

        // Set max'ed panel to null

        this.maximizedPanel = null;

 

        // Loop through children to disable dragging

        foreach (UIElement child in this.Children)

        {

            DragDockPanel panel =

                child as DragDockPanel;

            panel.DraggingEnabled = true;

        }

 

        // Update sizes and layout

        this.AnimatePanelSizes();

        this.AnimatePanelLayout();

    }

We clear the reference to the maximised panel, and then loop through all of the children, re-enabling dragging. Finally, we update the layout using the animated methods.

Let’s take a look at the animated size and layout methods, firstly, AnimatePanelLayout.

private void AnimatePanelLayout()

    {

        // If we are not in max'ed panel mode...

        if (this.maximizedPanel == null)

        {

            // Place panels in a grid...

        }

        else // If a panel is maximized...

        {

            // Layout the children with one panel maxmisised.

        }

     }

 

Firstly, we check to see if there is a maximised panel - if not, we use the code we already have to put them into a grid, if so, then we lay them out, with one maximised.

Something we need to consider here is that the panels may have been shuffled around, so they are no longer displayed in the same order that the host has them in the visual tree. So firstly, we need to get the current order of the panels, going left to right across rows, and down each row.

Dictionary<int, DragDockPanel> orderedPanels =

                new Dictionary<int, DragDockPanel>();

 

            // Loop through children to order them according to their

            // current row and column...

            foreach (UIElement child in this.Children)

            {

                DragDockPanel panel = (DragDockPanel)child;

 

                orderedPanels.Add(

                    (Grid.GetRow(panel) * this.columns) +

                    Grid.GetColumn(panel),

                    panel);

            }

 

Here we create a dictionary that stores an index of a panel, and the panel itself. The index is calculated using the panel’s row and column properties. This gives us the real position of the panel (in terms of a stacking order). Now we have the panels in order, we can loop through and position them.

// Set initial top of minimized panels to 0

            double currentTop = 0.0;

 

            // For each of the panels (as ordered in the grid)

            for (int i = 0; i < orderedPanels.Count; i++)

            {

                // If the current panel is not the maximized panel

                if (orderedPanels[i] != this.maximizedPanel)

                {

                    // Animate the size

                    orderedPanels[i].AnimatePosition(

                        this.ActualWidth -

                        this.minimizedColumnWidth,

                        currentTop

                        );

 

                    // Increment current top

                    currentTop +=

                        this.ActualHeight /

                        (double)(this.Children.Count - 1);

                }

                else // If the current panel is the maxmized panel

                {

                    // Animate it to 0,0

                    orderedPanels[i].AnimatePosition(0, 0);

                }

 

The simple loop goes through each panel. If it’s not the maximised panel, it stacks it on the right hand side, and keeps track of the position in a variable currentTop. If the panel is the maximised panel, then we move it to 0,0.

That’s all that’s required to position the panels when one is maximised. Let’s take a quick look at the panel sizing.

    private void AnimatePanelSizes()

    {

        // If there is not a maxmized panel...

        if (this.maximizedPanel == null)

        {

            // Size to grid cells

            ...

        }

        else // If there is a maximized panel

        {

            // Size for a maximised panel.

           

        }

    }

You will see that first we check if there is a maximised panel - if not, we use the code we had before to size the elements for a grid. Otherwise we loop through children, giving the appropriate sizes.

            foreach (UIElement child in this.Children)

            {

                DragDockPanel panel =

                    (DragDockPanel)child;

 

                // Set the size of the non

                // maximized children

                if (panel != this.maximizedPanel)

                {

                    panel.AnimateSize(

                        minimizedColumnWidth -

                        panel.Margin.Left -

                        panel.Margin.Right,

                        (this.ActualHeight /

                        (double)(this.Children.Count - 1)) -

                        panel.Margin.Top - panel.Margin.Bottom

                        );

                }

                else // Set the size of the maximized child

                {

                    panel.AnimateSize(

                        this.ActualWidth -

                        this.minimizedColumnWidth -

                        panel.Margin.Left - panel.Margin.Right,

                        this.ActualHeight -

                        panel.Margin.Top - panel.Margin.Bottom

                        );

                }

            }

 

As we loop through the children, we check to see if the current panel is the maximised one - if it isn’t then we size the panel to fit on the stack on the right hand side, if it is, then we size the panel to fill the rest of the space. Cool.

The same logic is added to the UpdatePanelLayout method, but we won’t go through that here.

I have decided to do templating of the panels in a video, showing off Blend 2.5. That will be the final part of the Drag-Dock-Expanding panel sample. We will move onto something new after that!

Source code is attached as always.

Check out a running sample here!

Martin

UPDATE: Get the latest blog media player code from Blacklight, our new CodePlex project!  

I have been meaning to start video blogging (vlogging) for some time. I think you can get stuff across far more simply that way. However, I hadn't gotten around to building a simple video player for hosting on my blog - well, problem solved. I finally got off my backside and made one.

I decided to make my first video blog on the media player itself, so sit back and enjoy.

Introducing the new Video Blog Media Player

You can download the video too. 

I have attached the source code for the media player to the post if you want to change / host it yourself. Thanks to Tim Sneath for giving the example on how to host Silverlight on your blog

You could also point to where I have hosted the page (if you don't have hosting yourself) - however, I cannot guarantee the connection or that the MediaPlayer will be bug free.

The player is hosted on this page... http://mightymeaty.members.winisp.net/MediaPlayer/MediaPlayer.html

You need to pass in a source to activate the player - i.e. http://mightymeaty.members.winisp.net/MediaPlayer/MediaPlayer.html?mediaSource=http://silverlight.services.live.com/11264/vlog_200808/video.wmv.

Happy vlogging!

Martin

UPDATE: Get the latest Dragging, docking, expanding panel code from Blacklight, our new CodePlex project!  

In Part 1, we looked at how we construct a Dragging, docking, expanding panel, and added the ‘dragging’ functionality by placing the panel in a Canvas. In this part, we are going to look at how we do the docking element.

Take a peek at the finished sample running, here.

The positioning and docking logic takes place in a host control, called DragDockPanelHost. This is a panel control, that derives Canvas, that positions the panels in a grid and then moves them around when the user is dragging DragDockPanel.

[But first, a small digression...]

Why derive Canvas and not Panel I hear you cry??? Dave Relyea posted a while back on why he doesn’t like Canvas. Whilst he makes some good points, I feel he missed a trick when it comes to considering Canvas for the type of layout we do here.

Deriving Panel gives you 2 methods - Measure and Arrange - tell the layout system how big you want to be, then layout your children. Simple for when you want your children to go to a specific place every time the layout updates. However, with our layout system, we are more complicated. We have panels that can be dragged, shuffled around, maximised etc. etc.

To do all of this by deriving Panel seemed difficult and cumbersome, however, when starting with Canvas, you have a superbly basic layout system, using absolute positioning and giving you animatable positioning properties. I didn’t need to worry about layout updated events at all, but just consume the custom events my panels raised.

Simple. I like Canvas for this kind of purpose. Please challenge me if you feel differently!

[Digression ends.]

OK. So first off, we create out class and derive from Canvas...

    public class DragDockPanelHost : Canvas

    {

        #region Private members

        // A local store of the number of rows

        private int rows = 1;

 

        // A local store of the number of columns

        private int columns = 1;

 

        // The panel currently being dragged

        private DragDockPanel draggingPanel = null;

        #endregion

 

        #region Constructors

        /// <summary>

        /// Constructor

        /// </summary>

        public DragDockPanelHost()

        {

            this.Loaded +=

                new RoutedEventHandler(DragDockPanelHost_Loaded);

            this.SizeChanged +=

                new SizeChangedEventHandler(DragDockPanelHost_SizeChanged);

        }

        #endregion

    }

We also have some private variables that store the number of rows and columns (so we don’t have to recalculate every time) as well as the currently dragging panel. In the constructor, we hook up the Loaded events and SizeChanged events.

When we get the Loaded event, we do the work to calculate how may rows and columns we require (based on how many children the panel has), place each panel in a grid and row, and hook up some events from the panel.

(NOTE - we only do this in the Loaded event, meaning panels added later won’t be ‘hooked up’. Please check out the limitations section at the end of the post for more details on this.)

Firstly, we work out the number of rows. When placing the panels in a grid like layout, we try and lay them out in a square, giving preference to width over height (as most resolutions are 4:3 or wider). Let me give you an example...

If we had 6 panels, we would have 2 rows with 3 panel in each row, rather than 3 rows with 2 panels in each. If we had 8 panels, we would have 2 rows with 4 panels in each. If we had 9 panels, we would have 3 rows, with 3 panels in each.

To work out the rows, we take the square root of the number of children, and round it down.

Once we know how many rows we have, we can work out how many columns are required...

    // Calculate the number of rows and columns required

    this.rows =

        (int)Math.Floor(Math.Sqrt((double)this.Children.Count));

 

    this.columns =

        (int)Math.Ceiling((double)this.Children.Count / (double)rows);

We then loop through the rows and colu