Category Archives: Uncategorized

Clear all .svn bindings from a directory

dir /B /S ".svn" /AD >a.list & for /F "delims=" %a in (a.list) DO (attrib -H "%a" & rd /Q /S "%a") & del /Q a.list

Similarly – clear all bin/obj/resharper directories

dir /B /S "bin" /AD >a.list & for /F "delims=" %a in (a.list) DO (attrib -H "%a" & rd /Q /S "%a") & del /Q a.list
dir /B /S "obj" /AD >a.list & for /F "delims=" %a in (a.list) DO (attrib -H "%a" & rd /Q /S "%a") & del /Q a.list
dir /B /S "_ReSharper*" /AD >a.list & for /F "delims=" %a in (a.list) DO (attrib -H "%a" & rd /Q /S "%a") & del /Q a.list

Note to self – use double ampersands in batch files.

Batch script to clean a solution folder

dir /B /S ..\"bin" /AD >a.list & for /F "delims=" %%a in (a.list) DO (attrib -H "%%a" & rd /Q /S "%%a")
dir /B /S ..\"obj" /AD >b.list & for /F "delims=" %%a in (b.list) DO (attrib -H "%%a" & rd /Q /S "%%a")
dir /B /S ..\"_ReSharper*" /AD >c.list & for /F "delims=" %%a in (c.list) DO (attrib -H "%%a" & rd /Q /S "%%a")
dir /B /S ..\"Debug" /AD >d.list & for /F "delims=" %%a in (d.list) DO (attrib -H "%%a" & rd /Q /S "%%a")
dir /B /S ..\"x64" /AD >e.list & for /F "delims=" %%a in (e.list) DO (attrib -H "%%a" & rd /Q /S "%%a")

GOTO EOF

del a.list
del b.list
del c.list
del d.list
del e.list

:EOF
Tagged ,

Using attached properties to style RowDefinitions of a Grid in WPF

The below sample shows how you can use attached dependency properties to style a Grid’s RowDefinitions. This specific case shows how a single property can be set to specify the number of rows in a Grid.

<Window
    x:Class="GridExtensions.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:GridExtensions="clr-namespace:GridExtensions"
    Title="MainWindow"
    Height="350"
    Width="525">
    <Grid>
        <Grid.Style>
            <Style>
                <Setter Property="GridExtensions:Griddier.RowsCount" Value="4"/>
            </Style>
        </Grid.Style>
        <TextBlock Grid.Row="0" Text="0"/>
        <TextBlock Grid.Row="1" Text="1"/>
        <TextBlock Grid.Row="2" Text="2"/>
        <TextBlock Grid.Row="3" Text="3"/>
    </Grid>
</Window>
namespace GridExtensions
{
    using System;
    using System.Windows;
    using System.Windows.Controls;

    public static class Griddier
    {
        #region RowsCount

        /// <summary>
        /// RowsCount Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty RowsCountProperty =
            DependencyProperty.RegisterAttached("RowsCount", typeof(int), typeof(Griddier),
                new FrameworkPropertyMetadata((int)0,
                    new PropertyChangedCallback(OnRowsCountChanged)));

        /// <summary>
        /// Gets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static int GetRowsCount(DependencyObject d)
        {
            return (int)d.GetValue(RowsCountProperty);
        }

        /// <summary>
        /// Sets the RowsCount property. This dependency property 
        /// indicates the number of star-heighted row definitions to include with the grid.
        /// </summary>
        public static void SetRowsCount(DependencyObject d, int value)
        {
            d.SetValue(RowsCountProperty, value);
        }

        /// <summary>
        /// Handles changes to the RowsCount property.
        /// </summary>
        private static void OnRowsCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d == null)
                throw new ArgumentNullException("d");
            var grid = d as Grid;
            if (grid == null)
                throw new ArgumentException("d should be of type Grid.", "d");
            int newRowsCount = (int)d.GetValue(RowsCountProperty);

            if (grid.RowDefinitions.Count > newRowsCount)
                while (grid.RowDefinitions.Count > newRowsCount)
                    grid.RowDefinitions.RemoveAt(newRowsCount);

            var gl = (GridLength)(new GridLengthConverter().ConvertFromString("*"));

            for (int i = 0; i < newRowsCount; i++)
            {
                if (grid.RowDefinitions.Count > i)
                    grid.RowDefinitions[i].Height = gl;
                else
                    grid.RowDefinitions.Add(new RowDefinition {Height = gl});
            }
        }

        #endregion
    }
}
Tagged , , , , , ,

