CommunityToolkit / CommunityToolkit/dotnet

Cannot override Broadcast when using ObservableRecipient attribute

Open
#728 2 comments 1 reaction 0 assignees View on GitHub
bug :bug:
Dominant language
C#
Stars
3.8k
Forks
400
PR merge metrics
No merged PRs in 30d

Description

### Describe the bug

Documentation of `ObservableRecipient.Broadcast` indicate that Broadcast(...) should be overridden in order to use a custom channel (token). If we need to use the features of `ObservableValidator` and need to use `[NotifyPropertyChangedRecipients]`, then we need to use `[ObservableRecipient]` on the class in order to have the missing pieces. I couldn't read online the documentation of the `ObservableRecipient.Broadcast` method generated by the attribute since the API docs for this dotnet CommunityToolkit aren't published on Microsoft Learn, only the old Windows Community Toolkit 7.0.0 are available (https://learn.microsoft.com/en-us/dotnet/api/communitytoolkit.mvvm.componentmodel.observablerecipient?view=win-comm-toolkit-dotnet-7.0) (and it didn't exist). So I referred to the XML docs in the SourceLink using F12 in Visual Studio.

### Regression

_No response_

### Steps to reproduce

I made a new empty WPF project, and created a little sample pointing out what works and also the problem when wanting to add ObservableValidator in the mix.

```csharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;

using System;
using System.ComponentModel.DataAnnotations;

namespace WpfApp3
{
public partial class MyFirstObservableClass : ObservableRecipient
{
[ObservableProperty]
[NotifyPropertyChangedRecipients]
private int myFirstIntProperty;

[ObservableProperty]
[NotifyPropertyChangedRecipients]
private int myFirstIntProperty2;

protected override void Broadcast(T oldValue, T newValue, string? propertyName)
{
var message = new PropertyChangedMessage(this, propertyName, oldValue, newValue);
_ = Messenger.Send(message, $"{nameof(MyFirstObservableClass)}.{propertyName}");
}
}

[ObservableRecipient]
public partial class MySecondObservableClass : ObservableValidator
{
[ObservableProperty]
[Range(1, 21)]
[NotifyPropertyChangedRecipients]
private int mySecondIntProperty;

//// Severity Code Description Project File Line Suppression State
//// Error CS0111 Type 'MySecondObservableClass' already defines a member called 'Broadcast' with the same parameter types WpfApp3 CommunityToolkit.Mvvm.SourceGenerators\CommunityToolkit.Mvvm.SourceGenerators.ObservableRecipientGenerator\WpfApp3.MySecondObservableClass.g.cs 111 Active
//protected virtual void Broadcast(T oldValue, T newValue, string? propertyName)
//{
// var message = new PropertyChangedMessage(this, propertyName, oldValue, newValue);
// _ = Messenger.Send(message, nameof(MySecondIntProperty));
//}

//// Same error CS0111 at build, but also the following IntelliSense when writing:
////
//// Severity Code Description Project File Line Suppression State
//// Error CS0115 'MySecondObservableClass.Broadcast(T, T, string?)': no suitable method found to override WpfApp3 C:\Users\\WpfApp3\WpfApp3\MyObservableClass.cs 49 Active
//protected override void Broadcast(T oldValue, T newValue, string? propertyName)
//{
// var message = new PropertyChangedMessage(this, propertyName, oldValue, newValue);
// _ = Messenger.Send(message, nameof(MySecondIntProperty));
//}
}

public partial class MyFirstRecipient
: ObservableRecipient,
IRecipient>
{
public MyFirstRecipient()
{
IsActive = true;
}

public void Receive(PropertyChangedMessage message)
{
throw new NotImplementedException();
}
}

public partial class MySecondRecipient
: ObservableRecipient,
IRecipient>
{
public MySecondRecipient()
{
IsActive = true;
}

protected override void OnActivated()
{
Messenger.RegisterAll(
this,
$"{nameof(MyFirstObservableClass)}.{nameof(MyFirstObservableClass.MyFirstIntProperty2)}"
);
}

public void Receive(PropertyChangedMessage message)
{
throw new NotImplementedException();
}
}

public partial class MyThirdRecipient : ObservableRecipient
{
[ObservableProperty]
private int fromMyFirstIntProperty;

public MyThirdRecipient()
{
IsActive = true;
}

protected override void OnActivated()
{
// Using a method group...
Messenger.Register, string>(
this,
$"{nameof(MyFirstObservableClass)}.{nameof(MyFirstObservableClass.MyFirstIntProperty)}",
(r, m) => r.Receive1(m)
);
Messenger.Register, string>(
this,
$"{nameof(MyFirstObservableClass)}.{nameof(MyFirstObservableClass.MyFirstIntProperty2)}",
(r, m) => r.Receive2(m)
);

base.OnActivated();
}

public void Receive(PropertyChangedMessage message)
{
throw new NotImplementedException();
}

public void Receive1(PropertyChangedMessage message)
{
if (
message.Sender.GetType() == typeof(MyFirstObservableClass)
&& message.PropertyName == nameof(MyFirstObservableClass.MyFirstIntProperty)
)
{
FromMyFirstIntProperty = message.NewValue;
}
}

public void Receive2(PropertyChangedMessage message)
{
if (
message.Sender.GetType() == typeof(MyFirstObservableClass)
&& message.PropertyName == nameof(MyFirstObservableClass.MyFirstIntProperty2)
)
{
FromMyFirstIntProperty = message.NewValue;
}
}
}
}
```

### Expected behavior

Be able to use the token feature with `[NotifyPropertyChangedRecipients]` when using an `ObservableValidator` class, thus I must be able to override the `Broadcast` method.

### Screenshots

_No response_

### IDE and version

VS 2022

### IDE version

VisualStudio.17.Release/17.6.4+33815.320

### Nuget packages

- [ ] CommunityToolkit.Common
- [ ] CommunityToolkit.Diagnostics
- [ ] CommunityToolkit.HighPerformance
- [X] CommunityToolkit.Mvvm (aka MVVM Toolkit)

### Nuget package version(s)

8.2.0

### Additional context

In the source generator ObservableRecipientGenerator.cs, there is already some logic to not generate some methods if it already exists.
https://github.com/CommunityToolkit/dotnet/blob/e071ed22b379f904820475db827be3ad04d3e96e/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservableRecipientGenerator.cs

What I would see as a solution would be to have the same kind of checks, for other methods, like `Broadcast`, and we would probably see this file have a new field in the record:
https://github.com/CommunityToolkit/dotnet/blob/e071ed22b379f904820475db827be3ad04d3e96e/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/ObservableRecipientInfo.cs

### Help us help you

No, just wanted to report this

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.