Archive

Posts Tagged ‘WPF’

Declaring Custom WPF Commands via MarkupExtensions

April 25th, 2009

And yet another markup extension that hopefully makes things a little easier for you. Basically, it’s the same pattern that the good doctor described here for value converters, but with custom commands in mind.

Without Extension

Basically, this extension makes your commands immediately available without having to declare them. One "traditional" way to declare a command is directly in XAML:

<Window.Resources>

  <cmd:HelloWorldCommand x:Key="SayHello" />

</Window.Resources>

 

…and then apply it like this:

<Button Command="{StaticResource SayHello}" />

 

Using the Markup Extension

The declaration part above can become somewhat tedious, especially if you have quite a few commands (e.g. because you are using a ribbon with lots of buttons on it). Thanks to using a markup extension, you can skip the declaration part altogether and just declare your command.

<Button Command="{cmd:HelloWorldCommand}" />

 

Implementation and Sample

 

/// <summary>
/// Basic implementation of the <see cref="ICommand"/>
/// interface, which is also accessible as a markup
/// extension.
/// </summary>
public abstract class CommandExtension<T> : MarkupExtension, ICommand
    where T:class, ICommand, new()
{
  /// <summary>
  /// A singleton instance.
  /// </summary>
  private static T command;


  /// <summary>
  /// Gets a shared command instance.
  /// </summary>
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    if (command == null) command = new T();
    return command;
  }


  /// <summary>
  /// Fires when changes occur that affect whether
  /// or not the command should execute.
  /// </summary>
  public event EventHandler CanExecuteChanged
  {
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
  }
  

  /// <summary>
  /// Defines the method to be called when the command is invoked.
  /// </summary>
  /// <param name="parameter">Data used by the command.
  /// If the command does not require data to be passed,
  /// this object can be set to null.
  /// </param>
  public abstract void Execute(object parameter);


  /// <summary>
  /// Defines the method that determines whether the command
  /// can execute in its current state.
  /// </summary>
  /// <returns>
  /// This default implementation always returns true.
  /// </returns>
  /// <param name="parameter">Data used by the command.  
  /// If the command does not require data to be passed,
  /// this object can be set to null.
  /// </param>
  public virtual bool CanExecute(object parameter)
  {
    return true;
  }
}

 

…and here’s a sample implementation:

public class HelloWorldCommand : CommandExtension<HelloWorldCommand>
{
  public override void Execute(object parameter)
  {
    MessageBox.Show("Hello world.");
  }
}

 

Of course, nothing prevents you from pimping the base class a little to your needs 🙂

Author: Categories: WPF Tags: ,

WPF Ribbon: RibbonCommands Can Cause Memory Leaks

April 15th, 2009

I stumbled over an issue when dealing with Microsoft’s WPF ribbon today. Apparently, the control that heavily relies on RibbonCommands rather than arbitrary ICommand instances may cause severe memory leaks. Have a look at this simple Window:

 

<Window ...>
  <Grid>
    <Grid.Resources>
      <r:RibbonCommand
        x:Key="MyCommand"
        Executed="OnRibbonClicked"
        LabelTitle="Click Me" />
    </Grid.Resources>

    <!-- a ribbon that only displays a command in the quick access toolbar -->
    <r:Ribbon>
      <r:Ribbon.QuickAccessToolBar>
        <r:RibbonQuickAccessToolBar>
          <r:RibbonButton
            Command="{StaticResource MyCommand}"
            r:RibbonQuickAccessToolBar.Placement="InToolBar" />
        </r:RibbonQuickAccessToolBar>
      </r:Ribbon.QuickAccessToolBar>
    </r:Ribbon>
  </Grid>
</Window>

 

…and here’s the event listener that was declared for the RibbonCommand:

private void OnRibbonClicked(object sender, ExecutedRoutedEventArgs e)
{
  MessageBox.Show("Ribbon command executing.");
}

 

Once opened and closed, this window will not be garbage collected until the application shuts down. The reason is the OnRibbonClicked event handler that doesn’t get deregistered.

There are signs that Microsoft will dump RibbonCommand completely once the ribbon goes live. However: This probably won’t happen too fast, so you will have to solve the issue on your own. On the bright side: It forces a cleaner design on you.

