Kazuhiko Nakayama
Kazuhiko Nakayama

Reputation: 801

Caliburn.Micro / Property not update in IHandle / EventAggregator

Here is step.

  1. Fire EventAggregator and send a message.
  2. Catch the message in IHandle.
  3. Update a TextBlock.( Not update TextBlock Binding at all : (

It is a simple logic. but not work on Caliburn.Micro. Please check my codes.

I make a sample project in GitHub.

You can test it with just click button.

enter image description here

Here is ViewModel

    public void PublishOnBackgroundThread(int flag) {

        Debug.WriteLine($"PublishOnBackgroundThread/{flag}");
        ix++;
        if( flag == 0)
        {
            _eventAggregator.PublishOnBackgroundThread(new HelloMessage(ix.ToString(), false));
        }
        else if( flag == 1)
        {
            _eventAggregator.PublishOnBackgroundThread(new HelloMessage(ix.ToString(), true));
        }
    }

    public void PublishOnCurrentThread(int flag)
    {
        Debug.WriteLine($"PublishOnCurrentThread/{flag}");
        ix++;
        if (flag == 0)
        {
            _eventAggregator.PublishOnCurrentThread(new HelloMessage(ix.ToString(), false));
        }
        else if (flag == 1)
        {
            _eventAggregator.PublishOnCurrentThread(new HelloMessage(ix.ToString(), true));
        }
    }
    public void PublishOnUIThread(int flag)
    {
        Debug.WriteLine($"PublishOnUIThread/{flag}");
        ix++;
        if (flag == 0)
        {
            _eventAggregator.PublishOnUIThread(new HelloMessage(ix.ToString(), false));
        }
        else if (flag == 1)
        {
            _eventAggregator.PublishOnUIThread(new HelloMessage(ix.ToString(), true));
        }
    }
    public void PublishOnUIThreadAsync(int flag)
    {
        Debug.WriteLine($"PublishOnUIThreadAsync/{flag}");
        ix++;
        if (flag == 0)
        {
            _eventAggregator.PublishOnUIThreadAsync(new HelloMessage(ix.ToString(), false));
        }
        else if (flag == 1)
        {
            _eventAggregator.PublishOnUIThreadAsync(new HelloMessage(ix.ToString(), true));
        }
    }

    public void Handle(HelloMessage message)
    {

        Debug.WriteLine($"Handle(HelloMessage message)/{message.UiAsync}/{message.msg}");
        if (message.UiAsync)
        {
            Execute.OnUIThreadAsync(() =>
            {
                _myText = message.msg;
                MyText = _myText;

            });
            /*Execute.OnUIThread(() =>
            {
                _myText = message.msg;
                MyText = _myText;

            });*/
        }
        else
        {
            _myText = message.msg;
            MyText = _myText;
        }
    }

    private int ix = 0;
    private String _myText = "Update Number at Here !";
    public String MyText
    {
        get { return _myText; }
        set
        {
            Debug.WriteLine($"this.Set(ref _myText, value);");
            this.Set(ref _myText, value);
        }
    }

Nere is xaml.

        <StackPanel.Resources>
            <Style TargetType="{x:Type Button}">
                <Setter Property="Margin" Value="5"/>
            </Style>
        </StackPanel.Resources>

        <TextBlock Text="{Binding MyText, Mode=TwoWay}" Margin="50" />
        <Button Content="PublishOnBackgroundThread" cal:Message.Attach="[Event Click]=[Action PublishOnBackgroundThread(0)]"/>
        <Button Content="PublishOnCurrentThread" cal:Message.Attach="[Event Click]=[Action PublishOnCurrentThread(0)]"/>
        <Button Content="PublishOnUIThread" cal:Message.Attach="[Event Click]=[Action PublishOnUIThread(0)]"/>
        <Button Content="PublishOnUIThreadAsync" cal:Message.Attach="[Event Click]=[Action PublishOnUIThreadAsync(0)]"/>


        <Button Content="PublishOnBackgroundThread + Execute.OnUIThreadAsync" cal:Message.Attach="[Event Click]=[Action PublishOnBackgroundThread(1)]"/>
        <Button Content="PublishOnCurrentThread + Execute.OnUIThreadAsync" cal:Message.Attach="[Event Click]=[Action PublishOnCurrentThread(1)]"/>
        <Button Content="PublishOnUIThread + Execute.OnUIThreadAsync" cal:Message.Attach="[Event Click]=[Action PublishOnUIThread(1)]"/>
        <Button Content="PublishOnUIThreadAsync + Execute.OnUIThreadAsync" cal:Message.Attach="[Event Click]=[Action PublishOnUIThreadAsync(1)]"/>

StackOverFlow Web page do not like long code. then, I am typing........

Upvotes: 0

Views: 677

Answers (2)

Frenchy
Frenchy

Reputation: 17017

If you want a clean code:

dont initialize the private variable _myText, but MyText in the constructor

    private readonly IEventAggregator _eventAggregator;
    public BaseViewModel()
    {
        MyText = "Update Number at Here !";
        _eventAggregator = IoC.Get<IEventAggregator>();
    }

and better if you use the Dependency Injection:

    private readonly IEventAggregator _eventAggregator;
    public BaseViewModel(IEventAggregator _eventAggregator)
    {
        MyText = "Update Number at Here !";
        this._eventAggregator = _eventAggregator;
    }

then declare the property:

private String _myText;
public String MyText
{
    get { return _myText; }
    set
    {
        Debug.WriteLine($"this.Set(ref _myText, value);");
        this.Set(ref _myText, value); // or
        //_myText = value;
        //NotifyOfPropertyChange(() => MyText);
    }
}

and finally Handle:

    public void Handle(HelloMessage message)
    {
        Debug.WriteLine($"Handle(HelloMessage message)/{message.UiAsync}/{message.msg}");
        if (message.UiAsync)
        {
            Execute.OnUIThreadAsync(() =>
            {
                MyText = message.msg;

            });
            /*Execute.OnUIThread(() =>
            {
                _myText = message.msg;
                MyText = _myText;

            });*/
        }
        else
        {
            MyText = message.msg;
        }
    }

Dont update the private variable _myText, but only the public variable MyText

each time you update the MyText variable, the NotifyOfPropertyChange sends the update to the view.

using this.Set or the two lines in comments is the same.. both they call the final method in caliburn:

    /// <summary>
    /// Notifies subscribers of the property change.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    // Token: 0x060000BF RID: 191 RVA: 0x0000342C File Offset: 0x0000162C
    public virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = null)
    {
        if (this.IsNotifying && this.PropertyChanged != null)
        {
            this.OnUIThread(delegate
            {
                this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
            });
        }
    }

Upvotes: 2

sharp
sharp

Reputation: 1211

Inside your property MyText, in set block write this:

set {
    Debug.WriteLine($ "this.Set(ref _myText, value);");
    this.Set(ref _myText, value);
    NotifyOfPropertyChange(() => MyText);
}

And inside Handle method remove all NotifyOfPropertyChange(() => MyText); since your are now calling it when ever you change the values property.

Upvotes: 0

Related Questions