Real-time Client-Server Communication on Windows Phone with Long Polling

I was looking for some information on Push technologies and the first thing I found was the Wikipedia article which among other things mentioned a technique called “Long Polling”. This seemed like a really feasible solution to the problem I have seen many people struggling with – getting real-time communication from the phone. Now I can see some limitations – an app needing to keep running to keep it from losing connection (this might change with one of the upcoming updates though if it enables multitasking), possible data usage or server security exposure problems, but I have little experience with server development, so why would I worry about that? Smile

I decided to try writing a Windows Phone 7 app with a back-end WCF service and a non-WP7-Silverlight app to talk to. This is simple – just create a solution in VS and a project for each of these three programs.

image

Let’s start with the WCF service.

Configure the WCF service to run on a specific port – go to project/properties/web and set “Specific port” to 2333. This will give you a reliable location to reference the service.

Add clientaccesspolicy.xml to the WCF service to let the SL4 app connect.

Rename (Refactor/Rename) Service1 to ChatService and define the service contract:

[ServiceContract]
public interface IChatService
{
    [OperationContract]
    void Send(string message, string recipient);
 
    [OperationContract]
    string GetMessage(string recipient);
}

As you can see – the service class will be simplified to only send messages to a given recipient and let a recipient poll for a message. This is the service class definition:

public class ChatService : IChatService
{
    // A list of messages for each recipient
    private static Dictionary<string, List<string>> messages = 
        new Dictionary<string, List<string>>();
 
    private static object syncObject = 
        new object();
 
    private static readonly TimeSpan Timeout = 
        TimeSpan.FromSeconds(50);
    
    public void Send(string message, string recipient)
    {
        lock (syncObject)
        {
            if (!messages.ContainsKey(recipient))
                messages.Add(recipient, new List<string>());
 
            messages[recipient].Add(message);
        }
    }
 
    public string GetMessage(string recipient)
    {
        var start = DateTime.Now;
 
        do
        {
            lock (syncObject)
            {
                if (!messages.ContainsKey(recipient))
                    messages.Add(recipient, new List<string>());
                else
                {
                    if (messages[recipient].Count > 0)
                    {
                        var message = messages[recipient][0];
                        messages[recipient].RemoveAt(0);
                        return message;
                    }
                }
 
                // Yes, just faking it out a bit to avoid the distractions from events,
                // resetevents etc.
                Thread.Sleep(50);
            }
        } while (DateTime.Now - start < Timeout);
 
        return null;
    }
}

We have a dictionary that has a list of messages for each registered recipient, the Send method just adds a message to the list and the GetMessage keeps polling to see if there is a new message in the list, then either returns one if one shows up or times out and returns null. The client will handle nulls and timeouts and interpret them as the good time to start another long poll.

Now do both WP7 and SL4 apps in parallel

Add service reference to both Silverlight projects, referencing http://localhost:2333/ChatService.svc as ChatService.

Now edit MainPage.xaml files for both apps – set x:Name=”root” on the root elements, then add this simplistic view as its Content shared by both apps:

 <Grid
    x:Name="LayoutRoot"
    Loaded="LayoutRoot_Loaded">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
 
    <ListBox
        Grid.Row="0"
        Grid.ColumnSpan="2"
        ItemsSource="{Binding Messages, ElementName=root}" />

    <TextBox 
        x:Name="InputBox"
        Grid.Row="1"
        Grid.Column="0"
        Text="{Binding Message, Mode=TwoWay, ElementName=root}" />

    <Button
        x:Name="SendButton"
        Grid.Row="1"
        Grid.Column="1"
        Click="SendButton_Click"
        Content="Send"
        Padding="10,0" />

</Grid>

The code behind for both will be almost identical – just swap recipients. The one below is for the WP7 version – it sends messages to “SLPC” and gets messages for “WP7”.

public partial class MainPage : PhoneApplicationPage
{
    public static readonly DependencyProperty MessagesProperty =
        DependencyProperty.Register("Messages", typeof(ObservableCollection<string>), typeof(MainPage),
            new PropertyMetadata(null));
 
