Tag Archives: winforms

Optimising Winforms Dropdown list combos for touch

Standard

image

The optimisation of the iPhone and the iPad for touch has contributed to it’s success. 

One example of this kind of optimisation is “drop down combos”.  Rather than sticking with the traditional touch UI of a big combo with a fat scrollbar (that would mess up web pages), they do something completely different.  As shown above a neat “roller” control is shown which makes combos a pleasure to use.

On the iPad the experience is more like a traditional combo, but it is not the same.

In the creation of Windows 7 Tablet user interfaces I am sure our users would prefer this kind of experience.

In Winforms it turns out that it’s possible to improve the touch experience markedly using the “OwnerDrawVariable” style on comboboxes.

As you can see, when the combo box looks relatively normal, not taking up much screen real estate….

image

Yet when it’s clicked, the control becomes bigger, so the normal flick scrolling features of Windows 7 tablet can work better.

image

The following code sample is a class called TouchCombo that can be used to cause combos to take on the new style.  Here’s the code for the sample form:

Public Class Form1
    Private Sub mPopulate(ByVal combo As ComboBox)
        combo.Items.Add("Alpha")
        combo.Items.Add("Beta")
        combo.Items.Add("Gamma")
        combo.Items.Add("Delta")
        combo.Items.Add("Epsilon")
        combo.SelectedIndex = 0

    End Sub

    Private moTouchCombo As New TouchCombo
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        mPopulate(ComboBox1)
        mPopulate(ComboBox2)
        moTouchCombo.Connect(ComboBox2)
    End Sub
End Class

 

Here’s the code for the TouchCombo class:

 

