Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

Friday, February 05, 2010

Using Caliburn inside of Windows Forms with WPF UserControls/ElementHost

I'm working to have a little MVVM action inside of our application, but it's Windows Forms, so I am using an ElementHost to get the WPF UserControl objects loaded. There are aspects to MVVM that cry out for frameworks to help you (binding double-click events to ListViewItem objects being one important one for me). I tried about a million things to get the very powerful Caliburn WPF application framework to do something--anything--in this hybrid context. It was the last thing I tried that worked! ;)

There seem to be two key things you must do to get Caliburn properly registered and listening in this context:
  1. You have to use the "WithAssemblies" option on the framework configuration call, passing in a reference to the current assembly, along with the "WithPresentationFramework" call.
  2. You must spin up Caliburn before the entry point UserControl is initialized and assigned to the ElementHost. If the UserControl is set as the Child via the Windows Forms designer, this means that you must initialize Caliburn in the Form containing the ElementHost's constructor before its InitializeComponent call.
    public partial class Form1 : Form
    {
        public Form1()
        {
            CaliburnFramework.ConfigureCore()
                .WithAssemblies(System.Reflection.Assembly.GetExecutingAssembly())
                .WithPresentationFramework()
                .Start();
            InitializeComponent(); // must come after the above
        }
    }

I went down several dead-ends before discovering this, such as thinking I needed a non-null System.Windows.Application.Current class, thinking I needed to initialize the framework inside of the UserControl's constructor, etc. I hope this helps someone.

You can download the sample application here.


Monday, September 07, 2009

Updating the UI from a BackgroundWorker in WPF

This approach enabled me to flexibly send messages to objects on the UI thread while processing from a BackgroundWorker object in WPF. Otherwise, the UI thread doesn't update if the BackgroundWorker code tries to manipulate its objects during processing. First I created this UIUpdater:
public delegate void UpdateTextDelegate(string message);

///
/// Sends messages to the UI thread via the dispatcher.
///

class UIUpdater
{
#region ctor
///
/// Initializes a new instance of the class.
///

/// The dispatcher in scope for the UI.
/// Function that will apply the message to the appropriate control(s).
public UIUpdater(Dispatcher uiDispatcher, UpdateTextDelegate uiUpdateDelegate)
{
if (default(Dispatcher) == uiDispatcher) throw new ArgumentNullException("uiDispatcher");
if (default(UpdateTextDelegate) == uiUpdateDelegate) throw new ArgumentNullException("uiUpdateDelegate");

dispatcher = uiDispatcher;
updateTextDelegate = uiUpdateDelegate;
}
#endregion

#region member variables
Dispatcher dispatcher = default(Dispatcher);
UpdateTextDelegate updateTextDelegate = default(UpdateTextDelegate);
#endregion

#region methods
///
/// Sends the message.
///

/// The message.
public void SendMessage(string message)
{
if (default(string) == message) throw new ArgumentNullException("message"); // allow string.Empty
dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(ThreadStart)delegate() {
updateTextDelegate(message);
});
}
#endregion
}

To use it, in the BackgroundWorker_DoWork event handler, initialize it and pass it into your objects that implement the background work. Give it a reference to your dispatcher and a delegate that will handle updates from the background thread:
UIUpdater updater = new UIUpdater(mainWindow.Dispatcher, new UpdateTextDelegate(UpdateStatus));

DoMyWork(updater);
Then from the code doing the background work, you can call the SendMessage delegate to safely pass along the message:
updater.SendMessage("Deleting all items from the " + listName + " list...");

Monday, January 19, 2009

Yet another WPF HyperlinkButton control

I wanted a general purpose implementation of the Silverlight HyperlinkButton for WPF that supported binding things like the font, font weight, etc. so I quickly rolled this puppy. It has its own Click event so clients can subscribe appropriately. I am sure there are some enhancements lurking, but it seems to do the job:

XAML:

