Using Infragistics Resource Washer in WPF Applications

[Infragistics] Mihail Mateev / Monday, December 12, 2011

NetAdvantage 2011 Vol.2 is a wonderful product that makes life easier for programmers. One of the most interesting features for XAML platforms in this release is the Resource Washer.

Infragistics Resource Washer is a XAML cross-platform component, that provides an fast and easy way to ‘wash’ a controls in the application layout with one or multiple colors. If you still are not familiar with Resource Washer, you can view the article Introduction to Resource Washing in NetAdvantage for Silverlight 11.2

This article is about how to use Resource Washer in WPF applications.

This component should be used with a resource dictionary and it will let you modify the overall color scheme of the application. The way it functions is not changing the original brushes/colors but rather making a copy of the dictionary and then changing the copy according to the defined settings. Resource dictionary contains resources used for application styling. As a resource dictionaries often we use predefined themes (sample application uses IG Theme). You could change one or more colors for specific theme and keep all other settings of styles.

The motivation to male this blog is to explain how to “wash” different Infragistics WPF components.

Working with themes

Themes are not directly  related with ResourceWasher component, but it is good to have in mind the different approach when used for different components.

In NetAdvantage for WPF LoB 2011 Vol.2 you could use components, designed only for WPF like XamDataChart and XamRibbon. Most of these components contain themes as resources in assemblies, containing controls.

 1: <igThemes:DataPresenterIGTheme/>
 2: <igThemes:PrimitivesIGTheme/>
 3: <igThemes:RibbonIGTheme/>
 4: <igThemes:EditorsIGTheme/>

 

Shared XAML components use external resources as ResourceDictionary files.

 1: <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.xamDataChart.xaml" />
 2: <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.xamColorPicker.xaml" />
 3: <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.DataVisualization.xaml" />

 

XamSchedule uses a different approach. You should set a color scheme to XamScheduleDataManager component.

 1: this.xamScheduleDataManager1.ColorScheme = new IGTheme();

 

When using a resource washer you need to set a ResourceDictionary to  ResourceWasher.SourceDictionary property.

 1: <Window.Resources>
 2:     <ResourceDictionary>
 3:         <ResourceDictionary.MergedDictionaries>
 4:             <igThemes:ResourceWasher x:Name="igResourceWasher"
 5:                 AutoWash="True" 
 6:                 WashMode="HueSaturationReplacement">
 7:                 <igThemes:ResourceWasher.SourceDictionary>
 8:                     <ResourceDictionary>
 9:                         <ResourceDictionary.MergedDictionaries>
 10:                             <igThemes:DataPresenterIGTheme/>
 11:                             <igThemes:PrimitivesIGTheme/>
 12:                             <igThemes:RibbonIGTheme/>
 13:                             <igThemes:EditorsIGTheme/>
 14:                             <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.xamDataChart.xaml" />
 15:                             <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.xamColorPicker.xaml" />
 16:                             <ResourceDictionary Source="/ResourceWasherIntegrationWpfApp;component/Themes/IG/IG.DataVisualization.xaml" />
 17:                         ResourceDictionary.MergedDictionaries>
 18:                     ResourceDictionary>
 19:                 igThemes:ResourceWasher.SourceDictionary>
 20:             igThemes:ResourceWasher>
 21:         ResourceDictionary.MergedDictionaries>
 22:     ResourceDictionary>
 23: Window.Resources>

 

Application layout

A demo application uses OData Northwind test service:  http://services.odata.org/Northwind/Northwind.svc/. Application is a dashboard that shows all customer orders.