    public ObservableCollection<string> Messages
    {
        get { return (ObservableCollection<string>)GetValue(MessagesProperty); }
        set { SetValue(MessagesProperty, value); }
    }
 
    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(MainPage),
            new PropertyMetadata(""));
 
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
 
    // Constructor
    public MainPage()
    {
        this.DataContext = this;
        Messages = new ObservableCollection<string>();
        InitializeComponent();
    }
 
    private void SendButton_Click(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.SendAsync(Message, "SLPC");
        Message = "";
    }
 
    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.GetMessageCompleted += OnGetMessageCompleted;
        svc.GetMessageAsync("WP7");
    }
 
    private void OnGetMessageCompleted(object sender, ChatService.GetMessageCompletedEventArgs e)
    {
        var svc = (ChatService.ChatServiceClient) sender;
 
        if (e.Error is TimeoutException ||
            (e.Error == null && e.Result == null))
        {
            svc.GetMessageAsync("WP7");
            return;
        }
 
        if (e.Error != null)
            MessageBox.Show(e.Error.Message);
        else
        {
            Messages.Add(e.Result);
        }
 
        svc.GetMessageAsync("WP7");
    }
}

As you can see we have two dependency properties for the view to bind to – the list of Messages and the Message to send. Clicking on Send will simply send the message through the service proxy, while as soon as the page is loaded – polling on GetMessage starts and OnGetMessageCompleted will check for timeouts and nulls, show any messages incoming and reissue requests to GetMessage.

Conclusion

What we get is this – a Windows Phone and a Silverlight 4 apps talking to one another with virtually no delay.

image

Now there are a lot of simplifications here – no authentication of clients, hardcoded recipients, the server using polling to check for new messages from other threads etc. but it works!

I am sure this can be improved and perhaps there are better or simpler ways to achieve this and I am open to comments.

Tagged , , , , ,

Binding to properties of a custom property in WPF, Silverlight 4 or Windows Phone 7

I was trying to create a UserControl for Windows Phone 7 yesterday and I wanted to enable specifying a  collection-type property that I could configure in my View/XAML file.

I have registered a DependencyProperty, but since I have not done many UserControls or CustomControls in WPF or SL, so I thought I would look how it is done in other similar places – like Grid.ColumnDefinitions. OK, so it is a custom type RowDefinitionCollection based on PresentationFrameworkCollection<RowDefinition>. I thought an ObservableCollection<MyItemType> or a List<> would work, but something did not work right, so I thought derive from PresentationFrameworkCollection<>, but when I tried – it turned out it does not have an accessible parameterless constructor which turned out to be some sort of an issue. I really wanted to make it work, so I thought – OK, I will implement my own version, so I created my collection type as a wrapper around an ObservableCollection, that derives from a FrameworkElement (I played with DependencyObject too) and implements all these interfaces – ICollection, ICollection<>, IList, IList<>, IEnumerable, IEnumerable<>.

All was good – I defined MyItemType as deriving from a FrameworkElement too and added some properties, among others an ICommand-typed-Command and CommandParameter. All hell broke loose when I tried to bind to the Command in XAML.

I kept getting these XamlParseException AG_E_PARSER_BAD_PROPERTY_VALUE on the line that had the binding to the Command. I tried looking at this from all angles of Google and Bing and could not find the right solution, until I tried the below and got the same problem!

<Grid>
    <Slider
        x:Name="slider"
        VerticalAlignment="Top"
        HorizontalAlignment="Stretch"
        Minimum="0"
        Maximum="100" />

    <Grid
        VerticalAlignment="Bottom"
        HorizontalAlignment="Stretch"
        Height="100">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{Binding Value, ElementName=slider}"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
 
        <Rectangle
            Fill="Red"
            Grid.Column="0"
            VerticalAlignment="Stretch"
            HorizontalAlignment="Stretch" />
    </Grid>
</Grid>

OK, so it seems like binding on ColumnDefinitions is not supported on WP7 either. Tried the same snippet in WPF – worked great. Silverlight 4 – no exception, but it just would not update my rectangle. So it seems like Silverlight is limited in that regard.

That was close – I started looking into it more and found that it would almost work, but the DataContext on my collection would not get updated whatever I do. Finally – I found two things I needed to do:

  • Handle DataContext changes on my UserControl and update the DataContext on my collection and do the same from the collection to MyItemType. Only thing – there is no DataContextChanged event. Sheesh… Fortunately we have our MVPs to the rescue – I found a solution on CodeProject quite easily – http://www.codeproject.com/Articles/38559/Silverlight-DataContext-Changed-Event.aspx – just added IDataContextChangedHandler to both the control and the collection and update the DataContext on their properties.
  • Made sure that MyItemsProperty DependencyProperty registration does not specify the default value as “new MyItemsCollection(MyUserControl)”, but rather do it in the constructor – otherwise all instances of the control would share the same collection! This is said to cause problems when applying styles to controls, since setting the value on the control overrides anything being set by the style, but I will not be bothered by it, since the control implementation isn’t lookless or re-template-able anyway.

