[DevExpress Support Team: CLONED FROM T578454: SVG Skins and Palettes – FAQ]
In the WinForms demo there's a "Color Swatches" button, how do I add it to XAF and save the selection for the next login of the user?
OBSOLETE - How to show Color Swatches for the Bezier skin and persist the user choice in XAF WinForms apps
Answers approved by DevExpress Support
With v18.2, the Light Style Main Form Template and Light Style Main Ribbon Form Template contain the built-in palette selector for the Bezier skin.
To use these templates, set the WinApplication.UseLightStyle property to True. To enable this feature in an existing application with custom templates, refer to the How to migrate a WinForms application to use the Light Style article.
-------------------------------------------------------------------------------------
For other Main Form templates and previous versions, use the following solution.
To provide the end user with the capability to select a color palette and persist the choice in the application model, you can add the following controller into the YourSolutionName.Module.Win project:
C#using System.ComponentModel;
using System.Linq;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Actions;
using DevExpress.ExpressApp.Model;
using DevExpress.ExpressApp.Win;
using DevExpress.ExpressApp.Win.SystemModule;
using DevExpress.LookAndFeel;
using DevExpress.Skins;
namespace MainDemo.Module.Win.Controllers {
public class SvgSkinController : WindowController, IModelExtender {
private SimpleAction showPaletteAction;
private ChooseSkinController chooseSkinController;
public SvgSkinController() {
TargetWindowType = WindowType.Main;
this.showPaletteAction = new SimpleAction(this, "ShowPalette", "Panels");
this.showPaletteAction.Execute += ShowPaletteAction_Execute;
this.showPaletteAction.ImageName = "Action_ChooseSkin";
}
protected override void OnFrameAssigned() {
base.OnFrameAssigned();
chooseSkinController = Frame.GetController<ChooseSkinController>();
if (chooseSkinController != null) {
chooseSkinController.ChooseSkinAction.ExecuteCompleted += ChooseSkinAction_ExecuteCompleted;
}
}
private void ChooseSkinAction_ExecuteCompleted(object sender, ActionBaseEventArgs e) {
RestorePalette(Application);
UpdateActionActivity(showPaletteAction);
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
if(disposing && chooseSkinController != null) {
chooseSkinController.ChooseSkinAction.ExecuteCompleted -= ChooseSkinAction_ExecuteCompleted;
}
}
public static void RestorePalette(XafApplication application) {
var model = (IModelApplicationOptionsSkinSvg)application.Model.Options;
if(model.Palette == null) return;
var skin = CommonSkins.GetSkin(UserLookAndFeel.Default);
DevExpress.Utils.Svg.SvgPalette palette = skin.CustomSvgPalettes[model.Palette];
if(palette != null) {
skin.SvgPalettes[Skin.DefaultSkinPaletteName].SetCustomPalette(palette);
LookAndFeelHelper.ForceDefaultLookAndFeelChanged();
}
}
public static void SavePalette(XafApplication application) {
var model = (IModelApplicationOptionsSkinSvg)application.Model.Options;
model.Palette = UserLookAndFeel.Default.ActiveSvgPaletteName;
}
private void ShowPaletteAction_Execute(object sender, SimpleActionExecuteEventArgs e) {
var form = ((WinWindow)Window).Form;
using(var dialog = new DevExpress.Customization.SvgSkinPaletteSelector(form)) {
dialog.ShowDialog();
SavePalette(Application);
}
}
protected override void UpdateActionActivity(ActionBase action) {
base.UpdateActionActivity(action);
var isSvgSkin = UserLookAndFeel.Default.ActiveStyle == ActiveLookAndFeelStyle.Skin
&& UserLookAndFeel.Default.ActiveSkinName == SkinStyle.Bezier;
action.Active["Svg"] = isSvgSkin;
}
public void ExtendModelInterfaces(ModelInterfaceExtenders extenders) {
extenders.Add<IModelApplicationOptionsSkin, IModelApplicationOptionsSkinSvg>();
}
}
public interface IModelApplicationOptionsSkinSvg {
[Category("Appearance")]
string Palette { get; set; }
}
}
Visual BasicOption Infer On
Imports System.ComponentModel
Imports System.Linq
Imports DevExpress.ExpressApp
Imports DevExpress.ExpressApp.Actions
Imports DevExpress.ExpressApp.Model
Imports DevExpress.ExpressApp.Win
Imports DevExpress.ExpressApp.Win.SystemModule
Imports DevExpress.LookAndFeel
Imports DevExpress.Skins
Namespace MainDemo.Module.Win.Controllers
Public Class SvgSkinController
Inherits WindowController
Implements IModelExtender
Private showPaletteAction As SimpleAction
Private chooseSkinController As ChooseSkinController
Public Sub New()
TargetWindowType = WindowType.Main
Me.showPaletteAction = New SimpleAction(Me, "ShowPalette", "Panels")
Me.showPaletteAction.ImageName = "Action_ChooseSkin"
AddHandler Me.showPaletteAction.Execute, AddressOf ShowPaletteAction_Execute
End Sub
Protected Overrides Sub OnFrameAssigned()
MyBase.OnFrameAssigned()
chooseSkinController = Frame.GetController(Of ChooseSkinController)()
If chooseSkinController IsNot Nothing Then
AddHandler chooseSkinController.ChooseSkinAction.ExecuteCompleted, AddressOf ChooseSkinAction_ExecuteCompleted
End If
End Sub
Private Sub ChooseSkinAction_ExecuteCompleted(ByVal sender As Object, ByVal e As ActionBaseEventArgs)
RestorePalette(Application)
UpdateActionActivity(showPaletteAction)
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
MyBase.Dispose(disposing)
If disposing AndAlso chooseSkinController IsNot Nothing Then
RemoveHandler chooseSkinController.ChooseSkinAction.ExecuteCompleted, AddressOf ChooseSkinAction_ExecuteCompleted
End If
End Sub
Public Shared Sub RestorePalette(ByVal application As XafApplication)
Dim model = DirectCast(application.Model.Options, IModelApplicationOptionsSkinSvg)
If model.Palette Is Nothing Then
Return
End If
Dim skin = CommonSkins.GetSkin(UserLookAndFeel.Default)
Dim palette As DevExpress.Utils.Svg.SvgPalette = skin.CustomSvgPalettes(model.Palette)
If palette IsNot Nothing Then
skin.SvgPalettes(Skin.DefaultSkinPaletteName).SetCustomPalette(palette)
LookAndFeelHelper.ForceDefaultLookAndFeelChanged()
End If
End Sub
Public Shared Sub SavePalette(ByVal application As XafApplication)
Dim model = DirectCast(application.Model.Options, IModelApplicationOptionsSkinSvg)
model.Palette = UserLookAndFeel.Default.ActiveSvgPaletteName
End Sub
Private Sub ShowPaletteAction_Execute(ByVal sender As Object, ByVal e As SimpleActionExecuteEventArgs)
Dim form = CType(Window, WinWindow).Form
Using dialog = New DevExpress.Customization.SvgSkinPaletteSelector(form)
dialog.ShowDialog()
SavePalette(Application)
End Using
End Sub
Protected Overrides Sub UpdateActionActivity(ByVal action As ActionBase)
MyBase.UpdateActionActivity(action)
Dim isSvgSkin = UserLookAndFeel.Default.ActiveStyle = ActiveLookAndFeelStyle.Skin AndAlso UserLookAndFeel.Default.ActiveSkinName = SkinStyle.Bezier
action.Active("Svg") = isSvgSkin
End Sub
Public Sub ExtendModelInterfaces(ByVal extenders As ModelInterfaceExtenders) Implements IModelExtender.ExtendModelInterfaces
extenders.Add(Of IModelApplicationOptionsSkin, IModelApplicationOptionsSkinSvg)()
End Sub
End Class
Public Interface IModelApplicationOptionsSkinSvg
<Category("Appearance")>
Property Palette() As String
End Interface
End Namespace
The main code is borrowed from the SVG Skins and Palettes – FAQ article for our WinForms components and is executed in the UI using the ShowPalette SimpleAction, which is activeonly when the Bezier skin is selected or the ChooseSkinController's Action is executed:
This action invokes the customization dialog for further skin tuning:
To save the palette user choice, the Options node in the Model Editor is extended as per the Concepts > Application Model > Extend and Customize the Application Model in Code article:
The ShowPalette action is assigned to the "Panels" category shown in the same sub-menu or Ribbon page as the ChooseSkin action. The "Appearance" category in existing templates cannot show additional actions because there's no action container. To place this action at a different location, you can either follow the UI Customization > Place an Action in a Different Location approach or create a custom main window template with an additional action container as described in the How to: Create a Custom WinForms Ribbon Template and How to: Create a Custom WinForms Standard Template articles.
I'd like to inform you about our progress in supporting SVG images and ask for your feedback. Please check out the WinForms SVG Images Support - v18.1 Preview article. We would greatly appreciate it if you test this functionality, report your comments and answer our questions. Also, see the XAF - Access v18.1 Features Today for Early Testing blog post for other new coming features.
Thank you for your time and cooperation.
I managed to add a button for changing the color but would like a more elegant solution and the feature to persist the style:
public sealed class ReyOneMainViewController : WindowController { public ReyOneMainViewController() { InitializeComponent(); TargetWindowType = WindowType.Main; ColorSwatchAction.Execute += (s, e) => ShowColorSwatch(e); } private void ShowColorSwatch(SimpleActionExecuteEventArgs e) { var owner = (Form)Application.MainWindow.Template; using (var dialog = new DevExpress.Customization.SvgSkinPaletteSelector(owner)) { dialog.ShowDialog(); } } }
I also has been solving the same task 2 days ago. Here what I've got.
This works only for Ribbon style, but we can easy do the same for Classic style too. In the OnActivated, subscribe to TemplateChanged event and put a button near Themes gallery:
private void Window_TemplateChanged(object sender, EventArgs e) { var template = ((Window)sender).Template as MainRibbonFormV2; if(template == null) return; var ribbon = template.Ribbon.AccessibleRibbon.Ribbon; ribbon.BeginInit(); var viewPage = ribbon.Pages.FirstOrDefault(x => x.Name == "viewPage"); if (viewPage != null) { var appearanceGroup = viewPage.Groups.FirstOrDefault(x => x.Name == "ribbonGroupAppearance"); if (appearanceGroup != null) { BarButtonItem itemPalette = new BarButtonItem(ribbon.Manager, "Switch palette", 0); itemPalette.LargeGlyph=ImageLoader.Instance.GetImageInfo("other.ColorMixer_32x32").Image; itemPalette.Hint = "Available only for the Bezier theme"; itemPalette.ItemClick += ItemPalette_ItemClick; appearanceGroup.ItemLinks.AddRange(itemPalette); } } ribbon.EndInit(); // Restore Bezier palette var model = Application.Model as IModelExtenderApplication; if (model != null && UserLookAndFeel.Default.ActiveSkinName == "The Bezier") { try { var skin = CommonSkins.GetSkin(UserLookAndFeel.Default); SvgPalette palette = skin.CustomSvgPalettes[model.ThemePalette]; skin.SvgPalettes[Skin.DefaultSkinPaletteName].SetCustomPalette(palette); LookAndFeelHelper.ForceDefaultLookAndFeelChanged(); } catch { // } } } private void ItemPalette_ItemClick(object sender, ItemClickEventArgs e) { using (SvgSkinPaletteSelector dialog = new SvgSkinPaletteSelector(Form.ActiveForm)) { dialog.ShowDialog(); if (UserLookAndFeel.Default.ActiveSkinName == "The Bezier") { var commonSkin = CommonSkins.GetSkin(UserLookAndFeel.Default); SvgPalette customPallete = commonSkin.SvgPalettes[Skin.DefaultSkinPaletteName].CustomPalette; var name = commonSkin.CustomSvgPalettes.FirstOrDefault(x => x.Value == customPallete).Key?.Name; var model = Application.Model as IModelExtenderApplication; if (model != null) { model.ThemePalette = name; } } } }
IModelExtenderApplication is a model extender with ThemePalette string property. It is added to the Application node of the model.
public interface IModelExtenderApplication { string ThemePalette { get; set; } }
Module class:
public override void ExtendModelInterfaces(ModelInterfaceExtenders extenders) { base.ExtendModelInterfaces(extenders); extenders.Add<IModelApplication, IModelExtenderApplication>(); }
@Juan Antonio, @Yauhen, thanks for tackling this problem.
@Yauhen that's a really nice approach. :-)
I used an action instead of creating a BarButtonItem, and also set as Active or not depending on the selected style as follows:
DevExpress.LookAndFeel.UserLookAndFeel.Default.StyleChanged += OnStyleChanged; private void OnStyleChanged(object sender, EventArgs e) { ColorSwatchAction.Active[_skinNameWithPaletteSupport] = IsBezierSkin(); } ColorSwatchAction = new SimpleAction(_components) { Caption = "Color Swatch", Category = "Windows", ConfirmationMessage = null, Id = "Win.ColorSwatchAction", ImageName = "Palette", ToolTip = null };
That way I avoid users from picking a color when the style doesn't support it.
@Juan Antonio Trujillo Montenegro, ha-ha, I was too lazy to write few more lines of code to disable an action. Instead, just added a tooltip.
What is really not to good with SimpleAction is that we can't put it right near themes gallery.
Thanks for the advice! I wi ll also use this in my code.
@Yauhen setting the action's category to Windows as shown in the code above does the trick. See attached file ;-)
PS. At first I tried with the other categories and didn't see the action at any place until I tried with each one and discovered the 'Windows' category did the trick, it's weird name, one would expect to find it under 'View' or 'Appearances'…
@Juan Antonio, the ChooseSkin action is on the 'Appearance' category.
@Santiago That's correct, however, when I set the new ColorSwatchAction to such category is not shown in any place; nonetheless, setting it in Windows category shows next to the ChooseSkin action as shown in my attached image.