I'm trying to create a lazy property with Catel framework. Is there a way to do it?
When I'm creating a property in ViewModel like this:
#region Photos property
/// <summary>
/// Gets or sets the Photos value.
/// </summary>
public FastObservableCollection<Photo> Photos
{
get
{
var temp = GetValue<FastObservableCollection<Photo>>(PhotosProperty);
if (temp == null)
Photos = SelectedPatient.GetPhotos();
return GetValue<FastObservableCollection<Photo>>(PhotosProperty);
}
set { SetValue(PhotosProperty, value); }
}
/// <summary>
/// Photos property data.
/// </summary>
public static readonly PropertyData PhotosProperty = RegisterProperty("Photos", typeof (FastObservableCollection<Photo>));
#endregion
the get function is called even without binding, so my lazy property initializes while ViewModel is initializing.
Is there a way to do it?
There is only 1 way to implement "lazy properties", and that is by using the Lazy<> class. The reason for this is that for some mappings (such as view model to model, etc), Catel uses SetValue directly instead of the property wrapper (compare Catel properties with dependency properties).
Related
In a parent class, I have a collection. In a child class, I want to expose a part of the parent class collection. I want changes from either location to be affect the other.
My real life situation is I am creating a part of an application that will record a database design. I have a ConstraintList collection inside of a Database class. The ConstraintList contains a Constraint class for each constraint in the database. I also have a TablesList collection in the Database class, that contains Table classes. In the Table class I have a ForeignKeyConstraintList where I want to expose the constraints from the parent (Database class) ConstraintList that are foreign key constraints for this Table class.
+-Database Class
|
+--ConstraintList <-----------
| |
+--TableList Same List
| |
+-Table Class |
| |
+-ForeignKeyConstraintList
I have tried using an existing List class from the primary collection and using Linq to filter it to another List collection. However this doesn't work because this makes two List classes. If an entry is removed from the one List it still exists in the other List.
I thought about having the ForeignKeyConstraintList property of the Table class pull directly from the ConstraintList property of the Database class each time it is called but the act of filtering it causes it to create a new List class and thus any entries removed from ForeignKeyConstraintList would not be removed from the ConstraintList.
Another option I came up with so far is creating a new class that implements the same interfaces as List but doesn't subclass from it. Then using a private field to store a reference to the primary List class. Then writing custom Add and Remove methods that sync any changes back to the ConstraintList. I would also need to create a custom implementation of the IEnemerable and IEnumerable to skip items that don't meet the filter criteria.
In a parent class, I have a collection. In a child class, I want to expose a part of the parent class collection. I want changes from either location to be affect the other.
I decided to write a custom List type class to accomplish this. I will post the code below. I haven't tested yet but I figured this would be a good start for anyone else who wants to do the same thing.
hmmmm, seems the class is too large to fit in here. I will just post the key parts and skip the public methods, which just implement the various interfaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace CodeWriter.Collections.Generic
{
/// <summary>
/// This represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort and manipulate the list.
/// This class serves as a wrapper for a <see cref="List{T}"/>. The internal class can be reached by the <see cref="SourceList"/> property.
/// The elements that this class exposes from the <see cref="SourceList"/> can be controlled by changing the <see cref="Filter"/> property.
/// </summary>
/// <typeparam name="T">The type of elements in the list.</typeparam>
/// <remarks>
/// This class was created to support situations where the functionality of two or more <see cref="List{T}"/> collections are needed where one is the Master Collection
/// and the others are Partial Collections. The Master Collection is a <see cref="List{T}"/> and exposes all elements in the collection. The Partial Collections
/// are <see cref="FilteredList{T}"/> classes (this class) and only expose the elements chosen by the <see cref="FilteredList{T}"/> property of this class. When elements are modified,
/// in either type of collection, the changes show up in the other collections because in the backend they are the same list. When elements are added or deleted from the Partial Collections,
/// they will disappear from the Master Collection. When elements are deleted from the Master Collection, they will not be available in the Partial Collection but it
/// may not be apparent because the <see cref="Filter"/> property may not be exposing them.
/// </remarks>
public class FilteredList<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
{
#region Public Constructor
public FilteredList(List<T> SourceList)
{
if (SourceList == null)
{
throw new ArgumentNullException("SourceList");
}
_SourceList = SourceList;
}
public FilteredList()
{
_SourceList = new List<T>();
}
public FilteredList(IEnumerable<T> Collection)
{
if (Collection == null)
{
throw new ArgumentNullException("Collection");
}
_SourceList = new List<T>(Collection);
}
#endregion
#region Protected Members
protected List<T> _SourceList;
protected Func<T, bool> _Filter;
#endregion
#region Public Properties
#region Source List Properties
/// <summary>
/// Gets or sets the base class that this class is a wrapper around.
/// </summary>
public List<T> SourceList
{
get
{
return _SourceList;
}
set
{
_SourceList = value;
}
}
/// <summary>
/// Gets or sets the value used to filter the <see cref="SourceList"/>.
/// </summary>
public Func<T, bool> Filter
{
get
{
return _Filter;
}
set
{
_Filter = value;
}
}
#endregion
#region Normal List<T> Implementation
/// <summary>
/// Provides access to the collection the in the same manner as an <see cref="Array"/>.
/// </summary>
/// <param name="Index">The Index of the element you want to retrieve. Valid values are from zero to the value in the <see cref="Count"/> property.</param>
/// <returns>The element at the position provided with the indexer.</returns>
public T this[int Index]
{
get
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
return Selected[Index];
}
set
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
Selected[Index] = value;
}
}
/// <summary>
/// Provides access to the collection the in the same manner as an <see cref="Array"/>.
/// </summary>
/// <param name="Index">The Index of the element you want to retrieve. Valid values are from zero to the value in the <see cref="Count"/> property.</param>
/// <returns>The element at the position provided with the indexer.</returns>
/// <remarks>This is required for IList implementation.</remarks>
object IList.this[int Index]
{
get
{
return this[Index];
}
set
{
if ((value is T) == false)
{
throw new ArgumentException("Value passed is not a valid type.");
}
this[Index] = (T)value;
}
}
/// <summary>
/// Gets or sets the total number of elements the internal data structure can hold without resizing.
/// </summary>
public int Capacity
{
get
{
return _SourceList.Capacity;
}
set
{
// We cannot let them shrink capacity because this class is a wrapper for the List<T> in the _SourceList property.
// They don't get to see all the entries in that list because it is filtered. Therefore it is not safe for them to shrink capacity.
// We check if they are shrinking the capacity.
if (value >= _SourceList.Capacity)
{
_SourceList.Capacity = value;
}
}
}
/// <summary>
/// Gets the number of elements contained in the <see cref="FilteredList{T}"/>.
/// </summary>
public int Count
{
get
{
List<T> Selected = _SourceList.Where(_Filter).ToList();
return Selected.Count();
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="FilteredList{T}"/> has a fixed size.
/// </summary>
public bool IsFixedSize
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="FilteredList{T}"/> is read-only.
/// </summary>
public bool IsReadOnly
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating whether access to the <see cref="FilteredList{T}"/> is synchronized (thread safe).
/// </summary>
public bool IsSynchronized
{
get
{
return false;
}
}
/// <summary>
/// Gets an object that can be used to synchronize access to the <see cref="FilteredList{T}"/>.
/// </summary>
public object SyncRoot
{
get
{
return _SourceList;
}
}
#endregion
#endregion
}
}
I am creating a self composing UserControl in WPF.
I have an attribute I adorn the properties of my entity with that instructs my SelfComposingUserControl what to do.
If I want a property to be edited by a specific UI Control I pass this in my attribute.
In my attribute, I'm really not sure how to pass which property on the UI Control I would like bound to my entity property.
Can anybody help?
Here is a stripped down version of my UIEditableAttribute:
public class UIEditableAttribute : Attribute
{
/// <summary>
/// UIControl to edit the property
/// </summary>
public Type UIControl { get; set; }
/// <summary>
/// UIControl dependency property that binds to the property
/// </summary>
public DependencyProperty UIControlValueProperty { get; set; }
}
And here is an example of a property using the attribute
private int _numberOfRows;
[UIEditable(DisplayGroup = "B", UIControl = typeof(RadNumericUpDown), UIControlValueProperty = RadNumericUpDown.ValueProperty)]
public int NumberOfRows
{
get { return _numberOfRows; }
set { CheckPropertyChanged(ref _numberOfRows, value); }
}
In the SelfComposingUserControl, I have got around this by using a switch statement on the type of UIControl that binds the property to the correct dependency property. I would however like to specify at the property level.
Many thanks in advance.
The answer is to pass the UIControlValueProperty as a string i.e. "ValueProperty" in the attribute, then get this useful function;
Get Dependency Property By Name
to get the dependency property from the type or object.
This is a pretty long question, so please bear with me.
Currently I am developing a small tool intended to help me keep track of the myriad of characters in my Stories.
The tool does the following:
Load the characters which are currently stored as json on the disk and stores them in a list, which is presented in the Shell via a ListBox.
If the user then opens a character the Shell, which is a Conductor<Screen>.Collection.OneActive, opens a new CharacterViewModel, that derives from Screen.
The Character gets the Character that is going to be opened via the IEventAggregator message system.
The CharacterViewModel furthermore has various properties which are sub ViewModels which bind to various sub Views.
And here is my Problem:
Currently I initialize the sub ViewModels manually when the ChracterViewModel is initialized. But this sounds fishy to me and I am pretty sure there is a better way to do this, but I cannot see how I should do it.
Here is the code of the CharacterViewModel:
/// <summary>ViewModel for the character view.</summary>
public class CharacterViewModel : Screen, IHandle<DataMessage<ICharacterTagsService>>
{
// --------------------------------------------------------------------------------------------------------------------
// Fields
// -------------------------------------------------------------------------------------------------------------------
/// <summary>The event aggregator.</summary>
private readonly IEventAggregator eventAggregator;
/// <summary>The character tags service.</summary>
private ICharacterTagsService characterTagsService;
// --------------------------------------------------------------------------------------------------------------------
// Constructors & Destructors
// -------------------------------------------------------------------------------------------------------------------
/// <summary>Initializes a new instance of the <see cref="CharacterViewModel"/> class.</summary>
public CharacterViewModel()
{
if (Execute.InDesignMode)
{
this.CharacterGeneralViewModel = new CharacterGeneralViewModel();
this.CharacterMetadataViewModel = new CharacterMetadataViewModel();
}
}
/// <summary>Initializes a new instance of the <see cref="CharacterViewModel"/> class.</summary>
/// <param name="eventAggregator">The event aggregator.</param>
[ImportingConstructor]
public CharacterViewModel(IEventAggregator eventAggregator)
: this()
{
this.eventAggregator = eventAggregator;
this.eventAggregator.Subscribe(this);
}
// --------------------------------------------------------------------------------------------------------------------
// Properties
// -------------------------------------------------------------------------------------------------------------------
/// <summary>Gets or sets the character.</summary>
public Character Character { get; set; }
/// <summary>Gets or sets the character general view model.</summary>
public CharacterGeneralViewModel CharacterGeneralViewModel { get; set; }
/// <summary>Gets or sets the character metadata view model.</summary>
public CharacterMetadataViewModel CharacterMetadataViewModel { get; set; }
/// <summary>Gets or sets the character characteristics view model.</summary>
public CharacterApperanceViewModel CharacterCharacteristicsViewModel { get; set; }
/// <summary>Gets or sets the character family view model.</summary>
public CharacterFamilyViewModel CharacterFamilyViewModel { get; set; }
// --------------------------------------------------------------------------------------------------------------------
// Methods
// -------------------------------------------------------------------------------------------------------------------
/// <summary>Saves a character to the file system as a json file.</summary>
public void SaveCharacter()
{
ICharacterSaveService saveService = new JsonCharacterSaveService(Constants.CharacterSavePathMyDocuments);
saveService.SaveCharacter(this.Character);
this.characterTagsService.AddTags(this.Character.Metadata.Tags);
this.characterTagsService.SaveTags();
}
/// <summary>Called when initializing.</summary>
protected override void OnInitialize()
{
this.CharacterGeneralViewModel = new CharacterGeneralViewModel(this.eventAggregator);
this.CharacterMetadataViewModel = new CharacterMetadataViewModel(this.eventAggregator, this.Character);
this.CharacterCharacteristicsViewModel = new CharacterApperanceViewModel(this.eventAggregator, this.Character);
this.CharacterFamilyViewModel = new CharacterFamilyViewModel(this.eventAggregator);
this.eventAggregator.PublishOnUIThread(new CharacterMessage
{
Data = this.Character
});
base.OnInitialize();
}
/// <summary>
/// Handles the message.
/// </summary>
/// <param name="message">The message.</param>
public void Handle(DataMessage<ICharacterTagsService> message)
{
this.characterTagsService = message.Data;
}
}
For Completion Sake I also give you one of the sub ViewModels. The others a of no importance because they are structured the same way, just perform different tasks.
/// <summary>The character metadata view model.</summary>
public class CharacterMetadataViewModel : Screen
{
/// <summary>The event aggregator.</summary>
private readonly IEventAggregator eventAggregator;
/// <summary>Initializes a new instance of the <see cref="CharacterMetadataViewModel"/> class.</summary>
public CharacterMetadataViewModel()
{
if (Execute.InDesignMode)
{
this.Character = DesignData.LoadSampleCharacter();
}
}
/// <summary>Initializes a new instance of the <see cref="CharacterMetadataViewModel"/> class.</summary>
/// <param name="eventAggregator">The event aggregator.</param>
/// <param name="character">The character.</param>
public CharacterMetadataViewModel(IEventAggregator eventAggregator, Character character)
{
this.Character = character;
this.eventAggregator = eventAggregator;
this.eventAggregator.Subscribe(this);
}
/// <summary>Gets or sets the character.</summary>
public Character Character { get; set; }
/// <summary>
/// Gets or sets the characters tags.
/// </summary>
public string Tags
{
get
{
return string.Join("; ", this.Character.Metadata.Tags);
}
set
{
char[] delimiters = { ',', ';', ' ' };
List<string> tags = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries).ToList();
this.Character.Metadata.Tags = tags;
this.NotifyOfPropertyChange(() => this.Tags);
}
}
}
I already read in on Screens, Conductors and Composition, IResult and Coroutines and skimmed the rest of the Documentation, but somehow I cannot find what I am looking for.
//edit: I should mention the code I have works just fine. I'm just not satisfied with it, since I think I am not understanding the concept of MVVM quite right and therefore make faulty code.
There is nothing wrong with having one ViewModel instantiate several child ViewModels. If you're building a larger or more complex application, it's pretty much unavoidable if you want to keep your code readable and maintainable.
In your example, you are instantiating all four child ViewModels whenever you create an instance of CharacterViewModel. Each of the child ViewModels takes IEventAggregator as a dependency. I would suggest that you treat those four child ViewModels as dependencies of the primary CharacterViewModel and import them through the constructor:
[ImportingConstructor]
public CharacterViewModel(IEventAggregator eventAggregator,
CharacterGeneralViewModel generalViewModel,
CharacterMetadataViewModel metadataViewModel,
CharacterAppearanceViewModel appearanceViewModel,
CharacterFamilyViewModel familyViewModel)
{
this.eventAggregator = eventAggregator;
this.CharacterGeneralViewModel generalViewModel;
this.CharacterMetadataViewModel = metadataViewModel;
this.CharacterCharacteristicsViewModel = apperanceViewModel;
this.CharacterFamilyViewModel = familyViewModel;
this.eventAggregator.Subscribe(this);
}
You can thus make the setters on the child ViewModel properties private.
Change your child ViewModels to import IEventAggregator through constructor injection:
[ImportingConstructor]
public CharacterGeneralViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
}
In your example, two of those child ViewModels are passed an instance of the Character data in their constructors, implying a dependency. In these cases, I would give each child ViewModel a public Initialize() method where you set the Character data and activate the event aggregator subscription there:
public Initialize(Character character)
{
this.Character = character;
this.eventAggregator.Subscribe(this);
}
Then call this method in your CharacterViewModel OnInitialize() method:
protected override void OnInitialize()
{
this.CharacterMetadataViewModel.Initialize(this.Character);
this.CharacterCharacteristicsViewModel.Initialize(this.Character);
this.eventAggregator.PublishOnUIThread(new CharacterMessage
{
Data = this.Character
});
base.OnInitialize();
}
For the child ViewModels where you're only updating the Character data through the EventAggregator, leave the this.eventAggregator.Subscribe(this) call in the constructor.
If any of your child ViewModels are not actually required for the page to function, you could initialize those VM properties via property import:
[Import]
public CharacterGeneralViewModel CharacterGeneralViewModel { get; set; }
Property imports don't occur until after the constructor has completed running.
I would also suggest handling the instantiation of ICharacterSaveService through constructor injection as well, rather than explicitly creating a new instance every time you save data.
The primary purpose of MVVM was to allow front-end designers to work on the layout of the UI in a visual tool (Expression Blend) and coders to implement the behavior and business without interfering with one another. The ViewModel exposes data to be bound to the view, describes the view's behavior at an abstract level, and frequently acts as a mediator to the back-end services.
There is no one "correct" way to do it, and there are situations where it isn't the best solution. There are times when the best solution is to toss the extra layer of abstraction of using a ViewModel and just write some code-behind. So while it's a great structure for your application as a whole, don't fall into the trap of forcing everything to fit into the MVVM pattern. If you have a few more graphically complex user controls where it simply works better to have some code-behind, then that's what you should do.
I am tring to extend an existing microsoft control called the PivotViewer.
This control has an existing property that I want to expose to my ViewModel.
public ICollection<string> InScopeItemIds { get; }
I have created an inherited class called CustomPivotViewer and I want to create a Dependency Property that I can bind to that will expose the values held in InScopeItemIds in the base class.
I have spent a fair while reading up about DependencyPropertys and am becomming quite disheartened.
Is this even possible?
You only need a DependencyProperty is you want it to be bindable, meaning: if you want to have, for example, a MyBindableProperty property in your control, with which you want to be able to do:
MyBindableProperty={Binding SomeProperty}
if, however, you want other DependencyProperties to bind to it, any property (either a DependencyProperty or a normal one) can be used.
I'm not sure what you really need, maybe you can clarify more, but if it's the first scenario that you want to implement, you can do it as follows:
create a DependencyProperty, let's call it BindableInScopeItemIds, like so:
/// <summary>
/// BindableInScopeItemIds Dependency Property
/// </summary>
public static readonly DependencyProperty BindableInScopeItemIdsProperty =
DependencyProperty.Register("BindableInScopeItemIds", typeof(ICollection<string>), typeof(CustomPivotViewer),
new PropertyMetadata(null,
new PropertyChangedCallback(OnBindableInScopeItemIdsChanged)));
/// <summary>
/// Gets or sets the BindableInScopeItemIds property. This dependency property
/// indicates ....
/// </summary>
public ICollection<string> BindableInScopeItemIds
{
get { return (ICollection<string>)GetValue(BindableInScopeItemIdsProperty); }
set { SetValue(BindableInScopeItemIdsProperty, value); }
}
/// <summary>
/// Handles changes to the BindableInScopeItemIds property.
/// </summary>
private static void OnBindableInScopeItemIdsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (CustomPivotViewer)d;
ICollection<string> oldBindableInScopeItemIds = (ICollection<string>)e.OldValue;
ICollection<string> newBindableInScopeItemIds = target.BindableInScopeItemIds;
target.OnBindableInScopeItemIdsChanged(oldBindableInScopeItemIds, newBindableInScopeItemIds);
}
/// <summary>
/// Provides derived classes an opportunity to handle changes to the BindableInScopeItemIds property.
/// </summary>
protected virtual void OnBindableInScopeItemIdsChanged(ICollection<string> oldBindableInScopeItemIds, ICollection<string> newBindableInScopeItemIds)
{
}
in the OnBindableInScopeItemIdsChanged, you can update the inner collection (InScopeItemIds)
remember that the property you want to expose is read-only (it has no "setter"), so you might need to update it as so:
protected virtual void OnBindableInScopeItemIdsChanged(ICollection<string> oldBindableInScopeItemIds, ICollection<string> newBindableInScopeItemIds)
{
InScopeItemIds.Clear();
foreach (var itemId in newBindableInScopeItemIds)
{
InScopeItemIds.Add(itemId);
}
}
Hope this helps :)
EDIT:
I realized misunderstandings and here is a new version (in the context of the original question):
So, you can use the property you need for the binding, with following circumstances having in mind:
as this property is read-only, you will not be able to use it for 2-way binding.
as far as the containing type does not implement INotifyPropertyChanged, your target control used to display the data will not be notified about the changes to the property value.
as far as the returned by this property value does not implement INotifyCollectionChanged (one example is ObservableCollection<T>), the changes to the collection will not be affected on the target control which is used to display it.
What is the correct syntax for an XML comment for a class property?
In response to a request for explicit examples, the following extract is from StyleCop help document, "SA1623: PropertySummaryDocumentationMustMatchAccessors":
The property’s summary text must begin with wording describing the types of accessors exposed within the property. If the property contains only a get accessor, the summary must begin with the word “Gets”. If the property contains only a set accessor, the summary must begin with the word “Sets”. If the property exposes both a get and set accessor, the summary text must begin with “Gets or sets”.
For example, consider the following property, which exposes both a get and set accessor. The summary text begins with the words “Gets or sets”.
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
set { this.name = value; }
}
If the property returns a Boolean value, an additional rule is applied. The summary text for Boolean properties must contain the words “Gets a value indicating whether”, “Sets a value indicating whether”, or “Gets or sets a value indicating whether”. For example, consider the following Boolean property, which only exposes a get accessor:
/// <summary>
/// Gets a value indicating whether the item is enabled.
/// </summary>
public bool Enabled
{
get { return this.enabled; }
}
In some situations, the set accessor for a property can have more restricted access than the get accessor. For example:
/// <summary>
/// Gets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
private set { this.name = value; }
}
In this example, the set accessor has been given private access, meaning that it can only be accessed by local members of the class in which it is contained. The get accessor, however, inherits its access from the parent property, thus it can be accessed by any caller, since the property has public access.
In this case, the documentation summary text should avoid referring to the set accessor, since it is not visible to external callers.
StyleCop applies a series of rules to determine when the set accessor should be referenced in the property’s summary documentation. In general, these rules require the set accessor to be referenced whenever it is visible to the same set of callers as the get accessor, or whenever it is visible to external classes or inheriting classes.
The specific rules for determining whether to include the set accessor in the property’s summary documentation are:
1.The set accessor has the same access level as the get accessor. For example:
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
protected string Name
{
get { return this.name; }
set { this.name = value; }
}
2.The property is only internally accessible within the assembly, and the set accessor also has internal access. For example:
internal class Class1
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
protected string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
internal class Class1
{
public class Class2
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
}
3.The property is private or is contained beneath a private class, and the set accessor has any access modifier other than private. In the example below, the access modifier declared on the set accessor has no meaning, since the set accessor is contained within a private class and thus cannot be seen by other classes outside of Class1. This effectively gives the set accessor the same access level as the get accessor.
public class Class1
{
private class Class2
{
public class Class3
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
public string Name
{
get { return this.name; }
internal set { this.name = value; }
}
}
}
}
4.Whenever the set accessor has protected or protected internal access, it should be referenced in the documentation. A protected or protected internal set accessor can always been seen by a class inheriting from the class containing the property.
internal class Class1
{
public class Class2
{
/// <summary>
/// Gets or sets the name of the customer.
/// </summary>
internal string Name
{
get { return this.name; }
protected set { this.name = value; }
}
}
private class Class3 : Class2
{
public Class3(string name) { this.Name = name; }
}
}
Install this: http://submain.com/products/ghostdoc.aspx
Right-click on the property, select 'Document This'.
Fill in the blanks.
According to MSDN, link, it appears there isn't an official tag for Class Properties. But, I would use something like this:
/// <summary>Here is an example of a propertylist:
/// <list type="Properties">
/// <item>
/// <description>Property 1.</description>
/// </item>
/// <item>
/// <description>Property 2.</description>
/// </item>
/// </list>
/// </summary>
I'd suggest to use StyleCop. It does not only enforce (a bit to strong for my taste) you to comment, but also gives you a hint how the comments should be startet.
According to C# documentation the keyword <value> is used to desrcibe properties.
Implementation would look like this:
/// <value>Description goes here.</value>
public string MyProperty { get; set; }
source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments#d319-value
Using <summary> should work too and even though I'm still learning C# it seems the documentation is more of a recommendation than a PEP8-like guide. So I think as long as one's code is consistent it comes down to personal preference and/or company guidelines.