I prefer custom commands anyway, so the natural way to go was to simply implement my own class that derives from RibbonCommand:

 

///<summary>
/// Base class for custom <see cref="RibbonCommand"/>
/// implementations.
///</summary>
public abstract class RibbonCommandBase : RibbonCommand
{
  /// <summary>
  /// Creates the command and registers weak event listeners.
  /// </summary>
  protected RibbonCommandBase()
  {
    Executed += OnExecute;
    CanExecute += OnCanExecute;
  }


  /// <summary>
  /// Determines whether the command can be executed or not.
  /// The default implementation always allows that.
  /// </summary>
  protected virtual void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
    e.CanExecute = true;
  }


  /// <summary>
  /// Command implementation - executes command logic.
  /// </summary>
  protected abstract void OnExecute(object sender, ExecutedRoutedEventArgs e);
}

 

The noteworthy thing here is that this abstract base class registers event listeners for its own events – one virtual, one abstract. Accordingly, a deriving command implementation only has to provide its own OnExecute and OnCanExecute (optional) methods to get going:

 

public class MessageCommand : RibbonCommandBase
{
  protected override void OnExecute(object sender, ExecutedRoutedEventArgs e)
  {
    MessageBox.Show("Ribbon command executing.");
  }
}

 

The last thing to correct is the command declaration in XAML – rather than declaring a RibbonCommand with a code-behind event listener, you declare your custom command (MessageCommand in the sample below). As you can see, there is no longer a listener for the Executed event – the command takes care of this on its own. Accordingly, this command can be declared in a resource dictionary without a code-behind file.

<!-- Custom command - can be declared in a resource dictionary -->
<cmd:MessageCommand x:Key="MyCommand"
                    LabelTitle="Click Me"
                    SmallImageSource="About.png"
/>

 

Sample Project

The attached sample project shows the two variants and the missing garbage collection of the standard ribbon commands.

I wasn’t sure about licensing restrictions, therefore I did not include the WPF ribbon assembly in the downloadable sample – you’ll have to add the reference yourself before compiling. Sorry for the inconvenience :/

Download: ribbon-commands.zip

 

 image

 

kick it on DotNetKicks.com

Author: Categories: WPF, WPF Controls Tags: ,

WPF Behavior Samples

April 13th, 2009

Behaviors in WPF and Silverlight are one of the features that were introduced with Blend 3, and unlike attached behaviors, they benefit from full designer support in Blend 3, thus providing a much higher level of accessibility to developers and designers alike.

Laurent Bugnion has posted a great tutorial where he walks you through the various aspects of a magnifying glass behavior, and Jeremiah Morrill generates eye candy through a behavior that adds glass effects to your visuals.

Last but not least, the Expression team created a new section on the Expression Gallery that is dedicated to behaviors. I sure like shiny new toys 😉

Author: Categories: WPF Tags:

WPF NotifyIcon – Release Candidate

April 1st, 2009

This is an implementation of a NotifyIcon (aka system tray icon or taskbar icon) for the WPF platform.

image

Update:

The control has now its own project page. Please go to

http://www.hardcodet.net/projects/wpf-notifyicon

Moving WPF DataGrid Rows using Drag and Drop

March 24th, 2009

For my upcoming NetDrives tool (will be released shortly) I wanted to enable the user to reorder managed network shares using drag and drop using a preview of the dragged row:

image

 

As it turned out, it’s not too hard to implement, but it took my a while to find all pieces of the puzzle, so I compiled a short sample. You can find the sample link at the end of the article.

 

Drag Indicator Popup

I used a popup as an drag indicator, which I bound to the item that was currently dragged (DraggedItem dependency property):

