using a Code Snippet for INotifyPropertyChanged
Asked Answered
D

5

10

I found this code snippet for INotifyPropertyChanged

But it shows the code like this :

INotifyPropertyChanged

I would have this :

  1. for public : capital letter for the first letter + ...

  2. for private : underscore + small letter for the first letter + ...

How can I achieve this ?

Edit : Without having to type the public and the private fields

<Snippet>
    <Declarations>
        <Literal>
            <ID>type</ID>
            <ToolTip>Property type</ToolTip>
            <Default>string</Default>
        </Literal>
        <Literal>
            <ID>property</ID>
            <ToolTip>Property name</ToolTip>
            <Default>MyProperty</Default>
        </Literal>
        <Literal>
            <ID>notifyMethod</ID>
            <ToolTip>name of method to raise PropertyChanged event</ToolTip>
            <Default>NotifyPropertyChanged</Default>
        </Literal>
    </Declarations>
    <Code Language="csharp">
        <![CDATA[private $type$ _$property$;
            public $type$ $property$
            {
                get { return _$property$;}
                set 
                { 
                    if (value != _$property$)
                    {
                        _$property$ = value;
                        $notifyMethod$("$property$");
                    }
                }
            }
        $end$]]>
    </Code>
</Snippet>
Deeply answered 22/10, 2013 at 9:48 Comment(6)
is this the same as this: code.msdn.microsoft.com/wpapps/…?Encore
Nope, this one : geekswithblogs.net/brians/archive/2010/07/27/…Deeply
#165145Encore
Related: We are using PropertyChanged.Fody. It allows you to use auto properties with INotifyPropertyChanged which massively reduces the amount of code needed.Embrangle
@Aschratt hehe , just suggested Fody on a different answer :)Bitch
@WassimAZIRAR I know this is an old question, and you may have long found an answer that is sufficient for your use case. However, my answer does provide the necessary modifications to your snippet that easily fulfills all of your requirements.Rewire
G
7

I don't think this can be done with native code snippets feature provided by Visual Studio.

Personally I use Resharper which makes it possible. It can turn code I write like

public string Name { get; set; }

into

private string _name;
public string Name
{
    get { return _name; }
    set
    {
        if(value == _name)
            return;
        _name = value;
        OnPropertyChanged("Name");
    }
}

It even generates the OnPropertyChanged() method for you.

Gaskins answered 13/11, 2013 at 15:40 Comment(9)
I have Resharper, but how you do that ?Deeply
@Schneider This feature was introduced in Resharper 7. Just put your cursor on a property of a class which implements INotifyPropertyChanged, you should see the context action menu. See here for details.Gaskins
I considered your answer, because it's the closest one to my questionDeeply
@Schneider Thanks! BTW Resharper can also generate the code using [CallerMemberName] for .NET 4.5 projects, which I don't think quite necessary since Resharper helps rename the string when you rename the property.Gaskins
for some reason, my Resharper doesn't catch that. My class derives from class that implements INPC. Version is 8.2.0.2160Legere
This is NOT the answer to this question. no need for Resharper. See the answer below from Akash for the solution: https://mcmap.net/q/1016822/-using-a-code-snippet-for-inotifypropertychangedSnipes
@AlexSorokoletov, same for me. My resharper (8.2) also doesn't seem to catch that. Were you able to resolve the issue?Whitecap
@AlexSorokoletov - Annotate the method which implements the property change, [Annotations.NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){... }Snakebird
@Whitecap - Annotate the method which implements the property change, [Annotations.NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){... }Snakebird
J
16