Public Class TouchCombo

    Public Sub Connect(ByVal combo As ComboBox)
        combo.DrawMode = DrawMode.OwnerDrawVariable
        combo.DropDownHeight = 200
        combo.DropDownWidth = combo.Width * 1.5
        AddHandler combo.MeasureItem, AddressOf mMeasureItem
        AddHandler combo.DrawItem, AddressOf mDrawItem
        AddHandler combo.DropDown, AddressOf mDropDown
        AddHandler combo.DropDownClosed, AddressOf mDropDownClosed
        AddHandler combo.Disposed, AddressOf mDisposed
    End Sub

    Private Sub mDrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs)

        Dim loCombo As ComboBox = sender

        Using loBrush = New System.Drawing.SolidBrush(e.ForeColor)

            ' Draw the normal Background
            e.DrawBackground()

            ' If it's not dropped down, make it look normal
            If Not loCombo.DroppedDown OrElse e.State = DrawItemState.ComboBoxEdit OrElse e.State = DrawItemState.Default Then
                e.Graphics.DrawString(loCombo.Items(e.Index), loCombo.Font, loBrush, e.Bounds.X, e.Bounds.Y)
            Else
                ' Otherwise draw it big
                Using loFont As New System.Drawing.Font("Arial", 15, FontStyle.Bold)
                    e.Graphics.DrawString(loCombo.Items(e.Index), loFont, loBrush, e.Bounds.X, e.Bounds.Y)
                End Using
            End If

            e.DrawFocusRectangle()

        End Using

    End Sub
    Private Sub mMeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs)
        e.ItemHeight = 35
    End Sub
    Private Sub mDropDown(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        loCombo.ItemHeight = 35
    End Sub

    Private Sub mDropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        loCombo.ItemHeight = 15
    End Sub

    Private Sub mDisposed(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        RemoveHandler loCombo.MeasureItem, AddressOf mMeasureItem
        RemoveHandler loCombo.DrawItem, AddressOf mDrawItem
        RemoveHandler loCombo.DropDown, AddressOf mDropDown
        RemoveHandler loCombo.DropDownClosed, AddressOf mDropDownClosed
        RemoveHandler loCombo.Disposed, AddressOf mDisposed
    End Sub
End Class

 

Download the project.

Advertisements

Using SetParent with a Winforms Exe not working

Standard

violence

Not a thrilling philosophical treatise on user experience this week folks.  Instead a problem that frustrated me for the last hour…

The SetParent api allows a windows developer to use Win32 host the window from one process into another process.

This code:

Declare Auto Function SetParent Lib "user32" (ByVal hWndChild As IntPtr, ByVal hWndNewParent As IntPtr) As IntPtr

Dim loProcess As Process
loProcess = Process.Start("path to a winforms executable")
loProcess.WaitForInputIdle()
loProcess.Refresh()
SetParent(loProcessInfo.MainWindowHandle, Me.Handle)

…works fine for notepad.exe, but wasn’t working at all for a Winforms application I was executing.

For some reason, MainWindowHandle of the process and always returned zero.  Very frustrating.

It turns out that the “ShowInTaskbar” property on the main form of the process you are running must be set = true for this to work.

So there you go.

For a simple overview of the technique, take a look at Hosting Exe Applications in a Winform Project.

Hosting Winforms in Firefox

Standard

 image

I have a strange fascination with hybrids.  Combining old and new technologies in bizarre and interesting ways.   Why must developers make “the choice” between a web or native technology? It would be great to build applications that would increase or decrease in features depending on the platform they were run on.

 

clip_image002

Do you have a Winforms application that you’d like to host in a web browser?  There’s an interesting technique you can use to do this enabled by .net Framework 3.5.

The .net framework provides the ability to create WPF web applications (commonly called XBAP).  WPF has the ability, in turn, to host Winforms. 

Security

It appears that in order to do this effectively, it’s necessary to deploy your XBAP application as a “Full Trust” application.

image

The code

To use this, create an XBAP project, then a page a “WinformHost” control on it.   The XAML could look like this:

<Page x:Class="Page1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Page1">
    <DockPanel LastChildFill="True">
        <Button Name="Button1"  DockPanel.Dock="Bottom"  >This is a WPF Button</Button>
        <WindowsFormsHost Name="WinformHost"   />
    </DockPanel>
</Page>

Then, create some code to instantiate your winform and show it on the Winform host:

Class Page1

    Private Sub Page1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim loForm As New HelloWorldWinform

        WinformHost.Child = WinformToUserControl.GetWrapper(loForm)
        loForm.Show()

    End Sub
End Class

Here’s the subroutine:

Public NotInheritable Class WinformToUserControl

    Public Shared Function GetWrapper(ByVal form As System.Windows.Forms.Form) As System.Windows.Forms.Control
        Dim loPanel As New System.Windows.Forms.Panel
        loPanel.Dock = Forms.DockStyle.Fill
        ShowFormInControl(loPanel, form)
        Return loPanel
    End Function

    Private Shared Sub ShowFormInControl(ByVal ctl As System.Windows.Forms.Control, ByVal frm As System.Windows.Forms.Form)

        frm.TopLevel = False
        frm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
        frm.AutoScaleMode = Forms.AutoScaleMode.Dpi
        ctl.Controls.Add(frm)

        frm.Dock = Forms.DockStyle.Fill

    End Sub
End Class

   

Interesting bits and pieces

We found that in order for the items inside the control to scale properly, the AutoScaleMode property on the Winform must be set to Dpi instead of the default, which is Font.  I have a feeling this is because Dpi is the mode preferred for WPF, but I am uncertain.

In the sample WinformToUserControl class I found it better to contain the Winform inside a panel rather than exposing the Winform directly.  It seemed to make it more stable.  Feel free to experiment with taking it off.

Gotchas

As of time of writing, Firefox on my Windows 7 64 bit machine steadfastly refuses to run xbap.  It appears the “Windows Presentation Foundation” addin has not been automatically installed.  I have no idea how to install it.  There’ appears to be no information about how to do so.  So if you have it, good for you.  If you don’t….try re-installing the .net framework 3.51 (which didn’t work for me…)

I do know there was some controversy about this addin being temporarily blacklisted by Mozilla due to security concerns last month, but that’s all over now isn’t it?  

 

Download

You can download a sample project here.

image

Links

Enabling callto (Skype) in your application

Standard

image image

You may have noticed that Skype seems to embed itself into Internet Explorer, Firefox and Chrome.  It recognises contacts and phone numbers in the page and provides the ability to call them.

I believe people will expect this kind of functionality in conventional windows applications also.  To assist with this I created a little class that will not only allow calls, but allows a check for the existence of a skype and give the ability to extract the calling applications icon.

Sample Project

The sample winforms project includes the CallTo class and a simple Winforms test form:

image

The form has a textbox and button.   One load, the call button is enabled and give an image.  The button makes the call.

 

Public Class Form1

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim loCallTo As New CallTo
        cmdCall.Image = loCallTo.Image
        cmdCall.Enabled = loCallTo.Enabled
    End Sub
    Private Sub cmdCall_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdCall.Click
        Dim loCallTo As New CallTo
        loCallTo.DoCall(txtPhoneNumber.Text)
    End Sub

End Class

 

 

Heres the source of CallTo.vb:

 

Imports Microsoft.Win32
Imports System.Runtime
Imports System.Runtime.InteropServices

''' <summary>
''' This class allows for making callto calls
''' </summary>
Public Class CallTo

    ''' <summary>
    ''' Gets the image.
    ''' </summary>
    ''' <value>The image.</value>
    Public ReadOnly Property Image() As Image
        Get
            If Not Enabled Then Return Nothing

            Dim lsValue As String
            With Registry.ClassesRoot.OpenSubKey("callto\DefaultIcon")
                lsValue = .GetValue("")
            End With

            If lsValue.Contains(",") Then
                Dim lsBits() As String = lsValue.Split(","c)
                Dim lsFilename As String = lsBits(0).Trim(New Char() {""""c})
                Dim liPosition As Integer = Val(lsBits(1))
                Return moGetIconFromExeOrDll(lsFilename, liPosition)
            Else
                Return Nothing
            End If

        End Get
    End Property
    Private Declare Auto Function ExtractIcon Lib "shell32" ( _
ByVal hInstance As IntPtr, ByVal lpszExeFileName As String, _
ByVal nIconIndex As Integer) As IntPtr
    Private Function moGetIconFromExeOrDll(ByVal filename As String, ByVal position As Integer) As Image
        Dim hInstance As IntPtr = Marshal.GetHINSTANCE( _
           System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0))
        Dim hIcon As IntPtr = ExtractIcon(hInstance, filename, position)
        Dim loBitmap As Bitmap = Bitmap.FromHicon(hIcon)
        Dim loReturn As Image = loBitmap.GetThumbnailImage(16, 16, Nothing, Nothing)

        loBitmap.Dispose()
        Return loReturn

    End Function

    ''' <summary>
    ''' Gets a value indicating whether this CallTo is enabled.
    ''' </summar
    ''' <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
    Public ReadOnly Property Enabled() As Boolean
        Get
            Try
                Dim loReg As RegistryKey = Registry.ClassesRoot.OpenSubKey("callto", False)
                loReg.Close()
            Catch
                Return False
            End Try
            Return True
        End Get
    End Property
    ''' <summary>
    ''' Does the call.
    ''' </summary>
    ''' <param name="destination">The destination.</param>
    Public Sub DoCall(ByVal destination As String)

        If Not Enabled Then Throw New ApplicationException("Callto is not enabled")
        Process.Start("callto://" & destination)

    End Sub

End Class

 

Download Sample

Flick Scrolling in Winforms

Standard

image

 

In the article A Simple Scroll Controller for Winforms I mentioned that I was working on a FlickScrolling framework for Winforms.

I finished a version of this code a couple of months ago and it seems to work well.  

The code never went into production because I think that Windows 7 will provide this functionality automatically for winforms applications. 

Nevertheless, some people have showed an interest in what I came up with, so I’m posting it here for your edification.  I hope someone finds it useful.

Usage

To use, simply instantiate the “ApplicationFlickScrolling” class, passing in the datatypes of the container controls you wish to scroll.

For example:

Private Shared moApplicationFlick As New ApplicationFlickScrolling(GetType(Panel))

To switch it on and off, used the “Enabled” property:

moApplicationFlick.Enabled = True

That’s it!

 

The Implementation

image

The code consists of 3 classes:

  • ApplicationFlickScrolling to act as the master controller class
  • ApplicationMouseEvents class for detecting mouse events
  • FlickDetector for detecting simple gestures
  • ScrollController for performing elastic-style easing of scrolling

Improvements

If I’d have continued developing this I would have modified ScrollController to use “proper” easing functions. 

 

 
Note: The usual disclaimers apply for this code, which has been given freely to help others learn and is not intended for usage in your business application or for running a nuclear facility.
Share this post :

Weird Science – Defining Winforms in XAML

Standard

image

I like to think of XAML as an “object instantiation script”.  According the to the main books on the subject it should be possible to utilise XAML to create any kind of .net object. 

Practical examples include WWF (Windows Workflow Foundation) , which allows the storage of definition in XAML, which is nothing to do WPF.  WPF (Windows Presentation Foundation) was the original reason XAML was created, but XAML was designed to be independent.

So I was thinking, why not see if I can define a traditional “Winform” in XAML.    Why would I want to?   I thought it might be fun.   As it turns out, it may actually be useful in some scenarios.  (With a great stretch of the imagination)

So does it work?  Oh yes it works and it works well.   “Well” means that VS2008 provides full intellisense for tags within the Xaml designer, as well as “code behind”.  Now that, my friends, is cool.  Imagine being able to create form layouts using XML tags instead of that oh-so-last year winforms designer!  The result is surprisingly WPF like, which I find intriguing.

To create the example below, I created a WPF project, then removed the default WPF “Window1.xaml” and replaced it with my own “Form1.xaml”:

<Form Name="Form1" x:Class="Form1"  Text="Hello" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        >
    <Form.Controls>
        <FlowLayoutPanel>
            <FlowLayoutPanel.Controls>
                <TextBox Name="txtName" x:Name="txtName" />
                <Button Name="cmdSayHello" Text="Say Hello" Click="Button_Click" /> 
            </FlowLayoutPanel.Controls>
        </FlowLayoutPanel>
    </Form.Controls>
</Form>

Please note that features such as code behind (partial classes), event hooks and most things in the “x” namespace work fine.

Creating XAML from an existing Winform

For even more fun and games, you can feed a running instance of a Winform into XAML Writer and get XAML source.  Sadly winforms incorporates read-only properties, so without massaging the Xml you’re not going to be able to clone a Winform using XAML serialization just yet.

The code would look something like this:

Dim loWinform As Form = Me ' Some Winform
Dim lsWinformXaml As String = XamlWriter.Save(loWinForm)
Dim loClonedForm As Form = XamlReader.Load(new XmlTextReader(new StringReader(xaml)))

 

See this Mike Hillberg’s object cloning article for more information.

 

So where from here?

What’s the use of it?  Well for me it proved that Xaml can be used to create any sort of object graph.  

Possible uses for this:

  • Embedding Winforms instantiation code within a WPF XAML document (something I haven’t tried)
  • Streaming Winforms form definitions from a server down to a client (effectively allowing a WebServer to render Winforms just like it renders HTML pages)
  • Providing a good interim step in creating a Winforms to WPF Converter

 

Any Comments?

If you find this topic interesting, ask questions or download a sample project, write some comments on the blog.  If I can see interest in the topic I may develop it further.

Share this post :

kick it on DotNetKicks.com