It works!

Tagged , , ,

How to unzip a group of zip files?

The following one-line batch script will unzip all zip files in current directory to separate directories. Assuming you have an unzip tool in your path…

It checks each zip file in current directory and if there is no directory with same name as the file name without extension – it creates the directory and unzips the file into the directory.

for %a in (*.zip) DO (if not exist %~na (md %~na & cd %~na & unzip ..\%a & cd ..))

This one will also delete the zip files:

for %a in (*.zip) DO (if not exist %~na (md %~na & cd %~na & unzip ..\%a & cd .. & del /q %a))

Tagged

Fixing full-screen WPF windows

2011-12-13 – Note – if you got here trying to find out how to create a full-screen WPF window – you need to set WindowState=”Maximized” and WindowStyle=”None”. This post is about a workaround for an issue that will sometimes arise when doing this.

A while back when I was working on a full-screen WPF application – I found a bug when sometimes, especially after you have changed your screen resolution – the application would show up with a visible bright border, which looks pretty ugly when the UI is mostly black and the display has a black frame as most displays seem to these days. I have found two related bugs reported, but only vague description of a workaround and no fix.

  1. https://connect.microsoft.com/VisualStudio/feedback/details/363288/maximised-wpf-window-not-covering-full-screen?wa=wsignin1.0
  2. https://connect.microsoft.com/VisualStudio/feedback/details/540394/maximized-window-does-not-cover-working-area-after-screen-setup-change?wa=wsignin1.0

I have managed to get some hints for how to solve it to a reusable copy&paste fix I am now using in various places and I thought I would share it:

1. Hook up the handler for the SourceInitialized event in your maximized window’s XAML or code behind and define it as below:

public MainWindow()
{
    InitializeComponent();
    this.SourceInitialized += mainWindow_SourceInitialized;
}

private void mainWindow_SourceInitialized(object sender, EventArgs e)
{
    IntPtr handle = (new WindowInteropHelper(this)).Handle;

    // Use WindowProc as the callback method
    // to process all native window messages.
    HwndSource.FromHwnd(handle)
        .AddHook(NativeMethods.MaximizedSizeFixWindowProc);
}

2. Add the NativeMethods class to your project.

using System;
using System.Runtime.InteropServices;

/// <summary>
/// Native Windows API methods and interfaces.
/// </summary>
internal static class NativeMethods
{
    #region Constants
    // The WM_GETMINMAXINFO message is sent to a window when the size or
    // position of the window is about to change.
    // An application can use this message to override the window's
    // default maximized size and position, or its default minimum or
    // maximum tracking size.
    private const int WM_GETMINMAXINFO = 0x0024;

    // Constants used with MonitorFromWindow()
    // Returns NULL.
    private const int MONITOR_DEFAULTTONULL = 0;

    // Returns a handle to the primary display monitor.
    private const int MONITOR_DEFAULTTOPRIMARY = 1;

    // Returns a handle to the display monitor that is nearest to the window.
    private const int MONITOR_DEFAULTTONEAREST = 2;
    #endregion

    #region Structs
    /// <summary>
    /// Native Windows API-compatible POINT struct
    /// </summary>
    [Serializable, StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int X;
        public int Y;
    }
    /// <summary>
    /// The RECT structure defines the coordinates of the upper-left
    /// and lower-right corners of a rectangle.
    /// </summary>
    /// <see cref="http://msdn.microsoft.com/en-us/library/dd162897%28VS.85%29.aspx"/>
    /// <remarks>
    /// By convention, the right and bottom edges of the rectangle
    /// are normally considered exclusive.
    /// In other words, the pixel whose coordinates are ( right, bottom )
    /// lies immediately outside of the the rectangle.
    /// For example, when RECT is passed to the FillRect function, the rectangle
    /// is filled up to, but not including,
    /// the right column and bottom row of pixels. This structure is identical
    /// to the RECTL structure.
    /// </remarks>
    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        /// <summary>
        /// The x-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public int Left;

        /// <summary>
        /// The y-coordinate of the upper-left corner of the rectangle.
        /// </summary>
        public int Top;

        /// <summary>
        /// The x-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public int Right;

