tags:

views:

24

answers:

1

I have some shared styles which define how my DataGrids look and i keep them in Styles.xaml

I added a settings page, that will let the user change some of the colors. On that page, I have a sample grid (which automatically looks like the other grids thanks to shared resource styles). On that page, a user can use a color picker, to modify colors of attributes such as row background color, highlighted row colors, header background, and other styles which are applied using data driven converters.

I would like the style to be applied on the settings page grid only for a preview, and if applied, back to global styles.

i am all the way up to the point of loading a color picker for each configurable color, and not sure what to apply the result to.

Should i:

a. apply the selected color directly to the grid? (seems like grid only lets me assign styles dynamically, and not individual style setters)

b. get shared resource, copy it, and swap it in settings grid, than if user "applies" swap it for shared resource? (this one is ideal in my opinion, but not sure how to do this..)

c. another way of doing this?

+1  A: 

I would be inclined to handle it this way:

  1. Make each grid color a separate resource, then reference them from the grid styles using DynamicResource.
  2. Put these in a separate "colors" ResourceDictionary inside your Styles.xaml (under ResourceDictionary.MergedDictionaries)
  3. Define a ColorProxy object that has a Color property that, when set, updates the color of a brush in a ResourceDictionary
  4. In the constructor for the settings page, clone the "colors" ResourceDictionary and construct a ColorProxy for each color, and then bind them
  5. In the "Save" button for the settings page, copy the "colors" ResourceDictionary to your user settings storage and also into the main "colors" ResourceDictionary

Most of this is straightforward, so I won't go into too much detail.

Here is the idea of Styles.xaml:

<ResourceDictionary>
  <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary>
      <SolidColorBrush x:Key="Row Background Color" Color="..." />
      ...
    </ResourceDictionary>
  </ResourceDictionary.MergedDictionaries>

  <Style TargetType="DataGrid">
     ...
     <Setter Property="Background" Value="{DynamicResource Row Background Color}" />
     ...
  </Style>
</ResourceDictionary>

Here is code to copy the construct the ColorProxy objects:

public IEnumerable<ColorProxy> ColorProxies
{
  get
  {
    return
      from key in _colorDict.Keys
      select new ColorProxy { Dictionary=_colorDict, Key=key };
  }
}

and ColorProxy itself:

public class ColorProxy
{
  public ResourceDictionary Dictionary { get; set; }
  public object Key { get; set; }
  public Color Value
  {
    get { return ((SolidColorBrush)Dictionary[Key]).Color; }
    set { Dictionary[Key] = new SolidColorBrush { Color = value }; }
  }
}

The colors in the ResourceDictionary can now be edited with:

<ItemsControl ItemsSource="{Binding ColorProxies}">
  <ItemsControl.ItemTemplate>
    <DataTemplate DataType="local:ColorProxy">
      <DockPanel>
        <TextBlock Text="{Binding Key}" Width="200" />
        <ColorPicker Color="{Binding Color}" />
      </DockPanel>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

The edited ResourceDictionary can be converted to a string for storage using XamlWriter and reloaded using XamlReader.

The MergedDictionaries collection in the main ResourceDictionary generated by Styles.xaml can be modified by calling .Remove() on the old dictionary and .Add() on the new one.

ResourceDictionaries can be cloned by simply constructing a new ResourceDictionary th iterating the entries in the old dictionary and adding them to the new one.

This technique need not be limited to editing colors. Any kind of proxy objects can be created, including generic ones where the data conversion is handled by a converter in the binding.

Ray Burns