WPF FAQ - Annotations

How can I create an application that is used to declare a Context Menu with routed commands that the users can access to create and manage annotations ?

Users typically create annotations by first selecting some text or an item of interest and then right-clicking to display a context menu of annotation options.

The following example shows the XAML you can use to declare a Context Menu with routed commands that users can access to create and manage annotations.

[XAML]

<DocumentViewer.ContextMenu>
  <ContextMenu>
    <MenuItem Command='ApplicationCommands.Copy' />
    <Separator />
    <!-- Add a Highlight annotation to a user selection. -->
    <MenuItem Command='ann:AnnotationService.CreateHighlightCommand'
              Header='Add Highlight' />
    <!-- Add a Text Note annotation to a user selection. -->
    <MenuItem Command='ann:AnnotationService.CreateTextStickyNoteCommand'
              Header='Add Text Note' />
    <!-- Add an Ink Note annotation to a user selection. -->
    <MenuItem Command='ann:AnnotationService.CreateInkStickyNoteCommand'
              Header='Add Ink Note' />
    <Separator />
    <!-- Remove Highlights from a user selection. -->
    <MenuItem Command='ann:AnnotationService.ClearHighlightsCommand'
              Header='Remove Highlights' />
    <!-- Remove Text Notes and Ink Notes from a user selection. -->
    <MenuItem Command='ann:AnnotationService.DeleteStickyNotesCommand'
              Header='Remove Notes' />
    <!-- Remove Highlights, Text Notes, Ink Notes from a selection. -->
    <MenuItem Command='ann:AnnotationService.DeleteAnnotationsCommand'
              Header='Remove Highlights & Notes' />
  </ContextMenu>
</DocumentViewer.ContextMenu>
Permalink
How can I Match Annotations with Annotated Objects ?

You can match annotations with the corresponding annotated objects. For example, consider a simple document reader application that has a comments pane. The comments pane might be a listbox that displays the text from a list of annotations that are anchored to a document. If the user selects an item in the listbox, then the application brings into view the paragraph in the document that the corresponding annotation object is anchored to.

The following piece of C # code demonstrates how to implement the event handler of such a listbox that serves as the comments pane.

[C#]

void annotationsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{

    Annotation comment = (sender as ListBox).SelectedItem as Annotation;
    if (comment != null)
    {
        // IAnchorInfo info;
        // service is an AnnotationService object
        // comment is an Annotation object
        info = AnnotationHelper.GetAnchorInfo(this.service, comment);
        TextAnchor resolvedAnchor = info.ResolvedAnchor as TextAnchor;
        TextPointer textPointer = (TextPointer)resolvedAnchor.BoundingStart;
        textPointer.Paragraph.BringIntoView();
    }
}
Permalink
How do I rearrange the columns in a DataGrid ?

The columns appear in the order in which their column styles were added to the tablestyle, being used by the grid. If you want to change this order, you need to create a new table style and add the column styles in the order you want things to appear.

Here are some code snippets that suggest how to do this.

[C#]

public void MoveColumn(DataGrid _dataGrid, string _mappingName, int fromCol, int toCol)
{
if(fromCol == toCol) return;
DataGridTableStyle oldTS = _dataGrid.TableStyles[_mappingName];
DataGridTableStyle newTS = new DataGridTableStyle();
newTS.MappingName = _mappingName;
for(int i = 0; i < oldTS.GridColumnStyles.Count; ++i)
{
if(i != fromCol && fromCol < toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);
if(i == toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[fromCol]);
if(i != fromCol && fromCol > toCol)
newTS.GridColumnStyles.Add(oldTS.GridColumnStyles[i]);
}
_dataGrid.TableStyles.Remove(oldTS);
_dataGrid.TableStyles.Add(newTS);
}
Sample usage
private void button1_Click(object sender, System.EventArgs e)
{
MoveColumn( myDataGrid, 'Customers', 3, 1 );
}
Permalink
How do I override the DataGridViewTextBox.Clone method ? In general, how do I implement ICloneable in a derived class ?

When the DataGridView needs to create a new cell in the column the method DataGridViewTextBoxCell.Clone is called in order to duplicate the ’DataGridViewColumn.CellTemplate’. It’s perfectly logical.

You can override the Clone method, but you can’t call base.Clone() since it returns an object of the base class type and you can’t code a copy constructor because there is no copy constructor for DataGridViewTextBoxCell.

With a base copy constructor you could code a Clone method like this :

[C#]

public override object Clone()
{
   return new MyDataGridViewEditingTextBoxCell(this);
}
With your overridden copy constructor you could code a Clone method like this :
public CustomDataGridViewTextBoxCell( CustomDataGridViewTextBoxCell cell)
  : base( cell )
{
   // Copy data of CustomDataGridViewTextBoxCell
   this.Flag = new Flag( cell.Flag );
   this.Data = cell.Data.Clone() as Data;
   // ...
}
Permalink
How do I reduce flickering when resizing a transparent Panel whose parent has a gradient background ?

Make sure to set these flags for your panel, for e.g. in it’s constructor. You will have to derive a class from Panel if you were initially using the Panel directly.

[C#]

base.SetStyle( ControlStyles.DoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.UserPaint, true );
Permalink