I am trying to implement a Custom Composite WebControl with "nested" properties, i.e., encapsulate a group of properties into a class.
For example, in this composite control, I have placed a button. I would like to be able to encapsulate relevant properties for the button into a class (e.g., buttonText, buttonStyle, etc.). This would make defining properties in multi-button/controls composite-control easier and consistent and intuitive.
Note: I would like for the encapsulated properties to appear grouped in the Properties dialog in VisualStudio, in a manner very similar to Style/Font.
Sample:
public class fooButtonProperties
{
[Category("Appearance"), Description("URL for the Profile page")]
public string URL { get; set; }
[Category("Appearance"), Description("Text to display"), DefaultValue("Profile")]
public string ButtonText { get; set; }
/// <summary>
/// Position of the control on the page, default is Right-Aligned
/// </summary>
[Category("Appearance"), Description("Position in the Header"), DefaultValue(PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions.Right)]
///Here is the composite control
public PIONEERFramework.Web.UI.WebControls.PageHeaderFooter.Classes.DesignEnum.DesignLayoutEnums.HorizontalPositions PositionInHeader { get; set; }
}
public class myCustomClass: System.Web.UI.WebControls.CompositeControl
{
protected System.Web.UI.HtmlControls.HtmlLink myButton;
[Category("Appearance")]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
private fooButtonProperties _myeButtonProp;
#region Constructor
public myCustomClass()
{
this._myeButtonProp = new fooButtonProperties ();
}
#endregion
}
Unfortunately, this approach dos not work. The new property myButtonProperties does not appear at all in the "Properies" dialog.
To create a nested property use the System.ComponentModel.DesignerSerializationVisibility attribute in your control like this:
[Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public fooButtonProperties myButtonProperties { get { return _profileButtonProp; } }
The final property name will be "myButtonProperties-URL" (with a hyphen). You can also add this attribute to properties in your fooButtonProperties class for even more nesting.
Please note that you may have to close the aspx file and rebuild the solution to refresh the Properties window.
The Category attribute works in your control and in your nested class.
The Description attribute for the descriptions seems correct BUT it does not work which could be a bug in Visual Studio. I found this link:
https://www.beta.microsoft.com/VisualStudio/feedback/details/653335/webcontrol-property-descriptions-do-not-appear-in-property-window
Also I observed that no properties show descriptions.
Regards
Oli
Related
I have a view that has a group of images I get from a web service
I receive them in a list of this class:
public class ImageModel
{
public int Id { get; set; }
public string Name { get; set; }
public string imageUrl { get; set; }
}
under each image I show an up-vote button, so I added another bool property to the model above:
public bool UpVoted { get; set; }
the ListView that shows these images is bound to an ObservableCollection<ImageModel > , I want to change the voting icon through a converter that convert the value of UpVoted to the corresponding icon, when the user click the voting icon: a command execute this method:
private void OnVoting(ImageModel image)
{
Images.Single(x => x.id == image.id).UpVoted = !image.UpVoted;
}
the problem is that the UI is not updated, and to make sure that I understood the problem I turned the model to a View model and made the required changes to the UpVoted property (I'm using MVVM light library)
bool upVoted;
public bool UpVoted
{
get { return upVoted; }
set
{
Set(ref upVoted, value);
}
}
and it works now,
so I need to bind the UpVoted to the UI, so it's updated whenever it changed
first
your model class must inherit from MvxNotifyPropertyChanged
public class ImageModel : MvxNotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
private bool upVoted ;
public bool UpVoted
{
get { return upVoted ; }
set { upVoted = value; RaisePropertyChanged(() => UpVoted ); }
}
}
then with MvxValueConverter you ready to go
Mustafa's answer mentions a class that is specific to MvvmCross library.
Another alternative is TinyMvvm.
If you wish to write your own MVVM (or understand how MVVM works),
the general pattern is to implement INotifyPropertyChanged: Implement Property Change Notification, which I discuss here.
A convenient way to implement INotifyPropertyChanged, is to make a base class that does that implementation, then inherit from that base class. You can use the code in that sample as your base class. Or use a slightly different implementation, that avoids having to manually pass the property name as a string:
using System.ComponentModel;
using System.Runtime.CompilerServices;
// Use this as base class for all your "view model" classes.
// And possibly for your (domain) model classes.
// E.g.: "public class MyLoginViewModel : HasNotifyPropertyChanged".
// OR "public class MyLoginModel : HasNotifyPropertyChanged".
// Give it whatever name you want, for ViewModels I suggest "ViewModelBase".
public class HasNotifyPropertyChanged : INotifyPropertyChanged
{
// --- This is pattern to use to implement each property. ---
// This works for any property type: int, Color, etc.
// What's different from a standard c# property, is the "SetProperty" call.
// You will often write an IValueConverter (elsewhere) to use in XAML to convert from string to your property type,
// or from your property type to a type needed in your UI.
// Comment out this example property if you don't need it.
/// <summary>
/// Set to "true" at end of your initialization.
/// Then can use Property Trigger on Ready value=true in XAML to do something when your instance is ready for use.
/// For example, load something from web, then trigger to update UI.
/// </summary>
private bool _ready;
public bool Ready
{
get => _ready;
set => SetProperty(ref _ready, value);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (property == null || !property.Equals(value))
{
property = value;
RaisePropertyChanged(propertyName);
}
}
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Again, an alternative to the above code is to use an existing MVVM library.
For another alternative, that doesn't require writing "SetProperty(..)" or "OnPropertyChanged(..)" in all of your property setters, google for info about using Fody/PropertyChanged. Then you wouldn't need any of the above code; your class would simply inherit from INotifyPropertyChanged. (And in app startup, you call a method that "injects" the needed logic into all properties of all INotifyPropertyChanged classes.)
Acknowledgement: The code pattern in example above is based on one of the open source libraries. It might be from TinyMvvm.
you do not say which sort of container that you are using but not all controls are set to support two way notification by default. so you may have to add a
Mode=TwoWay
to get notifications from the back end that data has changed. Or as the previous answer by Mustafa indicated you may need to verify that your class is implementing the InotifyPropertyChanged event with mvvm light.
I have prepared the simple sample(WindowsForms) with custom control(ButtonAdv) which derived from Microsoft Button control.And implemented CodedUI Extension support for that custom control with my custom properties(BackColor and ForeColor,etc) by refferring the below blog,
https://blogs.msdn.microsoft.com/devops/2011/10/28/coded-ui-test-extension-for-3rd-party-windows-forms-controlshow-to/
All is working fine, when asserting my custom control the custom properties are added properly in CodedUITestBuilder property window.
Problem:
When i try to assert the Microsoft Button, there also my custom properties(BackColor,ForeColor) are displayed with empty values in CodedUITestBuilder property window.
I don't know why it is. Please anyone suggest me how to avoid of asserting custom properties for MicroSoft controls?
Here is minimum code of my implementation,
public class SfButtonAdv1:WinControl
{
public SfButtonAdv1(UITestControl control)
: base(control)
{
SearchProperties.Add(UITestControl.PropertyNames.ControlType, ControlType.Button.Name);
}
new public abstract class PropertyNames : WinControl.PropertyNames
{
public static readonly string BackColor = "BackColor";
public static readonly string ForeColor = "ForeColor";
}
}
public class SfButtonAdv : Button
{
public SfButtonAdv()
{
}
protected override AccessibleObject CreateAccessibilityInstance()
{
return new SfButtonAccessiblity(this);
}
}
public class SfButtonAccessiblity : ControlAccessibleObject
{
//
public override string Description
{
get
{
return this.sfButtonAdv.Style.BackColor.Name
+ "," + this.sfButtonAdv.Style.ForeColor.Name
}
}
}
Please let me know if you need any details further,
Thanks in Advance,
I have a simple interface like this:
interface IToolbarMaster {
ToolBar Toolbar { get; }
}
I added this Interface to my MasterPage and moved the field declaration of the Toolbar-Control to the Code Behind File:
public partial class Layout : ctrls.MasterPage, IToolbarMaster {
public global::Some.Web.Controls.ToolBar Toolbar;
(...)
}
My pages need to access this Toolbar Control via the Interface (we have a .FindMaster<IToolbarMaster>() Method) - but the code says, Layout doesn't implement IToolbarMaster
I could implement a property like
public ToolBar ToolBar { get { return this.Toolbar; } } // Different name spelling
But this looks really odd in my opinion.
I ran into a similar issue in VB using an auto property with a default value.
Public Property MessagePlaceHolder() As PlaceHolder = Me.phMessage
Apparently that doesn't work. I had to create the property the traditional way.
Public ReadOnly Property MessagePlaceHolder() As PlaceHolder
Get
Return Me.phMessage
End Get
End Property
Wow, I already got the answer, I was very close.
I tried this
public global::Some.Web.Controls.ToolBar Toolbar { get; }
but it didn't work. But as I added the setter everything was fine:
public global::Some.Web.Controls.ToolBar Toolbar { get; set; }
I'm leaving this for future reference, if anyone has this problem too.
I have a user control that contains a number of panel controls. The user control has logic to set the visibility of each panel, based on the values of some variables contained in the user control.
I would like to enhance this functionality by adding the following enumeration:
public enum Visibility
{
Default,
Always,
Never
}
where Default would use the current logic to set the panel's visibility, but Always would (obviously) make the panel always visible, and Never would always hide the panel.
I would also like to add a "nested" design-time property named something like PanelVisiblity that the developer could expand, and see the panel names listed below, and allow them to assign a value from the Visibility enumeration for each.
If I add properties like this:
public partial class MyControl : UserControl
{
public Visibility ActivePanel { get; set; }
public Visibility CodePanel { get; set; }
}
the ActivePanel and CodePanel properties appear in the Properties window in Visual Studio, and I can set them to one of the three values in the enum. However, when I refactored my code to try to create a nested property like this:
public partial class MyControl : UserControl
{
public class PanelVisibility
{
public enum Visibility
{
Default,
Always,
Never
}
public Visibility ActivePanel { get; set; }
public Visibility CodePanel { get; set; }
}
}
I do not get an expandable PanelVisiblity property, I get a disabled Visiblity property, where the value is the namespace of class, plus 'PanelVisibility'.
I have Googled a number of variations in phrasing this search, but have not been able to find anything like what I'm looking for.
Any direction would be greatly appreciated.
It seems that you are looking for the same functionality as a DockPanel offers.
I'd suggest you that instead of re-inventing the wheel you should use one the available implementations to you such as :
http://msdn.microsoft.com/en-us/library/system.windows.controls.dockpanel.aspx
http://dockpanel2012.codeplex.com/
https://github.com/dockpanelsuite/dockpanelsuite
I am creating a graphing interface and would like to give the user the option to edit the graphs look, i.e. Series color, BackColor, Data point size, etc... The chart is being created using
System.Windows.Forms.DataVisualization.Charting
To allow the user to edit those options I've placed a PropertyGrid in the form. However there are some properties I do not want the user to have access to. I would like to be able to set up a chart in my form then create a propertygrid that is connected to that chart but have certain properties removed from the grid.
What I have so far tried is ...
public partial class Form1: Form
{
PropertyGrid propG1 = new PropertyGrid();
this.Controls.Add(propG1);
//... There is code here where my chart(chart1) is being populated with data
private void toolStripButton1_Click(object sender, EventArgs e)// The button is just to test
MyChart myC = new MyChart();
propG1.SelectedObject = myC;
}
So based on the recomendations I've received so far I've created a class called MyChart which contains the properties that I don't want to be displayed on my chart.
using System.ComponentModel
//...
public class MyChart : Chart
{
[Browsable(false)]
public new System.Drawing.Color Property
{
get{return BackColor;} // BackColor is just an example not necessarily a property I'd like to remove
set{base.BackColor = value;}
}
I haven't been able to remove the properties from my grid nor have I been able to connect myC with my chart1 so when a property is changed in the grid chart1 is affected. Thanks for the continuing help.
Instead of modifying the PropertyGrid component and its behavior you can modify the objects that you display with attributes. Something like this:
[Browsable(false)]
public object SomeProperty
{
}
Don't forget to add:
using System.ComponentModel;
And to override inherited properties and hide them from the propertyGrid you could do something like:
public class Chart : BaseChart
{
[Browsable(false)]
public new string BaseString // notice the new keyword!
{
get { return base.BaseString; } // notice the base keyword!
set { base.BaseString = value; }
}
// etc.
}
public class BaseChart
{
public string BaseString { get; set; }
}
Setting the Browsable attribute to false will keep SomeProperty from showing up in the PropertyGrid.
So, in a hypothetical chart class like the one below, you will see the chart instance, the SomeProperty1 property but not SomeProperty2.
public class Chart
{
public object Property1 { get; set; }
[Browsable(false)]
public object Property2 { get; set; }
// etc.
}
See Getting the most out of your property grid for more information. And here is a very, very good deep-dive into customizing the PropertyGrid control that will blow your mind. ;-)
And, even more fun with attributes and the PropertyGrid:
[DefaultPropertyAttribute("Property1")]
public class Chart
{
[CategoryAttribute("My Properties"),
DescriptionAttribute("My demo property int"),
DefaultValueAttribute(10)]
public int Property1 { get; set; }
[Browsable(false)]
public object Property2 { get; set; }
[CategoryAttribute("My Properties"),
DescriptionAttribute("My demo property string"),
DefaultValueAttribute("Hello World!")]
public string Property3 { get; set; }
// etc.
}