【发布时间】:2020-10-22 12:05:07
【问题描述】:
首先,我有一个具有如下依赖属性的用户控件。 MenuItems 属性绑定到 UI 上的某个 List 控件。
public static readonly DependencyProperty MenuItemsProperty = DependencyProperty.Register(
nameof(MenuItems),
typeof(IEnumerable<MenuItem>),
typeof(MenuViewControl),
new PropertyMetadata(null));
public IEnumerable<MenuItem> MenuItems
{
get => (IEnumerable<MenuItem>)GetValue(MenuItemsProperty);
set => SetValue(MenuItemsProperty, value);
}
MenuItem 类如下,它有 3 个属性,
public class MenuItem : BindableBase
{
private string _text;
private Action _action;
private ICommand _executeCommand;
public string Text
{
get => _text;
set => Set(ref _text, value);
}
public Action Action
{
get => _action;
set => Set(ref _action, value);
}
public ICommand ExecuteCommand
{
get => _executeCommand ?? (_executeCommand = new RelayCommand(Action, _canExecute));
set
{
if (Set(ref _executeCommand, value))
{
CanExecute = () => _executeCommand?.CanExecute(null) ?? true;
_executeCommand.CanExecuteChanged += (sender, args) => RaisePropertyChanged(nameof(IsEnabled));
}
}
}
}
现在我想在我的代码中的某个地方重用上面的用户控件。同样,我需要调用一些异步方法。所以我有一个当前 UI 的视图模型类,我将在其中调用上述用户控件,如下所示。我的问题是 IsBorderProgressRingVisible 永远不会设置为 false 并且 RunMethodResult 永远不会更新当前 UI 中的 TextBlock。请帮忙。
public class UserMaintenanceMethodsViewModel:BindableBase
{
//This collection is bound to the above UserControl's MenuItem property on my current UI.
private ObservableCollection<MenuItem> _userMaintenanceMenuCollection;
public ObservableCollection<MenuItem> UserMaintenanceMenuCollection
{
get => _userMaintenanceMenuCollection;
set => Set(ref _userMaintenanceMenuCollection, value);
}
//This string is bound to a textblock
private string _runMethodResult;
public string RunMethodResult
{
get => _runMethodResult;
set => Set(ref _runMethodResult, value);
}
//This property is bound to a progress ring.
private bool _isBorderProgressRingVisible;
public bool IsBorderProgressRingVisible
{
get => _isBorderProgressRingVisible;
set => Set(ref _isBorderProgressRingVisible, value);
}
//In my constructor I am calling some async methods as follows..
public UserMaintenanceMethodsViewModel()
{
_ = PopulateServiceMethods();
}
//Problem in this method is once the IsBorderProgressRingVisible is set to true, it never sets the value back to false. As a result the progress ring never collapses.
//The other problem is the RunMethodResult which is bound to a textblock never gets updated. Please help.
private async Task PopulateServiceMethods()
{
try
{
if (_atlasControlledModule != null)
{
IsBorderProgressRingVisible = true;
UserMaintenanceMenuCollection = new ObservableCollection<MenuItem>();
var Methods = await _atlasControlledModule.GetServiceMethods(AtlasMethodType.Maintenance).ConfigureAwait(true);
foreach (var method in Methods)
{
UserMaintenanceMenuCollection.Add(new MenuItem()
{
Text = method.Name,
Action = async () =>
{
var result = await ExcuteAtlasMethod(method).ConfigureAwait(true);
RunMethodResult = result.Status.ToString(); //The textblock on the UI never gets updated.
},
Warning = false
});
}
}
}
finally
{
IsBorderProgressRingVisible = false; //This code dosen't work.
}
}
private async Task<AtlasMethodRequest> ExcuteAtlasMethod(AtlasMethod method)
{
try
{
IsBorderProgressRingVisible = true;
return await _atlasControlledModule.CallMethod(method);
}
finally
{
IsBorderProgressRingVisible = false;
}
}
}
编辑:这是当前视图的 Xaml
<viewCommon:PageViewBase
x:Class="Presentation.InstrumentUI.ViewsLoggedIn.UserMaintenanceMethodsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewCommon="using:Presentation.InstrumentUI.Common"
xmlns:viewsCommon="using:Presentation.InstrumentUI.ViewsCommon"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:valueConverters="using:Presentation.Common.ValueConverters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<viewsCommon:MenuViewControl x:Name="UserMaintenanceMethodsMenuView"
Grid.Row="0"
Title="{Binding UserMaintenanceMethodsTitle, Source={StaticResource StringResources}}"
LifetimeScope="{x:Bind LifetimeScope}"
MenuItems="{x:Bind ViewModel.UserMaintenanceMenuCollection,Mode=OneWay}"
HeaderVisibility="Visible">
</viewsCommon:MenuViewControl>
</Grid>
</viewCommon:PageViewBase>
这是 xaml.cs
public sealed partial class UserMaintenanceMethodsView : PageViewBase
{
public IUserMaintenanceMethodsViewModel ViewModel { get; set; }
public UserMaintenanceMethodsView()
{
this.InitializeComponent();
ViewModel = LifetimeScope.Resolve<IUserMaintenanceMethodsViewModel>();
}
}
【问题讨论】:
-
我刚刚尝试在带有等待的测试解决方案中模拟您的代码,并且 IsBorderProgressRingVisible 似乎按预期设置。我猜你是通过一些事件在
MenuItem中调用Action,并猜测它什么时候对你不起作用。当你调试代码时,finally 子句会被命中吗?是否可以在您的情况下模拟_atlasControlledModule并验证功能? -
是的,最终块对我来说很重要。我看到断点将 IsBorderProgressRingVisible 的值设置为 false。我有 BooleanToVisibilityConverter,问题是即使变量设置为 false,断点也会进入转换器。 _atlasControlledModule 只是解析一些 json 数据并返回一个对象。
-
你能告诉我们你如何绑定
UserMaintenanceMethodsViewModel的XAML代码吗? -
纠结的代码。你试过
Binding而不是x:Bind吗?删除无用的.ConfigureAwait(true)。检查,调试器在哪种线程中触发了面包点(VS 工具栏中的组合框)。 -
你是如何绑定
IsBorderProgressRingVisible和RunMethodResult的,我在UWP 中尝试了代码,一切都很好。 ps:x:Bind的默认Mode是OneTime,所以一定要使用正确的模式。
标签: c# xaml uwp async-await