snippets can be written in xml and can be made for any language in vs

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>Notify Property Changed Method</Title>
    <Author>Akash</Author>
    <Shortcut>npcm</Shortcut>
    <Description>This method implements the OnPropertyChanged method and binds to the event handler</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>

    <Code Language="CSharp">
      <![CDATA[#region Notify Property Changed Members
  public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if(handler!=null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }    
        #endregion]]>
    </Code>
  </Snippet>
</CodeSnippet>

this is the code to automatically generate the notify property changed method . all you need to do is to save this in a single file with the extension as snippet in your Documents/VisulaStudio(YourVersion)/Code Snippets/Visual C#/

that's it you are ready to use it ...

Now,observe the code snippet there is shortcut tag .. this tag references the tag that you should use in vs while writing to activate the snippet ..

here is the code for the property itself:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <Header>
    <Title>Notifiable Property</Title>
    <Author>Akash</Author>
    <Shortcut>nprop</Shortcut>
    <Description>Property With in Built Property Changed method implementation.</Description>
    <SnippetTypes>
      <SnippetType>SurroundsWith</SnippetType>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>Type</ID>
        <Default>string</Default>
      </Literal>
      <Literal>
        <ID>Property</ID>
        <Default>PlaceHolder</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[private $Type$ _$Property$;
        public $Type$ $Property$
        {
            get { return _$Property$; }
            set { 
               if(value!=null || value != _$Property$) _$Property$ = value;
               OnPropertyChanged("$Property$");
            }
        }]]>
    </Code>
  </Snippet>
</CodeSnippet>

in this particular snippet all you need to do is type nprop and press tab tab it generates the requied code .. you only need to enter the datatype and the name .. the rest is taken care of by the snippet itself ...

Though this i a better solution and greatly improves the coding speed, this is suitable for small projects only the viewmodelbase method is suitable for larger projects.

Jarad answered 18/6, 2015 at 15:40 Comment(4)
Years later but definitely the best answer. No need for ResharperTreacherous
null checks should only be done for reference types.Envelope
@Envelope @Akash_Chowdary I updated it to use default($Type$) instead of nullThackeray
Still, the field will start with capital letter. I think the author (and me too) would want the field with camelCase. In that scenario you are setting the field to have the CamelCase the same as the property just putting an _ before.Ironbark
G
7

I don't think this can be done with native code snippets feature provided by Visual Studio.

Personally I use Resharper which makes it possible. It can turn code I write like

public string Name { get; set; }

into

private string _name;
public string Name
{
    get { return _name; }
    set
    {
        if(value == _name)
            return;
        _name = value;
        OnPropertyChanged("Name");
    }
}

It even generates the OnPropertyChanged() method for you.

Gaskins answered 13/11, 2013 at 15:40 Comment(9)
I have Resharper, but how you do that ?Deeply
@Schneider This feature was introduced in Resharper 7. Just put your cursor on a property of a class which implements INotifyPropertyChanged, you should see the context action menu. See here for details.Gaskins
I considered your answer, because it's the closest one to my questionDeeply
@Schneider Thanks! BTW Resharper can also generate the code using [CallerMemberName] for .NET 4.5 projects, which I don't think quite necessary since Resharper helps rename the string when you rename the property.Gaskins
for some reason, my Resharper doesn't catch that. My class derives from class that implements INPC. Version is 8.2.0.2160Legere
This is NOT the answer to this question. no need for Resharper. See the answer below from Akash for the solution: https://mcmap.net/q/1016822/-using-a-code-snippet-for-inotifypropertychangedSnipes
@AlexSorokoletov, same for me. My resharper (8.2) also doesn't seem to catch that. Were you able to resolve the issue?Whitecap
@AlexSorokoletov - Annotate the method which implements the property change, [Annotations.NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){... }Snakebird
@Whitecap - Annotate the method which implements the property change, [Annotations.NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null){... }Snakebird
R
4

It's surprising to me that the following suggestion hasn't been made. I took your initial snippet (from the originating author's page) and made the following modifications. You should be able to copy-and-paste this into your own snippet file.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propn</Title>
            <Shortcut>propn</Shortcut>
            <Description>Code snippet for property and backing field in class implementing INotifyPropertyChanged</Description>
            <Author>Brian Schroer, Modified by RLH</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property Type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>variable</ID>
                    <ToolTip>Underlying Variable</ToolTip>
                    <Default>_myProperty</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
        <Literal>
          <ID>notifyMethod</ID>
          <ToolTip>name of method to raise PropertyChanged event</ToolTip>
          <Default>NotifyPropertyChanged</Default>
        </Literal>
      </Declarations>
            <Code Language="csharp"><![CDATA[private $type$ $variable$;
    public $type$ $property$
    {
        get { return $variable$;}
        set 
    { 
        if (value != $variable$)
        {
            $variable$ = value;
            PropertyChanged(this, new PropertyChangedEventArgs("$property$"));
        }
    }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

What's Changed

First, I added a new literal variable. This creates a new, updatable item that you can tab from within the snippet. The variable name is defaulted to _propertyName which, as you can see, is a lowercase name. Note, unlike the original snippet, the underscore was being hardcoded before the PropertyName literal. In my code, I've split out the variable name from the property name.

I chose not to hardcode the underscore in my snippet so that if others use this code, they are at liberty to choose a different style of variable name. However, by way of the default, I am hinting at using the underscore.

If you want to force the underscore, then change the default variable value to propertyName. Then, everywhere variable is referenced, place the _ character before the reference.

Rewire answered 19/8, 2016 at 15:3 Comment(0)
K
2

Unfortunately, this is not possible using code snippets.

What you require would have to transform $property$ or some other literal. Even if you split the property name into 2 parts (first letter and the rest), you would have to make the letter upper case (or the other way - lower case).

Snippets offer only very limited number of transformation functions - precisely 3 for C# and not one of them can give the required result. See Code Snippet Functions on MSDN. This is true for all versions of Visual Studio up to 2013.

Kamat answered 10/11, 2013 at 6:6 Comment(0)
H
1

I'm going to give you the answer you don't want to hear: You shouldn't need to do this at all. Your model should have the (full or auto) properties, and then your ViewModel properties should just have a getter and a setter that returns _model.MyProperty.

Also, you may want to look into CallerMemberName to get rid of that pesky magic string. I'll post some examples if you want me to.


For example, I have a DealsUser model class (note how internal logic like generating the email address if it was never explicitly set, is done here):

public class DealsUser : IDealsUser
{
    public DealsUser() : this("GUEST")
    {
    }

    public DealsUser(string username)
    {
        this.Username = username;
        this.IsAdministrator = false;
        this.IsPlanModerator = false;
        this.IsPlanner = false;
    }

    public string Username { get; set; }

    public bool IsAdministrator { get; set; }

    public bool IsPlanModerator { get; set; }

    public bool IsPlanner { get; set; }

    private string _emailAddress;
    public string EmailAddress
    {
        get
        {
            return _emailAddress ?? string.Format(
                "{0}@mycompany.co.za", this.Username);
        }
        set
        {
            _emailAddress = value;
        }
    }

    public override string ToString()
    {
        return this.Username;
    }

And I have a BaseViewModel class with the following event and protected methods (note how we use CallerMemberName to eliminate magic strings):

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    var eventHandler = this.PropertyChanged;
    if (eventHandler != null)
    {
        eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

protected bool SetProperty<T>(ref T storage, T value,
    [CallerMemberName] string propertyName = null)
{
    if (object.Equals(storage, value))
    {
        return false;
    }

    storage = value;
    this.OnPropertyChanged(propertyName);
    return true;
}

protected bool SetModelProperty<T>(T storage, T value, Action setter,
    [CallerMemberName] string propertyName = null)
{
    if (object.Equals(storage, value))
    {
        return false;
    }

    setter();
    this.OnPropertyChanged(propertyName);
    return true;
}

#endregion

I then inherit from BaseViewModel, I dependency inject the model into my constructor, and I try to keep my ViewModel as lean as possible. Note that I have to use SetModelProperty instead of SetProperty, because you cannot pass a property (e.g. _dealsUser.Username) into a lamba function as a reference variable. Also note that IsPlanner and IsPlanModerator contain extra logic that updates related notifying properties when they change:

public class DealsUserVM : BaseViewModel
{
    private readonly IDealsUser _dealsUser;

    public DealsUserVM()
        : this(new DealsUser())
    {
        // Empty ctor
    }

    public DealsUserVM(IDealsUser dealsUser)
    {
        _dealsUser = dealsUser;
    }

    public IDealsUser Model
    {
        get
        {
            return _dealsUser;
        }
    }

    public string Username
    {
        get { return _dealsUser.Username; }
        set
        {
            SetModelProperty(_dealsUser.Username, value,
                () => { _dealsUser.Username = value; });
        }
    }

    public bool IsAdministrator
    {
        get { return _dealsUser.IsAdministrator; }
        set
        {
            SetModelProperty(_dealsUser.IsAdministrator, value,
                () => { _dealsUser.IsAdministrator = value; });
        }
    }

    public bool IsPlanModerator
    {
        get { return _dealsUser.IsPlanModerator; }
        set
        {
            // If IsPlanModerator has changed (and was updated as a result)
            if (SetModelProperty(_dealsUser.IsPlanModerator, value,
                () => { _dealsUser.IsPlanModerator = value; }))
            {
                // If IsPlanModerator is now TRUE
                if (value)
                {
                    this.IsPlanner = true;
                }
            }
        }
    }

    public bool IsPlanner
    {
        get { return _dealsUser.IsPlanner; }
        set
        {
            // If IsPlanner has changed (and was updated as a result)
            if (SetModelProperty(_dealsUser.IsPlanner, value,
                    () => { _dealsUser.IsPlanner = value; }))
            {
                // If IsPlanner is now FALSE
                if (!value)
                {
                    this.IsPlanModerator = false;
                }
            }
        }
    }

    public string EmailAddress
    {
        get { return _dealsUser.EmailAddress; }
        set
        {
            SetModelProperty(_dealsUser.EmailAddress, value,
                () => { _dealsUser.EmailAddress = value; });
        }
    }

    public override string ToString()
    {
        return _dealsUser.ToString();
    }
}

Hope this helps :-)

Heritor answered 13/11, 2013 at 17:14 Comment(1)
@Schneider: Examples addedHeritor

© 2022 - 2024 — McMap. All rights reserved.