In the sample WPF application are used different Infragistics WPF components: XamDataGrid, XamDataChart, XamRibbon,  XamSchedule (XamMonthView), XamColorPicker. The code below shows the application layout.

 1: <Grid x:Name="LayoutRoot" Width="Auto">
 2:     <Grid.RowDefinitions>
 3:         <RowDefinition Height="150"/>
 4:         <RowDefinition Height="*"/>
 5:     Grid.RowDefinitions>
 6:     <igRibbon:XamRibbon Grid.Row="0"  Margin="0" Name="commandsRibbon" VerticalAlignment="Top" >
 7:         <igRibbon:XamRibbon.ApplicationMenu>
 8:             <igRibbon:ApplicationMenu Image="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/ApplicationMenuImage.png"
 9:         RecentItemsHeader="Recent Items">
 10:                 <igRibbon:ButtonTool Caption="New" LargeImage="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/NewAdd32.png"/>
 11:                 <igRibbon:ButtonTool Caption="Open" LargeImage="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Open32.png"/>
 12:                 <igRibbon:ButtonTool Caption="Save" LargeImage="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Save32.png" />
 13:                 <igRibbon:ButtonTool Caption="Close" LargeImage="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Close32.png" />
 14:                 <igRibbon:SeparatorTool/>
 15:  
 16:                 <igRibbon:ApplicationMenu.RecentItems>
 17:                     <igRibbon:ButtonTool Caption="1. Document1.doc"/>
 18:                     <igRibbon:ButtonTool Caption="2. Documen2.doc"/>
 19:                     <igRibbon:ButtonTool Caption="3. Document3.doc"/>
 20:                     <igRibbon:ButtonTool Caption="4. Document4.doc"/>
 21:                     <igRibbon:ButtonTool Caption="5. Document5.doc"/>
 22:                     <igRibbon:ButtonTool Caption="6. Document6.doc"/>
 23:                     <igRibbon:ButtonTool Caption="7. Document7.doc"/>
 24:                     <igRibbon:ButtonTool Caption="8. Document8.doc"/>
 25:                     <igRibbon:ButtonTool Caption="9. Document9.doc"/>
 26:                     <igRibbon:ButtonTool Caption="1. Document1.doc"/>
 27:                 igRibbon:ApplicationMenu.RecentItems>
 28:                 <igRibbon:ApplicationMenu.FooterToolbar>
 29:                     <igRibbon:ApplicationMenuFooterToolbar>
 30:                         <igRibbon:ButtonTool Caption="Exit"/>
 31:                     igRibbon:ApplicationMenuFooterToolbar>
 32:                 igRibbon:ApplicationMenu.FooterToolbar>
 33:             igRibbon:ApplicationMenu>
 34:         igRibbon:XamRibbon.ApplicationMenu>
 35:         <igRibbon:XamRibbon.QuickAccessToolbar>
 36:             <igRibbon:QuickAccessToolbar>
 37:                 <igRibbon:QatPlaceholderTool TargetId="ADD_CONTACT_TOOL" />
 38:             igRibbon:QuickAccessToolbar>
 39:         igRibbon:XamRibbon.QuickAccessToolbar>
 40:         <igRibbon:XamRibbon.Tabs>
 41:             <igRibbon:RibbonTabItem Header="Home">
 42:  
 43:                 <igRibbon:RibbonGroup Caption="Contacts">
 44:  
 45:                     <igRibbon:ToggleButtonTool Id="ADD_CONTACT_TOOL" IsChecked="{Binding ElementName=ordersDataGrid, Path=(FieldLayoutSettings).AllowAddNew, Mode=TwoWay}"  Caption="Add Contact" igRibbon:RibbonGroup.MaximumSize="ImageAndTextLarge"  >
 46:                         <igRibbon:ToggleButtonTool.SmallImage>
 47:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/UserAdd16.png" />
 48:                         igRibbon:ToggleButtonTool.SmallImage>
 49:                             <igRibbon:ToggleButtonTool.LargeImage>
 50:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/UserAdd32.png" />
 51:                             igRibbon:ToggleButtonTool.LargeImage>
 52:                     igRibbon:ToggleButtonTool>
 53:                     <igRibbon:ButtonTool Id="DELETE_CONTACT_TOOL" Caption="Delete Contact" igRibbon:RibbonGroup.MaximumSize="ImageAndTextLarge"  Click="ButtonToolClick">
 54:                         <igRibbon:ButtonTool.SmallImage>
 55:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/UserDelete16.png" />
 56:                         igRibbon:ButtonTool.SmallImage>
 57:                         <igRibbon:ButtonTool.LargeImage>
 58:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/UserDelete32.png" />
 59:                         igRibbon:ButtonTool.LargeImage>
 60:                     igRibbon:ButtonTool>
 61:  
 62:                 igRibbon:RibbonGroup>
 63:                 <igRibbon:RibbonGroup Caption="Resources">
 64:                     <StackPanel Orientation="Vertical">
 65:                         <Label Content="Select Resource"/>
 66:                         <ComboBox x:Name="cboResources"  HorizontalAlignment="Stretch" />
 67:                     StackPanel>
 68:                     
 69:                 igRibbon:RibbonGroup>
 70:                 <igRibbon:RibbonGroup Caption="Schedule">
 71:  
 72:                     <igRibbon:ToggleButtonTool Id="SCHEDULE_TOOL"  IsChecked="{Binding ElementName=MainWindow, Path=IsScheduleVisible, Mode=TwoWay}"  Caption="Show Schedule" igRibbon:RibbonGroup.MaximumSize="ImageAndTextLarge"  >
 73:                         <igRibbon:ToggleButtonTool.SmallImage>
 74:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Table16.png" />
 75:                         igRibbon:ToggleButtonTool.SmallImage>
 76:                         <igRibbon:ToggleButtonTool.LargeImage>
 77:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Table32.png" />
 78:                         igRibbon:ToggleButtonTool.LargeImage>
 79:                     igRibbon:ToggleButtonTool>
 80:                 igRibbon:RibbonGroup>
 81:                 <igRibbon:RibbonGroup Caption="Resource Washer">
 82:                     <Controls:ResourceWasherControl x:Name="rwControl" />
 83:                 igRibbon:RibbonGroup>
 84:  
 85:             igRibbon:RibbonTabItem>
 86:             <igRibbon:RibbonTabItem Header="Export">
 87:                 <igRibbon:RibbonGroup Caption="Print / Export">
 88:                     <igRibbon:ButtonTool Id="PRINT_TOOL" Caption="Print" igRibbon:RibbonGroup.MaximumSize="ImageAndTextLarge"  Click="ButtonToolClick">
 89:                         <igRibbon:ButtonTool.SmallImage>
 90:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Printer16.png" />
 91:                         igRibbon:ButtonTool.SmallImage>
 92:                         <igRibbon:ButtonTool.LargeImage>
 93:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/Printer32.png" />
 94:                         igRibbon:ButtonTool.LargeImage>
 95:                         
 96:                     igRibbon:ButtonTool>
 97:                     <igRibbon:ButtonTool Id="EXPORT_TOOL" Caption="Export" igRibbon:RibbonGroup.MaximumSize="ImageAndTextLarge"  Click="ButtonToolClick">
 98:                         <igRibbon:ButtonTool.SmallImage>
 99:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/ExportContacts16.png" />
 100:                         igRibbon:ButtonTool.SmallImage>
 101:                         <igRibbon:ButtonTool.LargeImage>
 102:                             <BitmapImage UriSource="/ResourceWasherIntegrationWpfApp;component/Images/Ribbon/ExportContacts32.png" />
 103:                         igRibbon:ButtonTool.LargeImage>
 104:                     igRibbon:ButtonTool>
 105:                 igRibbon:RibbonGroup>
 106:             igRibbon:RibbonTabItem>
 107:             -->
 108:         igRibbon:XamRibbon.Tabs>
 109:     igRibbon:XamRibbon>
 110:     <Grid Grid.Row="1">
 111:         <Grid.ColumnDefinitions>
 112:             <ColumnDefinition/>
 113:             <ColumnDefinition Width="Auto"/>
 114:         Grid.ColumnDefinitions>
 115:         <Grid x:Name="NoScheduleView" Visibility="Visible">
 116:             <Grid.RowDefinitions>
 117:                 <RowDefinition/>
 118:                 <RowDefinition/>
 119:             Grid.RowDefinitions>
 120:             <igDP:XamDataGrid Grid.Row="0" Margin="5" DataSource="{Binding}" Name="ordersDataGrid">
 121:                 <igDP:XamDataGrid.FieldSettings>
 122:                     <igDP:FieldSettings AllowEdit="True" />
 123:                 igDP:XamDataGrid.FieldSettings>
 124:                 <igDP:XamDataGrid.ViewSettings>
 125:                     <igDP:GridViewSettings />
 126:                 igDP:XamDataGrid.ViewSettings>
 127:                 <igDP:XamDataGrid.FieldLayoutSettings>
 128:                     <igDP:FieldLayoutSettings   AutoGenerateFields="True"  AddNewRecordLocation="Default"  AllowDelete="True" SelectionTypeCell="Single" SelectionTypeField="None" SelectionTypeRecord="Single"  />
 129:                 igDP:XamDataGrid.FieldLayoutSettings>
 130:             igDP:XamDataGrid>
 131:             <ig:XamDataChart Grid.Row="1"  Margin="5" DataContext="{Binding}"  Name="ordersDataChart" 
 132:                     HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
 133:                     SeriesMouseLeftButtonDown="XamDataChartSeriesMouseLeftButtonDown" >
 134:                 <ig:XamDataChart.Axes>
 135:                     <ig:CategoryXAxis x:Name="xmXAxis" ItemsSource="{Binding}" Label="{}{CustomerID}"  />
 136:                     <ig:NumericYAxis x:Name="xmYAxisFreight" />
 137:                 ig:XamDataChart.Axes>
 138:                 <ig:XamDataChart.Series>
 139:                     <ig:ColumnSeries Thickness="2"                                                  
 140:                                     ItemsSource="{Binding}"
 141:                                     ValueMemberPath="Freight"
 142:                                     XAxis="{Binding ElementName=xmXAxis}"
 143:                                     YAxis="{Binding ElementName=xmYAxisFreight}">
 144:                     ig:ColumnSeries>
 145:                 ig:XamDataChart.Series>
 146:             ig:XamDataChart>
 147:         Grid>
 148:         <Grid x:Name="ScheduleView" Grid.Column="1" Visibility="Visible" Width="400">
 149:             <ig:ListScheduleDataConnector HorizontalAlignment="Left" Name="listScheduleDataConnector1" VerticalAlignment="Top"                
 150:            ResourceItemsSource="{Binding Resources, Mode=TwoWay}" Loaded="ListScheduleDataConnector1Loaded" 
 151:            ResourceCalendarItemsSource="{Binding ResourceCalendars, Mode=TwoWay}"  
 152:             AppointmentItemsSource="{Binding Appointments, Mode=TwoWay}">
 153:                 <ig:ListScheduleDataConnector.ResourcePropertyMappings>
 154:                     <ig:ResourcePropertyMappingCollection UseDefaultMappings="True"/>
 155:                 ig:ListScheduleDataConnector.ResourcePropertyMappings>
 156:                 <ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings>
 157:                     <ig:ResourceCalendarPropertyMappingCollection UseDefaultMappings="True"/>
 158:                 ig:ListScheduleDataConnector.ResourceCalendarPropertyMappings>
 159:                 <ig:ListScheduleDataConnector.AppointmentPropertyMappings>
 160:                     <ig:AppointmentPropertyMappingCollection UseDefaultMappings="True"/>
 161:                 ig:ListScheduleDataConnector.AppointmentPropertyMappings>
 162:             ig:ListScheduleDataConnector>
 163:             <ig:XamScheduleDataManager HorizontalAlignment="Left" Name="xamScheduleDataManager1"  CurrentUserId="currentUser"
 164:          VerticalAlignment="Top" DataConnector="{Binding ElementName=listScheduleDataConnector1}" >
 165:                 <ig:XamScheduleDataManager.Settings>
 166:                     <ig:ScheduleSettings>
 167:                         <ig:ScheduleSettings.AppointmentSettings>
 168:                             <ig:AppointmentSettings AllowEdit="False"  AllowDragging="No" AllowResizing="No"/>
 169:                         ig:ScheduleSettings.AppointmentSettings>
 170:                     ig:ScheduleSettings>
 171:                 ig:XamScheduleDataManager.Settings>
 172:             ig:XamScheduleDataManager>
 173:             <ig:XamMonthView HorizontalAlignment="Stretch"  Name="xamMonthView1"  VerticalAlignment="Stretch" 
 174:                         DataManager="{Binding ElementName=xamScheduleDataManager1}" Visibility="Visible" />
 175:  
 176:         Grid>
 177:     Grid>
 178:  
 179: Grid>

 

