提升警卫方法改变了吗?

时间:2014-12-27 12:13:12

标签: wpf caliburn.micro

以下是我的ViewModel的代码:

public BindableCollection<ICaller> Callers { get { return callService.Current.Callers; } }    
public void TalkPrivate(ICaller caller)
        {
            callService.TalkPrivate(caller);
        }

        public bool CanTalkPrivate(ICaller caller)
        {
            return caller.OnHold || callService.IsConference;
        }

XAML:

 <ItemsControl ItemsSource="{Binding Callers}" Grid.Row="0" Margin="10,22,10,10"
                  Visibility="{Binding Callers.Count, Converter={StaticResource CollectionSizeToVisibilityConverter}, ConverterParameter=false}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="40"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="120"/>
                    </Grid.ColumnDefinitions>

                    <controls:CircleContentControl Height="40" Width="40" Grid.Column="0">
                        <Image Source="{Binding Image}" Stretch="Uniform" />
                    </controls:CircleContentControl>
                    <TextBlock Text="{Binding Display}" Grid.Column="1" FontSize="24" VerticalAlignment="Center" Margin="5,0,0,0"/>
                    <StackPanel Grid.Column="2" Orientation="Horizontal">
                        <Button cal:Message.Attach="TalkPrivate($dataContext)" Style="{StaticResource CallActionButtonStyle}" ToolTip="Falar privado"
                                Height="50" Width="50">
                            <Rectangle Width="20" Height="20" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}">
                                <Rectangle.OpacityMask>
                                    <VisualBrush Stretch="Fill" Visual="{DynamicResource appbar_phone}" />
                                </Rectangle.OpacityMask>
                            </Rectangle>
                        </Button>

呼叫服务将更改呼叫者 OnHold 属性。但是,UI没有按顺序改变;也就是说,按钮未被禁用/启用。调用 TalkPrivate 方法后,似乎不会调用 CanTalkPrivate

如何强制按钮可用性刷新?

已审核 有关我的代码的更多见解

CallerViewModel

public class CallerViewModel : PropertyChangedBase, ICaller
{
    public CallerViewModel(string phoneNumber, string name, string image = null)
    {
        PhoneNumber = phoneNumber;
        Name = name;
        Display = name;
        Image = image;
    }

    public CallerViewModel(string phoneNumber, Contact contact)
        : this(phoneNumber, contact.Display, contact.Image) { }

    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            NotifyOfPropertyChange();
        }
    }

    private string display;
    public string Display
    {
        get { return display; }
        set
        {
            display = value;
            NotifyOfPropertyChange();
        }
    }

    private string phoneNumber;
    public string PhoneNumber
    {
        get { return phoneNumber; }
        set
        {
            phoneNumber = value;
            NotifyOfPropertyChange();
        }
    }

    private string image;
    public string Image
    {
        get { return image; }
        set
        {
            image = value;
            NotifyOfPropertyChange();
        }
    }

    private bool onHold;
    public bool OnHold
    {
        get { return onHold; }
        set
        {
            onHold = value;
            NotifyOfPropertyChange();
        }
    }


    public void AppendToDisplay(string value)
    {
        if (Display == Name)
            Display = value;
        else
            Display += value;
    }

    public void ResetDisplay()
    {
        Display = Name;
    }
}

}

CallService

public class CallService : ICallService
{
    private readonly ICallerSearch callerSearch;

    public bool IsInCall { get { return Current != null; } }
    public bool IsConference { get; private set; }
    public ICurrentCall Current { get; private set; }
    public string IncomingPhoneNumber { get; set; }

    public CallService(ICallerSearch callerSearch)
    {
        this.callerSearch = callerSearch;
    }

    public ICurrentCall CreateCall(string number)
    {
        var caller = callerSearch.FindByNumber(number);
        Current = new CurrentCall(caller);
        return Current;
    }

    public ICurrentCall CreateCall(ICaller caller)
    {
        Current = new CurrentCall(caller);
        return Current;
    }

    public void EndCall(ICaller caller = null)
    {
        if (caller == null)
            EndAll();
        else
        {
            Current.Callers.Remove(caller);
            if (IsConference)
                IsConference = false;
        }
    }

    private void EndAll()
    {
        if (Current != null)
            Current.Dispose();
        Current = null;
    }

    public ICaller AddCaller(string number)
    {
        foreach (var caller in Current.Callers)
            caller.OnHold = true;

        var newCaller = callerSearch.FindByNumber(number);
        Current.Add(newCaller);
        return newCaller;
    }

    public void MergeCalls()
    {
        IsConference = true;
    }

    public void TalkPrivate(ICaller caller)
    {
        foreach (var item in Current.Callers)
            item.OnHold = true;
        caller.OnHold = false;

        if (IsConference)
            IsConference = false;
    }
}

