I have a custom form which extends Form, and it overrides the OnPaint method to draw the form in a specific design using the Theme. I have a custom class "Theme" which contains four colors to set on the form:
public class Theme
{
public Color BackColor { get; set; }
public Color MouseHoverColor { get; set; }
public Color ThemeColor { get; set; }
public Color ForeColor { get; set; }
}
The theme has a custom UITypeEditor which uses a custom theme editor using color pickers, which I have tested and works fine:
public class ThemeTypeEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
var editor = new ThemeEditor((CustomForm) context.Instance);
if (editor.ShowDialog() == DialogResult.OK)
{
return editor.Theme;
}
return base.EditValue(context, provider, value);
}
}
I also have a static class "Themes" which contains a default theme for the form to use:
public static class Themes
{
public static Theme DarkGreen = new Theme
{
BackColor = Color.FromArgb(53, 53, 53),
ForeColor = Color.FromArgb(237, 234, 235),
MouseHoverColor = Color.FromArgb(65,65,65),
ThemeColor = Color.FromArgb(32, 203, 88)
};
}
And in my custom form, I have this property which is used by the OnPaint method:
private Theme theme = (Theme) Themes.DarkGreen;
[Category("Appearance"),
Description("Specifies the Theme for this form."),
Editor(typeof(ThemeTypeEditor), typeof(UITypeEditor)),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Theme Theme {
get { return theme; }
set {
theme = value;
Invalidate();
}
}
However, when I try to alter the theme in the designer on a new form which extends CustomForm, the designer does not update and the theme reverts to Themes.DarkGreen at runtime. I am unsure as to what I should be doing for this, as it is my understanding that I should be able to alter the theme and see the changes at design-time and runtime. As stated, the OnPaint method uses the Theme field above to draw the background color of the form and additional painted graphics. Any help is appreciated.
EDIT:
The form in the form designer changes when the project is built and run, but the actual running form remains unchanged. I would like to have the changes be immediately visible when the theme is applied, and for it to be applied on the built application.
EDIT 2:
I decided to show a MessageBox to debug this. I placed one in my ThemeTypeEditor's EditValue method before it returned the new theme, and sure enough, it works, and the theme values are correct. However, I also placed a MessageBox in my Theme Property setter, after Invalidate();, and nothing shows. It's like the property's set isn't being called at all.
I solved this by changing various things. The first was creating a base class named ComponentBase which extends Component and implement INotifyPropertyChanged:
public class ComponentBase : Component, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
var changeService = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
if (changeService != null)
changeService.OnComponentChanged(this, TypeDescriptor.GetProperties(this).Find(propertyName, false), field, value);
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
This meant that I needed to remove the Auto-Property nature of the properties of my Theme. I changed the Editor of each Color in the Theme so that it used the color picker, and gave them a DesignerSerializationVisibility of Visible:
public sealed partial class Theme : ComponentBase
{
private Color backgroundColor;
[Category("Theme")]
[DisplayName("Background Color")]
[Editor(typeof (ThemeTypeEditor), typeof (UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof (ThemeColorConverter))]
public Color BackgroundColor
{
get { return backgroundColor; }
set { SetField(ref backgroundColor, value); }
}
private Color foregroundColor;
[Category("Theme")]
[DisplayName("Foreground Color")]
[Editor(typeof(ThemeTypeEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof(ThemeColorConverter))]
public Color ForegroundColor
{
get { return foregroundColor; }
set { SetField(ref foregroundColor, value); }
}
private Color highlightColor;
[Category("Theme")]
[DisplayName("Highlight Color")]
[Editor(typeof(ThemeTypeEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof(ThemeColorConverter))]
public Color HighlightColor
{
get { return highlightColor; }
set { SetField(ref highlightColor, value); }
}
private Color borderColor;
[Category("Theme")]
[DisplayName("Border Color")]
[Editor(typeof(ThemeTypeEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof(ThemeColorConverter))]
public Color BorderColor
{
get { return borderColor; }
set { SetField(ref borderColor, value); }
}
private Color themeColor;
[Category("Theme")]
[DisplayName("Theme Color")]
[Editor(typeof(ThemeTypeEditor), typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[TypeConverter(typeof(ThemeColorConverter))]
public Color ThemeColor
{
get { return themeColor; }
set { SetField(ref themeColor, value); }
}
}
As the Theme in my custom form uses DesignerSerializationVisibility.Content, this allows code to be generated for the contents of the Theme, meaning that the individual colors in the Theme are updated both design-time and run-time:
private Theme theme;
[DisplayName("Theme")]
[Category("Appearance")]
[Description("Specifies the Theme for this form.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Theme Theme
{
get { return theme; }
set
{
theme = value;
Invalidate();
}
}
Related
I have created a UserControl which extends PictureBox Control
public partial class AudioMonitor : PictureBox
{
private SelectionSettings _selectionSettings;
[Description("Various settings regarding to the selection visuals"), Category("Custom")]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public SelectionSettings SelectionSettings
{
get { return this._selectionSettings; }
set { this._selectionSettings = value; }
}
}
SelectionSettings property is a custom class which I have created as follows:
[Serializable]
public class SelectionSettings
{
private SelectionMarker _startMarker;
private SelectionMarker _endMarker;
private SelectionPen _selectionStyle;
public SelectionMarker StartMarker
{
get { return this._startMarker; }
set { this._startMarker = value; }
}
public SelectionMarker EndMarker
{
get { return this._endMarker; }
set { this._endMarker = value; }
}
public SelectionPen SelectionStyle
{
get { return this._selectionStyle; }
set { this._selectionStyle = value; }
}
}
[Serializable]
public class SelectionMarker
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
set { this._style = value; }
}
public float Width
{
get { return this._width; }
set { this._width = value; }
}
public Pen Pen
{
get
{
Pen pen = new Pen(this._color);
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
[Serializable]
public class SelectionPen
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
private float _alpha = 100;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
}
public float Width
{
get { return this._width; }
}
public float Alpha
{
get { return this._alpha; }
}
public int AlphaPercent
{
get { return (int)Math.Round(this._alpha * 100 / 255); }
set
{
if (value > 0 && value <= 100)
this._alpha = (value * 255 / 100);
else
throw new ArgumentException("Alpha percentage should be between (0, 100]");
}
}
public Pen Pen
{
get
{
Pen pen = new Pen(Color.FromArgb((byte)this._alpha, this._color.R, this._color.G, this._color.B));
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
When I place my custom control on a Form and open the Properties Window I can see it as follows :
As you can see I can not set "SelectionSettings" property from the "Properties" window at the design time. What I need is to place the "..." button next to the Property name and open a pop-up to set values.
It should look something like this :
How can I accomplish this task?
What you want to do is add an Editor to AudioMonitor's SelectionSettings property.
To do this, you should create a custom class derived from the UITypeEditor Class.
In order to inherit from UITypeEditor, your project must reference System.Design, which can be done by going to the Project menu, selecting Add Reference to bring up the Reference Manager, navigating to Assemblies->Framework on the left panel and making sure System.Design is checked in the list.
In your custom UITypeEditor derived class, override the method EditValue to bring up a custom dialog that edits a value of type SelectionSettings. Then set the Editor Attribute on your SelectionSettings property to your custom UITypeEditor derived class.
Here's a generic code example of how this would look:
using System;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms;
// ...
[Editor(typeof(SomeProperty_Editor), typeof(UITypeEditor))] // You might be able to place this attribute on class SomeType, but I haven't tried yet
public SomeType SomeProperty
{
get { /* stuff */ }
set { /* stuff */ } // optional, really
}
class SomeProperty_Editor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService service = (IWindowsFormsEditorService)(provider.GetService(typeof(IWindowsFormsEditorService)));
SomeProperty_EditorWindow EditorWindow = new SomeProperty_EditorWindow(value as SomeType);
service.ShowDialog(EditorWindow);
if (EditorWindow.EditCancelled)
return value;
else
return EditorWindow.GetEdittedValue();
}
}
class SomeProperty_EditorWindow : Form
{
public SomeProperty_EditorWindow(SomeType CurrentProperty) : base()
{
InitializeComponents();
// Grab info in CurrentProperty here and display it on form
}
public void InitializeComponents()
{
// write yourself or use designer
}
public SomeType GetEdittedValue()
{
// return editted value from form components
}
public bool EditCancelled = false; // Set true if cancel button hit
}
You may want to look up the PropertyGrid Control as it could be very useful in your EditorWindow. Also, you can go to the UITypeEditor Class's MSDN page, look at all of the .NET derived classes under "Inheritance Hierarchy" to see if there is a built in editor that is close in functionality to what you want to do and inherit directly from that class.
Custom_View.xaml
<UserControl>
<local:Custom_Text_Field
Custom_Text_Field_Color="{x:Bind ViewModel.Color1 , Mode=TwoWay}">
</local:Custom_Text_Field>
<local:Custom_Text_Field
Custom_Text_Field_Color="{x:Bind ViewModel.Color2 , Mode=TwoWay}">
</local:Custom_Text_Field>
<Button Click="{x:Bind ViewModel.ChangeColor"/>
</UserControl>
Custom_View.cs
public sealed partial class Custom_View : UserControl
{
public Custom_View_VM ViewModel { get; set; }
public Custom_View()
{
ViewModel = new Custom_View_VM();
this.InitializeComponent();
}
}
Custom_View_VM.cs
public class Custom_View_VM : NotificationBase
{
public Brush Color1 { get; set; }
public Brush Color2 { get; set; }
public void ChangeColor{//change color1 or color2};
}
I used the NotificationBase class from this example: https://blogs.msdn.microsoft.com/johnshews_blog/2015/09/09/a-minimal-mvvm-uwp-app/
If I affect values for Color1 or Color2 in the constructeur, it work (change the view), but after a call to ChangeColor, values in the View model are changed but it didn't impact the view.
For the UI to update it should receive a PropertyChanged event. You should use NotificationBase's mechanism to set properties which will also raise the PropertyChanged event:
public class Custom_View_VM : NotificationBase
{
private Brush color1;
public Brush Color1
{
get { return color1; }
set { SetProperty(color1, value, () => color1 = value); }
}
// TODO: same here
public Brush Color2 { get; set; }
public void ChangeColor{//change color1 or color2};
}
Also colors don't usually go into ViewModels. The ViewModel should have some business logic property that you can base the color of the TextBox from XAML, like IsNameAvailable.
You need to register the Property.
public static readonly DependencyProperty Custom_Text_Field_Color_Property =
DependencyProperty.Register("Custom_Text_Field_Color", typeof(Brush),
typeof(Class_Name), new UIPropertyMetadata(null));
public Brush Custom_Text_Field_Color
{
get { return (Brush)GetValue(Custom_Text_Field_Color_Property); }
set { SetValue(Custom_Text_Field_Color_Property, value); }
}
Use the Control Name (i.e., Class Name) for typeof(Class_Name).
In you case class NotificationBase is a custom class, you can use is or not.
I only explain the MVVM design pattern in basically.
In ViewModel, it should be implement Interface INotifyPropertyChanged, and when set property, to trigger event PropertyChanged.
public sealed class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(nameof(ProductName)));
}
}
}
}
Under sample will demo this MVVM design pattern.
https://code.msdn.microsoft.com/How-to-achieve-MVVM-design-2bb5a580
Basically I'm creating my own form
public class CryForm : System.Windows.Forms.Form
for several reasons, one of which is a very specific style.
Therefore I want the Form.BackColor property to be 'locked' to Black, so that it cannot be changed from 'outside'
CryForm1.BackColor = Color.whatevercolorulike
should not be possible anymore.
Is there any way to achieve this or should I come up with a completely different solution?
This should work, although you won't get a compile time error when trying to set the property.
public override Color BackColor
{
get { return Color.Black; }
set { }
}
You can make it explicit that changing the BackColor is not supported. It will result in a runtime exception if anything is trying to change it:
public override Color BackColor
{
get { return Color.Black; }
set { throw new NotSupportedException("CryForm doesn't support changing the BackColor"); }
}
If your need to is to 'lock' the background color of the form at design-time, probably the most efficient and least error-prone solution would be to override the BackColor property and mark with an attribute, then inherit from the form in the designer.
You could declare:
public class FixedBackgroundForm : Form
{
protected new static readonly Color DefaultBackColor = Color.Black;
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override Color BackColor
{
get { return base.BackColor; }
set { base.BackColor = value; }
}
public FixedBackgroundForm()
{
this.BackColor = DefaultBackColor
}
}
Which would both set your form background color to Black automatically, and prevent changing of the background color from within the designer.
When you add new forms to your project, inherit from FixedBackgroundForm:
public partial class Form1 : FixedBackgroundForm
{
...
}
If you needed to "fix" the background color to black no matter what, simply use this line for the BackColor setter:
set { base.BackColor = DefaultBackColor; }
Another option is to add this to the form load event:
this.BackColorChanged += (s, e2) =>
{
if (this.BackColor != Color.Black)
this.BackColor = Color.Black;
};
Just add
public new Color BackColor
{
get { return Color.Black; }
}
to your code!
I currently have a custom Render set up for a MenuStrip on a C# windows forms application:
private class HeaderMenuRender : ProfessionalColorTable
{
public override Color MenuItemSelectedGradientBegin
{
get
{
return Color.Gray;
}
}
public override Color MenuItemSelectedGradientEnd
{
get
{
return Color.Gray;
}
}
public override Color MenuItemPressedGradientBegin
{
get
{
return Color.Gray;
}
}
public override Color MenuItemPressedGradientEnd
{
get
{
return Color.Gray;
}
}
public override Color MenuItemBorder
{
get
{
return Color.Gray;
}
}
public override Color MenuBorder
{
get
{
return Color.Gray;
}
}
}
This is then applied to an existing MenuStrip control on a form to create a Custom render.
However when running the application and making a selection from the menu which is displayed, despite the Menu options being the correct colour, there is a small 'White' (arguably could be 'Control' colour) bar which spans the length of the selected MenuStrip option (indicated in the blue box below):
Is there a particular property of the custom Renderer that I am not including or something I am missing which is setting this specific part of the MenuStrip selected item? The examples I have seen elsewhere do not seem to have this issue.
In the above code, it seems you made items Gray using BackColor property.
You can use override ToolStripDropDownBackground to return Color.Gray, this removes that while line.
Also you can overriding ImageMarginGradientBegin, ImageMarginGradientMiddle and ImageMarginGradientEnd to make also image/checkbox area Gray.
Here is What I added to your codes to get the desired value:
public override Color ImageMarginGradientBegin
{
get { return Color.Gray; }
}
public override Color ImageMarginGradientMiddle
{
get { return Color.Gray; }
}
public override Color ImageMarginGradientEnd
{
get { return Color.Gray; }
}
public override Color ToolStripDropDownBackground
{
get { return Color.Gray; }
}
I have a MenuStrip and I want to change it's color. So far, I have this code:
public class TestColorTable : ProfessionalColorTable
{
public override Color MenuItemSelected
{
get{ return Color.LightGray; } // Don't mind the colors...
}
public override Color MenuItemBorder
{
get { return Color.LightGray; }
}
public override Color MenuItemSelectedGradientBegin
{
get { return Color.LightGray; }
}
public override Color MenuItemSelectedGradientEnd
{
get { return Color.LightGray; }
}
public override Color MenuItemPressedGradientBegin
{
get { return Color.DimGray; }
}
public override Color MenuItemPressedGradientEnd
{
get { return Color.DimGray; }
}
public override Color MenuBorder
{
get { return Color.LightGray; }
}
}
With this code, as well as the designer, I managed to change the color of almost every element of my MenuStrip. Almost.
Here are the results:
As you can see, there are two issues: 1) The two separators and 2) That thin white border around the submenus.
Any ideas on how to change the color of those two parts of my MenuStrip?
For the separator color try overriding the SeparatorDark and or SeparatorLight property of the ProfessionalColorTable class.
As for the thin white border around the submenus..., well, its actually not a border. It's the ToolStripDropDown (the submenu) background itself. Try overriding the ToolStripDropDownBackground property of the ProfessionalColorTable class to change its color.
Example:
public class TestColorTable : ProfessionalColorTable
{
...
public override Color SeparatorDark
{
get { return Color.DimGray; }
}
public override Color ToolStripDropDownBackground
{
get { return Color.DimGray; }
}
...
}