以下是我的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>
并不是所有这些都很重要,但它确实有效。谢谢大家!
答案 0 :(得分:1)
Caliburn.Micro使用数据绑定来评估何时必须调用保护方法。
在您的ICaller实例上实现INotifyPropertyChanged并为OnHold和IsConference属性引发PropertyChanged(如果您还没有这样做)。
更改TalkPrivate和CanTalkPrivate的方法签名,以获取可能更改的每个值的参数:
public bool CanTalkPrivate(ICaller caller, bool onHold, bool isConference)
{
return onHold || isConference;
}
相应地更改您的操作消息:
TalkPrivate($datacontext, $datacontext.OnHold, $datacontext.IsConference)
或者,您可以创建一个额外的属性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可以监视它们的属性变化。
我倾向于处理这样的问题的方式,你有这种视图逻辑,像CallerViewModel
和Callers
这样的专用视图模型就是这些模型的集合。
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已更改。如果ICaller
和ICallService
使用属性更改通知,则可以使用。你可能需要CallerViewModel
上的方法,当你知道事情发生了变化时,你可以打电话。由于我不知道这两个属性是如何被改变的,所以很难回答这部分,这部分是特定于你的应用程序。
由于问题已更新而进行修改。
将ICaller
作为CallerViewModel
肯定会使此解决方案变得更加容易。将NotifyOfPropertyChange(() => CanTalkPrviate);
添加到OnHold
可以解决正确更新UI问题的一半问题。
更新IsConferenceChanging
上的用户界面的潜在解决方案是一些解决方案之一。
CallerViewModel
侦听的“呼叫已更改消息”。ICallService
上的简单.NET事件,用于CallerViewModel
订阅的呼叫更改时。MergeCalls
,AddCaller
等的方法添加代码以更新CallerViewModel
。