<!-- the popup that is displayed if user moves rows -->
<Popup
  x:Name="popup1"
  IsHitTestVisible="False"
  Placement="RelativePoint"
  PlacementTarget="{Binding ElementName=me}"
  AllowsTransparency="True">
  <Border
    BorderBrush="{DynamicResource CellBorderBrush}"
    BorderThickness="2"
    Background="White"
    Opacity="0.75">
    <StackPanel
      Orientation="Horizontal"
      Margin="4,3,8,3">
      <Image
        Source="/Shared/Images/DragInsert.png"
        Width="16"
        Height="16" />
      <TextBlock
        Style="{DynamicResource DefaultLabel}"
        FontWeight="Bold"
        VerticalAlignment="Center"
        Text="{Binding ElementName=me, Path=DraggedItem.Name}"
        Margin="8,0,0,0" />
    </StackPanel>
  </Border>
</Popup>

 

Disabling Drag and Drop in Edit Mode

I didn’t want to enable drag and drop if the grid was in edit mode. Accordingly, I registered two event listeners on the grid:

<dg:DataGrid
  BeginningEdit="OnBeginEdit"
  CellEditEnding="OnEndEdit"
  .. />

 

The corresponding event listeners just set the IsEditing flag, which is evaluated when handling mouse events:

/// <summary>
/// State flag which indicates whether the grid is in edit
/// mode or not.
/// </summary>
public bool IsEditing { get; set; }

private void OnBeginEdit(object sender, DataGridBeginningEditEventArgs e)
{
  IsEditing = true;
  //in case we are in the middle of a drag/drop operation, cancel it...
  if (IsDragging) ResetDragDrop();
}

private void OnEndEdit(object sender, DataGridCellEditEndingEventArgs e)
{
  IsEditing = false;
}

 

Listening to Mouse Events

In order to display and move the popup with the mouse, I registered listeners for the following three mouse events:

  • PreviewMouseLeftButtonDown (on the datagrid)
  • MouseLeftButtonUp (directly on the layout root)
  • MouseMove (directly on the layout root)

 

Note: I started with listeners on the grid only, which caused some side effects. Apparently, the datagrid (current March release) not always fires the mouse events properly. This caused choppy animations when hovering over certain cells. Fortunately, this is not an issue with the MouseMove event of the layout root.

 

Starting Drag and Drop

DnD is started as soon as the user presses the left mouse button on the datagrid. I had to use the PreviewLeftMouseButton event in order to get the notification, and I needed to determine the clicked row based on the mouse position. I blogged about finding an element under the mouse a while ago here, but the UIHelpers class is part of the sample project here.

My mouse button event listener basically does the following:

  • Check if the mouse is being placed over a grid row.
  • Set the IsDragging flag to true.
  • Store the dragged item in the DraggedItem dependency property (used by the popup to display the name).

 

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
  //exit if in edit mode
  if (IsEditing) return;

  //find the clicked row
  var row = UIHelpers.TryFindFromPoint<DataGridRow>((UIElement) sender,
                                                    e.GetPosition(shareGrid));
  if (row == null) return;

  //set flag that indicates we're capturing mouse movements
  IsDragging = true;
  DraggedItem = (IShareConfiguration) row.Item;
}

 

Moving the Popup

I registered a listener for the MouseMove event directly on the layout root (not on the datagrid). Basically, the event listener just moves the popup to the current mouse location along with a few minor tasks:

  • If the popup has not been opened yet, display it.
  • Set the grid to read-only.
  • Reposition the popup by setting the PlacementRectangle property.
  • Make sure the grid row under the mouse is being selected. Once again, this didn’t work reliably if I relied on the datagrid to do it by itself.

 

/// <summary>
/// Completes a drag/drop operation.
/// </summary>
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  if (!IsDragging || IsEditing)
  {
    return;
  }

  //get the target item
  ShareConfiguration targetItem = (ShareConfiguration) shareGrid.SelectedItem;

  if (targetItem == null || !ReferenceEquals(DraggedItem, targetItem))
  {
    //remove the source from the list
    ShareList.Remove(DraggedItem);

    //get target index
    var targetIndex = ShareList.IndexOf(targetItem);

    //move source at the target's location
    ShareList.Insert(targetIndex, DraggedItem);

    //select the dropped item
    shareGrid.SelectedItem = DraggedItem;
  }

  //reset
  ResetDragDrop();
}

 

Finishing Drag and Drop

Once the user releases the mouse button, I need to perform the actual drop operation. I already had the dragged item (DraggedItem property, was set when the operation started) so all I needed was the drop target. My target is the currently selected row.