Resource Washing

To wash controls it is enough to have ResourceWasher.AutoWash = true and set ResourceWasher.WashColor. Washing will be done immediately.

 1: void RwControlColorChanged(object sender, RoutedEventArgs e)
 2: {
 3:     var resWash = (ResourceWasher)((this.Resources.MergedDictionaries)[0]);
 4:  
 5:     if (resWash == null)
 6:     {
 7:         return;
 8:     }
 9:  
 10:     resWash.WashColor = rwControl.WashColor;
 11: }

 

Run the sample application

WPFResourceWasher_Pic01[1]

Change the wash color and wash mode (optional) from “Resource Washer” ribbon group.

WPFResourceWasher_Pic02[1]

Click “Resource Wash” button to apply the new color.

Unfortunately some parts of the controls are not “washed”.

WPFResourceWasher_Pic00[1] 

XamSchedule Washing

To be possible to apply custom color scheme to XamSchedule you need to have a custom class, that inherits some of the preset color schemes. In this sample class CustomColorScheme inherits IGColorScheme (IGColorScheme is used to implement IG Theme for XamSchedule elements). You could override BaseColors read only property to change the first of preset colors with custom one.

You could use ctor with parameter to set the custom color.

 1: class CustomColorScheme: IGColorScheme
 2: {
 3:     #region Private Members
 4:  
 5:     private readonly Color _defaultColor;
 6:  
 7:     #endregion //Private Members
 8:  
 9:     #region Constructors
 10:     public CustomColorScheme(Color color)
 11:         : base()
 12:     {
 13:         _defaultColor = color;
 14:     }
 15:     #endregion //Constructors
 16:  
 17:     #region Overrides
 18:  
 19:     #region BaseColors
 20:     public override ReadOnlyCollection BaseColors
 21:     {
 22:         get
 23:         {
 24:             return new ReadOnlyCollection(
 25:                 new Color[] { _defaultColor ,(Color)ColorConverter.ConvertFromString("#FFA8A8A8"), (Color)ColorConverter.ConvertFromString("#FF216E99"),
 26:                     (Color)ColorConverter.ConvertFromString("#FFA4BA29"),(Color)ColorConverter.ConvertFromString("#FF828282"), (Color)ColorConverter.ConvertFromString("#FFFDBD48"), 
 27:                 (Color)ColorConverter.ConvertFromString("#FFFF6A6F"),(Color)ColorConverter.ConvertFromString("#FF9E6FC1"), (Color)ColorConverter.ConvertFromString("#FFF79036"), 
 28:                 (Color)ColorConverter.ConvertFromString("#FF793BAC"),(Color)ColorConverter.ConvertFromString("#FF48892D"), (Color)ColorConverter.ConvertFromString("#FFD3404B"), 
 29:                 (Color)ColorConverter.ConvertFromString("#FF8282BE"),(Color)ColorConverter.ConvertFromString("#FF8CBEBE"), (Color)ColorConverter.ConvertFromString("#FFC8A775")}
 30:             );
 31:         }
 32:     }
 33:     #endregion //BaseColors
 34:  
 35:  
 36:     #endregion //Overrides
 37: }

 