        /// <summary>
        /// The y-coordinate of the lower-right corner of the rectangle.
        /// </summary>
        public int Bottom;
    }

    /// <summary>
    /// The MINMAXINFO structure contains information about a window's
    /// maximized size and position and its minimum and maximum tracking size.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms632605%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct MINMAXINFO
    {
        /// <summary>
        /// Reserved; do not use.
        /// </summary>
        public POINT Reserved;

        /// <summary>
        /// Specifies the maximized width (POINT.x)
        /// and the maximized height (POINT.y) of the window.
        /// For top-level windows, this value
        /// is based on the width of the primary monitor.
        /// </summary>
        public POINT MaxSize;

        /// <summary>
        /// Specifies the position of the left side
        /// of the maximized window (POINT.x)
        /// and the position of the top
        /// of the maximized window (POINT.y).
        /// For top-level windows, this value is based
        /// on the position of the primary monitor.
        /// </summary>
        public POINT MaxPosition;

        /// <summary>
        /// Specifies the minimum tracking width (POINT.x)
        /// and the minimum tracking height (POINT.y) of the window.
        /// This value can be obtained programmatically
        /// from the system metrics SM_CXMINTRACK and SM_CYMINTRACK.
        /// </summary>
        public POINT MinTrackSize;

        /// <summary>
        /// Specifies the maximum tracking width (POINT.x)
        /// and the maximum tracking height (POINT.y) of the window.
        /// This value is based on the size of the virtual screen
        /// and can be obtained programmatically
        /// from the system metrics SM_CXMAXTRACK and SM_CYMAXTRACK.
        /// </summary>
        public POINT MaxTrackSize;
    }

    /// <summary>
    /// The WINDOWINFO structure contains window information.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms632610%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct WINDOWINFO
    {
        /// <summary>
        /// The size of the structure, in bytes.
        /// The caller must set this to sizeof(WINDOWINFO).
        /// </summary>
        public uint Size;

        /// <summary>
        /// Pointer to a RECT structure
        /// that specifies the coordinates of the window.
        /// </summary>
        public RECT Window;

        /// <summary>
        /// Pointer to a RECT structure
        /// that specifies the coordinates of the client area.
        /// </summary>
        public RECT Client;

        /// <summary>
        /// The window styles. For a table of window styles,
        /// <see cref="http://msdn.microsoft.com/en-us/library/ms632680%28VS.85%29.aspx">
        /// CreateWindowEx
        /// </see>.
        /// </summary>
        public uint Style;

        /// <summary>
        /// The extended window styles. For a table of extended window styles,
        /// see CreateWindowEx.
        /// </summary>
        public uint ExStyle;

        /// <summary>
        /// The window status. If this member is WS_ACTIVECAPTION,
        /// the window is active. Otherwise, this member is zero.
        /// </summary>
        public uint WindowStatus;

        /// <summary>
        /// The width of the window border, in pixels.
        /// </summary>
        public uint WindowBordersWidth;

        /// <summary>
        /// The height of the window border, in pixels.
        /// </summary>
        public uint WindowBordersHeight;

        /// <summary>
        /// The window class atom (see
        /// <see cref="http://msdn.microsoft.com/en-us/library/ms633586%28VS.85%29.aspx">
        /// RegisterClass
        /// </see>).
        /// </summary>
        public ushort WindowType;

        /// <summary>
        /// The Windows version of the application that created the window.
        /// </summary>
        public ushort CreatorVersion;
    }

    /// <summary>
    /// The MONITORINFO structure contains information about a display monitor.
    /// The GetMonitorInfo function stores information in a MONITORINFO structure.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd145065%28VS.85%29.aspx"/>
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    private struct MONITORINFO
    {
        /// <summary>
        /// The size, in bytes, of the structure. Set this member
        /// to sizeof(MONITORINFO) (40) before calling the GetMonitorInfo function.
        /// Doing so lets the function determine
        /// the type of structure you are passing to it.
        /// </summary>
        public int Size;

        /// <summary>
        /// A RECT structure that specifies the display monitor rectangle,
        /// expressed in virtual-screen coordinates.
        /// Note that if the monitor is not the primary display monitor,
        /// some of the rectangle's coordinates may be negative values.
        /// </summary>
        public RECT Monitor;

        /// <summary>
        /// A RECT structure that specifies the work area rectangle
        /// of the display monitor that can be used by applications,
        /// expressed in virtual-screen coordinates.
        /// Windows uses this rectangle to maximize an application on the monitor.
        /// The rest of the area in rcMonitor contains system windows
        /// such as the task bar and side bars.
        /// Note that if the monitor is not the primary display monitor,
        /// some of the rectangle's coordinates may be negative values.
        /// </summary>
        public RECT WorkArea;

        /// <summary>
        /// The attributes of the display monitor.
        ///
        /// This member can be the following value:
        /// 1 : MONITORINFOF_PRIMARY
        /// </summary>
        public uint Flags;
    }
    #endregion

    #region Imported methods
    /// <summary>
    /// The GetWindowInfo function retrieves information about the specified window.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms633516%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="pwi">The reference to WINDOWINFO structure.</param>
    /// <returns>true on success</returns>
    [return: MarshalAs(UnmanagedType.Bool)]
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool GetWindowInfo(IntPtr hwnd, ref WINDOWINFO pwi);

    /// <summary>
    /// The MonitorFromWindow function retrieves a handle to the display monitor
    /// that has the largest area of intersection with the bounding rectangle
    /// of a specified window.
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd145064%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="dwFlags">Determines the function's return value
    /// if the window does not intersect any display monitor.</param>
    /// <returns>
    /// Monitor HMONITOR handle on success or based on dwFlags for failure
    /// </returns>
    [DllImport("user32.dll")]
    private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);

    /// <summary>
    /// The GetMonitorInfo function retrieves information about a display monitor
    /// <seealso cref="http://msdn.microsoft.com/en-us/library/dd144901%28VS.85%29.aspx"/>
    /// </summary>
    /// <param name="hMonitor">A handle to the display monitor of interest.</param>
    /// <param name="lpmi">
    /// A pointer to a MONITORINFO structure that receives information
    /// about the specified display monitor.
    /// </param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
    #endregion

    /// <summary>
    /// Window procedure callback.
    /// Hooked to a WPF maximized window works around a WPF bug:
    /// https://connect.microsoft.com/VisualStudio/feedback/details/363288/maximised-wpf-window-not-covering-full-screen?wa=wsignin1.0#tabs
    /// possibly also:
    /// https://connect.microsoft.com/VisualStudio/feedback/details/540394/maximized-window-does-not-cover-working-area-after-screen-setup-change?wa=wsignin1.0
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="msg">The window message.</param>
    /// <param name="wParam">The wParam (word parameter).</param>
    /// <param name="lParam">The lParam (long parameter).</param>
    /// <param name="handled">
    /// if set to <c>true</c> - the message is handled
    /// and should not be processed by other callbacks.
    /// </param>
    /// <returns></returns>
    internal static IntPtr MaximizedSizeFixWindowProc(
        IntPtr hwnd,
        int msg,
        IntPtr wParam,
        IntPtr lParam,
        ref bool handled)
    {
        switch (msg)
        {
            case WM_GETMINMAXINFO:
                // Handle the message and mark it as handled,
                // so other callbacks do not touch it
                WmGetMinMaxInfo(hwnd, lParam);
                handled = true;
                break;
        }
        return (IntPtr)0;
    }

    /// <summary>
    /// Creates and populates the MINMAXINFO structure for a maximized window.
    /// Puts the structure into memory address given by lParam.
    /// Only used to process a WM_GETMINMAXINFO message.
    /// </summary>
    /// <param name="hwnd">The window handle.</param>
    /// <param name="lParam">The lParam.</param>
    private static void WmGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    {
        // Get the MINMAXINFO structure from memory location given by lParam
        MINMAXINFO mmi =
            (MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));

        // Get the monitor that overlaps the window or the nearest
        IntPtr monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        if (monitor != IntPtr.Zero)
        {
            // Get monitor information
            MONITORINFO monitorInfo = new MONITORINFO();
            monitorInfo.Size = Marshal.SizeOf(typeof(MONITORINFO));
            GetMonitorInfo(monitor, ref monitorInfo);

            // The display monitor rectangle.
            // If the monitor is not the primary display monitor,
            // some of the rectangle's coordinates may be negative values
            RECT rcMonitorArea = monitorInfo.Monitor;

            // Get window information
            WINDOWINFO windowInfo = new WINDOWINFO();
            windowInfo.Size = (UInt32)(Marshal.SizeOf(typeof(WINDOWINFO)));
            GetWindowInfo(hwnd, ref windowInfo);
            int borderWidth = (int)windowInfo.WindowBordersWidth;
            int borderHeight = (int)windowInfo.WindowBordersHeight;

            // Set the dimensions of the window in maximized state
            mmi.MaxPosition.X = -borderWidth;
            mmi.MaxPosition.Y = -borderHeight;
            mmi.MaxSize.X =
                rcMonitorArea.Right - rcMonitorArea.Left + 2 * borderWidth;
            mmi.MaxSize.Y =
                rcMonitorArea.Bottom - rcMonitorArea.Top + 2 * borderHeight;

            // Set minimum and maximum size
            // to the size of the window in maximized state
            mmi.MinTrackSize.X = mmi.MaxSize.X;
            mmi.MinTrackSize.Y = mmi.MaxSize.Y;
            mmi.MaxTrackSize.X = mmi.MaxSize.X;
            mmi.MaxTrackSize.Y = mmi.MaxSize.Y;
        }

        // Copy the structure to memory location specified by lParam.
        // This concludes processing of WM_GETMINMAXINFO.
        Marshal.StructureToPtr(mmi, lParam, true);
    }
}