/// <summary>
/// Completes a drag/drop operation.
/// </summary>
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
  if (!IsDragging || IsEditing || shareGrid.SelectedItem == null)
  {
    return;
  }
  
  //get the target item
  IShareConfiguration targetItem = (IShareConfiguration)shareGrid.SelectedItem;

  if (!ReferenceEquals(DraggedItem, targetItem))
  {
    //the actual business logic that works on source and target
  }

  //reset
  ResetDragDrop();
}

 

You might also want to check whether the mouse is currently over the grid (or a grid row) or not – in that case just use the TryFindFromPoint method from the UIHelpers class.

 

Cleaning Up

The ResetDragDrop method just performs a cleanup of the code by closing the popup and adjusting a few properties:

/// <summary>
/// Closes the popup and resets the
/// grid to read-enabled mode.
/// </summary>
private void ResetDragDrop()
{
  IsDragging = false;
  popup1.IsOpen = false;
  shareGrid.IsReadOnly = false;      
}

 

Download Sample: http://blog.hardcodet.net/wp-content/uploads/2009/03/datagrid_dragdrop.zip

Author: Categories: DataGrid Tags: ,

Format WPF Bindings With Resource File Lookups

March 21st, 2009

This post explains a pattern that combines Data Binding, the new StringFormat property and resource lookups through the application of a custom MarkupExtension.

 

StringFormat

Microsoft introduced the StringFormat property with SP1 for .NET 3.5. StringFormat often eliminates the need for a custom converter if you just need to format a bound value.
Here’s a very simple sample that displays hello xxx on the screen (where “xxx” is the value entered in a TextBox control:

 

<TextBox
    x:Name="name"
    Text="world" />

<TextBlock
    Text="{Binding ElementName=name,
                   Path=Text,
                   StringFormat=hello {0}}" />

 

Resource Files

I often use resource files to store strings – not only for localization tasks but also in order to maintain strings that are intended for users at a central place. My resource strings often contain placeholders which are populated at runtime using String.Format:

 

image

 

//use resource file to create message
var msg = Resources.Ask_Whether_To_Connect_To_New_Share;
msg = String.Format(msg, configuration.Name);

//display dialog
MessageBoxResult doConnect = Dialogs.ShowYesNo(msg);

 

 

Bringing Bindings and Resource Files Together

In the sample above, I accessed the resource file programmatically. In XAML however, I prefer a declarative approach. In order to get there, I implemented a simple MarkupExtension that allows me to do just that:

 

<!-- Display a formatted text --> <TextBlock x:Name="Title" Text="{Binding Path=Name, StringFormat={ext:Resource Ask_Whether_To_Connect_To_New_Share}}"

/>

 

The above snippet results in a formatted text being displayed:

image

 

Resource MarkupExtension

Here’s the markup extension. As you can see, it’s fairly trivial. Do note that the reference to the application’s Resources file is hardcoded – you will have to set a using statement or adjust that line in order to use your custom resource file.

 

/// <summary>
/// A markup extension that provides simple access to a given
/// entry in the application's <see cref="Resources"/> file.
/// </summary>
public class Resource : MarkupExtension
{
  /// <summary>
  /// The resource key to be used for the lookup.
  /// </summary>
  public string ResourceKey { get; set; }


  /// <summary>
  /// Inits the <see cref="Resource"/> markup extension
  /// with the key to be assigned.
  /// </summary>
  /// <param name="resourceKey">The resource key to be assigned.</param>
  public Resource(string resourceKey)
  {
    ResourceKey = resourceKey;
  }


  /// <summary>
  /// Performs a lookup for the defined <see cref="ResourceKey"/>.
  /// </summary>
  /// <returns>
  /// The value of the resource that is specified by the
  /// <see cref="ResourceKey"/> property. If the property is not
  /// set, a null reference is returned.
  /// </returns>
  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    if (String.IsNullOrEmpty(ResourceKey)) return null;
    return Resources.ResourceManager.GetObject(ResourceKey);
  }
}

 

Enjoy 🙂

Author: Categories: WPF Tags: ,