<UserControl x:Class="Controls.HyperlinkButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="root"
    >
    <TextBlock x:Name="hyperlinkContentText"
        Text="{Binding HyperlinkContent, ElementName=root}"
        FontSize="{Binding FontSize, ElementName=root, Path=FontSize}"
        FontFamily="{Binding FontFamily, ElementName=root, Path=FontFamily}"
        FontStretch="{Binding FontStretch, ElementName=root, Path=FontStretch}"
        FontStyle="{Binding FontStyle, ElementName=root, Path=FontStyle}"
        FontWeight="{Binding FontWeight, ElementName=root, Path=FontWeight}"
        Foreground="{Binding Foreground, ElementName=root, Path=Foreground}"
        Background="{Binding Background, ElementName=root, Path=Background}"
        Opacity="{Binding Opacity, ElementName=root, Path=Opacity}"
        OpacityMask="{Binding OpacityMask, ElementName=root, Path=OpacityMask}"
        MouseEnter="hyperlinkContentText_MouseEnter"
        MouseLeave="hyperlinkContentText_MouseLeave"
        MouseLeftButtonDown="hyperlinkContentText_MouseLeftButtonDown" />
</UserControl>

Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Controls
{
    /// <summary>
    /// Interaction logic for HyperlinkButton.xaml
    /// </summary>
    public partial class HyperlinkButton : UserControl
    {
        #region dependency properties
        /// <summary>
        /// Dependency property for hyperlink text content.
        /// </summary>
        public static readonly DependencyProperty HyperlinkContentProperty;
        #endregion

        #region routed events
        /// <summary>
        /// Routed event for handling the "button" click.
        /// </summary>
        public static readonly RoutedEvent ClickEvent;
        #endregion

        #region ctor
        #region static
        /// <summary>
        /// Initializes the <see cref="HyperlinkButton"/> class.
        /// </summary>
        static HyperlinkButton()
        {
            FrameworkPropertyMetadata fpm = new FrameworkPropertyMetadata(string.Empty);
            HyperlinkContentProperty = DependencyProperty.Register(
                "HyperlinkContent",
                typeof(string),
                typeof(FrameworkElement),
                fpm);

            ClickEvent = EventManager.RegisterRoutedEvent(
                "Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HyperlinkButton));
        }
        #endregion

        #region instance
        /// <summary>
        /// Initializes a new instance of the <see cref="HyperlinkButton"/> class.
        /// </summary>
        public HyperlinkButton()
        {
            InitializeComponent();
        }
        #endregion
        #endregion

        #region properties
        /// <summary>
        /// Gets or sets the content of the hyperlink.
        /// </summary>
        /// <value>The content of the hyperlink.</value>
        public string HyperlinkContent
        {
            get { return (string)GetValue(HyperlinkContentProperty); }
            set { SetValue(HyperlinkContentProperty, value); }
        }

        /// <summary>
        /// Occurs when [click].
        /// </summary>
        public event RoutedEventHandler Click
        {
            add
            {
                AddHandler(ClickEvent, value);
            }
            remove
            {
                RemoveHandler(ClickEvent, value);
            }
        }
        #endregion

        #region event handlers
        /// <summary>
        /// Handles the MouseEnter event of the hyperlinkContentText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseEnter(object sender, MouseEventArgs e)
        {
            hyperlinkContentText.TextDecorations = TextDecorations.Underline;
            hyperlinkContentText.Cursor = Cursors.Hand;
        }

        /// <summary>
        /// Handles the MouseLeave event of the hyperlinkContentText control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseLeave(object sender, MouseEventArgs e)
        {
            hyperlinkContentText.TextDecorations = null;
            hyperlinkContentText.Cursor = Cursors.Arrow;
        }

        /// <summary>
        /// Handles the MouseLeftButtonDown event of the hyperlinkContentText control. Maps it to the click
        /// event of this control and suppresses it after raising Click.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
        private void hyperlinkContentText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            RaiseEvent(new RoutedEventArgs(ClickEvent, this));
        }
        #endregion
    }
}