(Source code at dropbox.)

Tagged , , ,

Cross-browser keyboard shortcuts

Alt+D – aDress bar
Ctrl+E – sEarch box
Ctrl+D – aDd bookmark
Ctrl+Enter – in the address bar – surrounds whatever is there with “http://www.” and “.com” and navigates to the address, so if you just enter, say – bing and hit Ctrl+Enter – it navigates to http://www.bing.com
It is a wierd letter selection, especially with how easy to mix it is with Alt/Ctrl+D, but they work on IE, Firefox, Chrome and Opera

Standards not maintained in implementations of standards

I just read something on Scoble’s blog which has drawn my attention. It was a response to Joel Spolsky complaining about online calendars missing obvious (for some?) functionality.
How come all these (new?) applications are missing so much of the things, that should be an obvious thing? I have seen that many times. Someone makes a new implementation of some known piece of software – be it a calendar, a word processor or a simple text box control and while it sometimes promises to be innovative in some way – it doesn’t do some basic things other implementations do. There are probably many reasons why we see that:
it might be just a simple implementation, that is not meant to do everything the other things do,
or maybe it is planned to be expanded to include that functionality in the future,
or maybe the developers behind it were not aware that behavior is a standard,
or maybe it is not a standard after all, though you use it every day?
 
What can I as a developer do something about it? I could probably write a word processor myself, but how long would it make me to catch up with MS Word? Maybe I could work on some open source implementation, but they seem to be so messed up, that I might end up rewriting it anyways. Maybe I could get Microsoft to hire me to let me work on the product? Seems like a good idea. For now though – I might try to contribute to common knowledge of standard applications and write about them. List what they should do. Maybe no one will listen, but maybe someone will notice my effort or even make some use of it?
 
