Reputation: 1221
I am new to Prism and have a problem with Event Aggregator. I modify the HelloWorld example given by Microsoft to show my problem, which is described at the end.
First, I define a MyMessage class to be send as the payload of an event as follow:
public class MyMessage
{
public int id { get; set; }
public string content { get; set; }
public override string ToString()
{
return this.content;
}
}
Secondly, I define an event as follow
public class MessageArrivedEvent : PubSubEvent<MyMessage>
{
}
After that, I add a button to Shell.xaml and its click function is as follow
private void button_Click(object sender, RoutedEventArgs e)
{
message.content = message.content == "hello1" ? "hello2" : "hello1";
ServiceLocator.Current.GetInstance<IEventAggregator>()
.GetEvent<MessageArrivedEvent>()
.Publish(message);
}
Here, message is a property in public partial class Shell : Window class.
Furthermore, I add a combo box to HelloWorldView and bind the ItemsSource to its corresponding viewmodel.
private ObservableCollection<MyMessage> _comboboxitemsource;
public ObservableCollection<MyMessage> comboboxitemsource
{
get { return this._comboboxitemsource; }
set
{
this._comboboxitemsource = value;
this.OnPropertyChanged("comboboxitemsource");
}
}
The way I handle the event is as follow:
public HelloWorldViewModel()
{
this.comboboxitemsource = new ObservableCollection<MyMessage>();
ServiceLocator.Current.GetInstance<IEventAggregator>().GetEvent<MessageArrivedEvent>().Subscribe(this.MessageArrived, ThreadOption.UIThread, false);
}
private void MessageArrived(MyMessage message)
{
for (int i = 0; i < this.comboboxitemsource.Count; i++)
{
if (this.comboboxitemsource[i].id == message.id)
{
this.comboboxitemsource[i].content = message.content;
break;
}
}
if (this.comboboxitemsource.Count == 0)
this.comboboxitemsource.Add(message);
}
Now I set the content of the message in Shell part to "hello1". When I click the button, its content will be changed to "hello2" and then message will be sent to the HelloWorldModule. Since at this time, there is no element in comboboxitemsource, the message will be added into this itemsource. When the button is clicked again, the content of the message will be changed to "hello1" and the message will be sent to the module. The problem is before the message is changed in MessageArrived function, the content is already changed automatically to "hello1". After running MessageArrived, the content in the dropdown list is still "hello2", rather than "hello1".
Can anyone help to explain the problem? Thank you
Upvotes: 0
Views: 1018
Reputation: 10015
The real problem is that you're using 1 single message object all over your application and you're changing that single object. That's basicly how reference types work.
A reference type contains a pointer to another memory location that holds the data. Reference types include the following:
- String
- All arrays, even if their elements are value types
- Class types, such as Form
- Delegates
Source: https://msdn.microsoft.com/en-us/library/t63sy5hs.aspx
To recap what you're actually doing:
id=1 content="hello1"
) and store the reference to it in the property on your Shell.content="hello2"
and you use the EventAggregator
to send the (reference) of the message to the Module.Now you end up with multiple pointers (one in your property, one in your ObservableCollection to the same Message object in memory.
content="hello1"
. Since it's 1 object referenced everywhere, all variables already see content="hello1"
.Note that you don't see it on the UI in the ComboBox without INotifyPropertyChanged, but when you debug the code, the value is already changed (as we're still talking about the same 1 single message object).
How to fix it?
This way you will end up with a list of different items in your ObservableCollection. It's then up to you to decide if you want to show one item for each message send (simply add every incoming message) or filter out duplicate strings (apply a check).
Upvotes: 1