@nigel,ICaller实际上已经是一个ViewModel了。但是ICallService不是。

EDIT2 - 解决方案

@Nigel指出了正确的方向。我做的是: CallerViewModel 收听 ICallService ConferenceStarted ConferenceEnded 并更新 CanTalkPrivate

 public CallerViewModel(ICallService callService, string phoneNumber, string name, string image = null)
    {
        this.callService = callService;
        callService.ConferenceStarted += ConferenceStarted;

        PhoneNumber = phoneNumber;
        Name = name;
        Display = name;
        Image = image;
    }

    private void ConferenceStarted(object sender, System.EventArgs e)
    {
        NotifyOfPropertyChange(() => CanTalkPrivate);
    }

    private bool onHold;
    public bool OnHold
    {
        get { return onHold; }
        set
        {
            onHold = value;
            NotifyOfPropertyChange();
            NotifyOfPropertyChange(()=>CanTalkPrivate);
        }
    }

    public bool CanTalkPrivate
    {
        get
        {
            return OnHold || callService.IsConference;
        }
    }

但是,我无法使用防护方法,因为它仍然无法正常工作。但现在我可以将IsEnabled绑定到CanTalkPrivate:

<Button cal:Message.Attach="TalkPrivate($dataContext)" IsEnabled="{Binding CanTalkPrivate}" Style="{StaticResource CallActionButtonStyle}" ToolTip="Falar privado"
                                Height="50" Width="50">
                            <Rectangle Width="20" Height="20" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}">
                                <Rectangle.OpacityMask>
                                    <VisualBrush Stretch="Fill" Visual="{DynamicResource appbar_phone}" />
                                </Rectangle.OpacityMask>
                            </Rectangle>
                        </Button>

并不是所有这些都很重要,但它确实有效。谢谢大家!

3 个答案:

答案 0 :(得分:1)

Caliburn.Micro使用数据绑定来评估何时必须调用保护方法。

  1. 在您的ICaller实例上实现INotifyPropertyChanged并为OnHold和IsConference属性引发PropertyChanged(如果您还没有这样做)。

  2. 更改TalkPrivate和CanTalkPrivate的方法签名,以获取可能更改的每个值的参数:

    public bool CanTalkPrivate(ICaller caller, bool onHold, bool isConference)
    {
        return onHold || isConference;
    }
    
  3. 相应地更改您的操作消息:

    TalkPrivate($datacontext, $datacontext.OnHold, $datacontext.IsConference)
    
  4. 或者,您可以创建一个额外的属性CanTalkPrivate并使用它:

    TalkPrivate($datacontext, $datacontext.CanTalkPrivate)
    

答案 1 :(得分:1)

或者另一种选择是更改它,以便CanTalkPrivate是bool属性,只需在对基础服务进行更改时进行通知调用。

public bool CanTalkPrivate { get{ return OnHold || IsConference;} }

NotifyOfPropertyChange(() => CanTalkPrivate);

答案 2 :(得分:1)

正如大多数人建议的那样,CanTalkPrivate使用属性是最好的,因为您可以控制何时发生有关更改的通知。使用Guard方法而不是属性只有在参数来自UI时才能正常工作,Caliburn.Micro可以监视它们的属性变化。

我倾向于处理这样的问题的方式,你有这种视图逻辑,像CallerViewModelCallers这样的专用视图模型就是这些模型的集合。

public class CallerViewModel : PropertyChangedBase
{
    private readonly ICaller caller;
    private readonly ICallService callService;

    public CallerViewModel(ICaller caller, ICallService callService)
    {
        this.caller = caller;
        this.callService = callService;
    }

    public void TalkPrivate()
    {
        callService.TalkPrivate(caller);
    }

    public bool CanTalkPrivate
    {
        get
        {
            return caller.OnHold || callService.IsConference;
        }
    }
}

您仍然需要一种方法来告诉此视图模型CanTalkPrivate已更改。如果ICallerICallService使用属性更改通知,则可以使用。你可能需要CallerViewModel上的方法,当你知道事情发生了变化时,你可以打电话。由于我不知道这两个属性是如何被改变的,所以很难回答这部分,这部分是特定于你的应用程序。

由于问题已更新而进行修改

ICaller作为CallerViewModel肯定会使此解决方案变得更加容易。将NotifyOfPropertyChange(() => CanTalkPrviate);添加到OnHold可以解决正确更新UI问题的一半问题。

更新IsConferenceChanging上的用户界面的潜在解决方案是一些解决方案之一。

  1. 使用事件聚合器广播CallerViewModel侦听的“呼叫已更改消息”。
  2. ICallService上的简单.NET事件,用于CallerViewModel订阅的呼叫更改时。
  3. 为使用MergeCallsAddCaller等的方法添加代码以更新CallerViewModel