If I do not want to fail – maybe I should start with something simple? Probably some research would be useful. Microsoft sure has to have some guidelines for user interfaces! And so here it is – User Interface Design and Development. There even is a book they offer. Maybe I will get it some day.
 
As for today – I want to start with something that looks most simple, but has so much within… a text box control! Let’s see if I can add more details to that Wikipedia entry

MS and UIs of 2006.

I am quite excited. There are at least two interesting products that can add something fresh to the GUIs this year. Windows Vista and Office 12. Vista promises to revolutionize the way we will develop UIs, with all new APIs and graphic accelerators support. New Office will bring some completely fresh concepts in controling an application. Many people are looking forward to it, although many are scepticals. Myself – I trust the Microsoft’s research effort.
Vista is supposed to bring fewer changes than it seemed at first and some are unhappy about it. I think the black Glass UI is nice and does not distract user’s attention from the work space, although probably the default theme will be more vivid, or will be easily customizable to satisfy the tastes of the color hungry. The fonts themselves will probably make it a real beautie and some will probably clutter their sidebar with plenty of gadgets. Myself – I will rather stick to my two dozens of shortcuts in the quicklaunch area.
Office 12 will bring the “ribbon”. I think it might become a new standard of interaction styles bringing synergy of toolbars and menus. And even more direct manipulation with the “floatie”. The world seems to start taking full advantage of all GUI concepts at last. It is becoming harder to come up with new and original functionality, so usability and user experience are the things that make a difference.
The recent most important UI breakthroughs for me? Skype with its well designed context menus, Google Earth – the first in its kind and finally AJAX, which will make the web look more like GUI. It is a completely new thing, which shouldn’t be treated as a classical web interface (hence the Web 2.0 term) and rather tries to immitate GUIs. Personally – I would rather make some use of the shdocvw.dll and build these things using a client side program rather than a web browser, which works faster and enables better and more flexible interaction. Obviously some people find it harder to design, although with .NET Windows Forms it is actually easier. Although you limit yourself to a few latest versions of Windows – you don’t have to rely on the browsers compatibility. Maybe with Vista shipped – we will see more interesting attempts here.
 
