Search code examples
wpfserializationgridviewattached-properties

WPF: How to get name or other unique identifier of calling object in event handler for serialization?


I have a GridView and want to serialize column widths across sessions. My idea of how to accomplish this is to attach a behavior to the GridViewColumns in such a way that each time the width of a column is changed the attached event handler is called and stores the new width. This already works well.

The only remaining problem:

How do I know in the event handler which GridViewColumn sent the event? I obviously need to know that in order to be able to store the width and later set the width on the correct column when restoring. Ideally I would like to use the name specified in XAML as column identifier.

Here is my code. XAML:

<GridView>
  <GridViewColumn x:Name="GridColumn0"
    HeaderTemplate="{StaticResource GridViewHeaderTemplate}" HeaderContainerStyle="{StaticResource GridViewHeaderStyle}" 
    Header="{x:Static strings:Strings.MainWindow_AppLog_Header_Severity}"
    behaviors:GridViewBehaviors.PersistColumnWidth="True">

C# (please scroll down - question at bottom):

// Register the property used in XAML
public static readonly DependencyProperty PersistColumnWidthProperty =
     DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(bool), typeof(GridViewBehaviors),
     new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnPersistColumnWidthChanged)));

// Provide read access to the value
public static bool GetPersistColumnWidth(DependencyObject d)
{
    return (bool)d.GetValue(PersistColumnWidthProperty);
}

// Provide write access to the value (set from XAML)
public static void SetPersistColumnWidth(DependencyObject d, bool value)
{
    d.SetValue(PersistColumnWidthProperty, value);
}

// This gets called once when the XAML is compiled to BAML
// Set the event handler
private static void OnPersistColumnWidthChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
    GridViewColumn column = sender as GridViewColumn;
    if (column == null)
        return;

    // Couple the UI event with a delegate
    if ((bool)args.NewValue)
        ((INotifyPropertyChanged)column).PropertyChanged += new PropertyChangedEventHandler(PersistWidth);
    else
        ((INotifyPropertyChanged)column).PropertyChanged -= new PropertyChangedEventHandler(PersistWidth);
}

// Deal with the events
static void PersistWidth(object sender, PropertyChangedEventArgs e)
{
    GridViewColumn column = sender as GridViewColumn;
    if (column == null)
        return;

    // We are only interested in changes of the "ActualWidth" property
    if (e.PropertyName != "ActualWidth")
        return;

    // Ignore NaNs
    if (column.ActualWidth == double.NaN)
        return;

    // Persist the width here
    // PROBLEM:
    // How to get a unique identifier for column, ideally its name set in XAML?
}

Solution

  • Thanks for your answers, Robert, Thomas and Rune. I like Runes answer, but I found something even easier for my situation here:

    I changed the type of the attached property from bool to string and simply store the name of the column there. The relevant changes are below.

    XAML:

    <GridViewColumn 
      behaviors:GridViewBehaviors.PersistColumnWidth="MainWindow_AppLog_Column0">
    

    C#:

    public static readonly DependencyProperty PersistColumnWidthProperty = 
      DependencyProperty.RegisterAttached("PersistColumnWidth", typeof(string),
      typeof(GridViewBehaviors), new FrameworkPropertyMetadata(string.Empty, 
      new PropertyChangedCallback(OnPersistColumnWidthChanged)));
    
    static void PersistWidth(object sender, PropertyChangedEventArgs e)
    {
      // This now yields "MainWindow_AppLog_Column0"
      string columnID = GetPersistColumnWidth(column);