Update the RwControlColorChanged method to create a custom color theme with the selected color and set it to XamScheduleDataManager instance.

 1: void RwControlColorChanged(object sender, RoutedEventArgs e)
 2: {
 3:  
 4:     var resWash = (ResourceWasher)((this.Resources.MergedDictionaries)[0]);
 5:  
 6:     if (resWash == null)
 7:     {
 8:         return;
 9:     }
 10:  
 11:     this.xamScheduleDataManager1.ColorScheme = new CustomColorScheme(rwControl.WashColor);
 12:  
 13:     this.xamMonthView1.DataManager.ColorSchemeResolved.InvalidateBrushCache();
 14:  
 15:  
 16:     resWash.WashColor = rwControl.WashColor;
 17: }

 

Now XamMonthView instance is washed with the selected color.

WPFResourceWasher_Pic03a[1]

XamDataChart Series Washing

XamDataChart series don’t change its color using the resource washer.

You could “wash” series using Series.Brush property.

 1: void RwControlColorChanged(object sender, RoutedEventArgs e)
 2:        {
 3:  
 4:  
 5:            var resWash = (ResourceWasher)((this.Resources.MergedDictionaries)[0]);
 6:  
 7:            if (resWash == null)
 8:            {
 9:                return;
 10:            }
 11:  
 12:            this.xamScheduleDataManager1.ColorScheme = new CustomColorScheme(rwControl.WashColor);
 13:  
 14:            this.xamMonthView1.DataManager.ColorSchemeResolved.InvalidateBrushCache();
 15:  
 16:            ordersDataChart.Series[0].Brush = new SolidColorBrush(rwControl.WashColor);
 17:  
 18:            resWash.WashColor = rwControl.WashColor;
 19:        }

 

Now all Infragistics components are “washed” properly.

WPFResourceWasher_Pic03[1] 

WPFResourceWasher_Pic04[1]

If you open system dialogs from your application, these components will not be “washed”. You could use Infragistics XamDialogWindow or own user control to have “washed” dialogs.

WPFResourceWasher_Pic06[1]

 

Source code you could download here.