Maybe my interests in these areas will finally find some use? We’ll see.
 
I think it is time to make another UI.
 

Meta coding? Macros and regular expressions.

I have just read a short article on CodeProject about using regular expressions in coding (

http://www.codeproject.com/article.asp?tag=38295622841838755) and decided to comment on that.

In situations as in that article and especially when converting a table or list of values into a sequence of assignments, calls, declarations etc – I prefer to use macros. It is easier to record and play a macro that does something, than write a regular expression for the same thing. Sometimes only one of the two options is possible so I value them both highly, as they enable to really streamline code generation. Aside from that – it is not something they teach at those programming courses at the university (at least they did not on mine) – so it looks so pro… :]

In this example, using VS2005 I would record the macro as following…

Piece of initial text
workstation id=LEBOWSKI;packet size=4096;user id=sa;data source=LEBOWSKI;persist security info=True;initial catalog=TheInternet;password=protected

The sequence of actions to record the macro:
0. Put the cursor at the beginning of the text
1. Ctrl+Shift+R (starts recording)
2. Type: options.Add(“
3. Open a find window (Ctrl+F) and look for character ‘=’
4. Type: “, “
5. Press: Ctrl+Right Arrow (jumps to the end of the word)
6. Type: “);
7. Press Enter for new line
8. Press Delete to remove the semicolon
9. Ctrl+Shift+R to end recording

Now while holding Ctrl+Shift press P a couple of times and you’ve got your output text with sequence of function calls.
You can even see the actual VB code (Alt+F11) for the macro and edit it if you want to get something more custom, but that probably does not happen very often – at least I have not used that yet.
The key settings for macros might be different in your environment, so you might need to check them or set them up.

Using a similar technique I often generate properties in C# – just starting with a list of a few dozen words – you just have to change one word into a property, remembering to start with the caret at the beginning of the first word and ending with the caret at the beginning of the second word and using clipboard to put the word in the source of the property a few times.

That is most useful when you want to read some data from a file – e.g. an XML one. You get a list of tag names in your source code, record and use one macro to generate the properties with the same names as XML element names to store the values and then another macro to retrieve values from the XML file and assign them to these properties.
You can create hundreds of lines of tedious code in a matter of seconds. Real robust.

The reason why I prefer macros when possible is that you do not have to remember all the special characters and the syntax used in search and replace strings, which is a problem if you only need them once in a while – especially, that they differ among various editors – they even differ between VS and .NET System.Text.RegularExpressions namespace.

The macro code sample below can be used to transform a list of names separated by a new line only into a sequence of properties of a C# class, nicely enclosed in #region outlining tag. Just open the macro editor and replace the code of the RecordingModule with the one below.

Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

Public Module RecordingModule

    Sub TemporaryMacro()
        DTE.ActiveDocument.Selection.EndOfLine(True)
        DTE.ActiveDocument.Selection.Copy()
        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
        DTE.ActiveDocument.Selection.Text = "#region "
        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
        DTE.ActiveDocument.Selection.Indent(2)
        DTE.ActiveDocument.Selection.EndOfLine()
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "#endregion"
        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
        DTE.ActiveDocument.Selection.Indent(2)
        DTE.ActiveDocument.Selection.LineUp()
        DTE.ActiveDocument.Selection.EndOfLine()
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "private string m_"
        DTE.ActiveDocument.Selection.Paste()
        DTE.ActiveDocument.Selection.Text = ";"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "public string "
        DTE.ActiveDocument.Selection.Paste()
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "{"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "get"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "{"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "return m_"
        DTE.ActiveDocument.Selection.Paste()
        DTE.ActiveDocument.Selection.Text = ";"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "}"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "set"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "{"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "m_"
        DTE.ActiveDocument.Selection.Paste()
        DTE.ActiveDocument.Selection.Text = " = value;"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "}"
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.Text = "}"
        DTE.ActiveDocument.Selection.LineDown()
        DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
        DTE.ActiveDocument.Selection.LineDown()
        DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText)
        DTE.ActiveDocument.Selection.NewLine()
        DTE.ActiveDocument.Selection.DeleteLeft(2)
    End Sub
End Module