我有一个 winui 3 项目,在微软的 MVVM 工具包的帮助下使用 MVVM 模式。 我创建了一个具有 2 个 ICommand 类型的 DependencyProperties 的 UserControl。这样,当我在视图中使用此控件时,我可以执行视图模型中按钮单击的代码,以符合 mvvm 原则。
用户控制:
public sealed partial class MyDialog : UserControl
{
public static readonly DependencyProperty YesSelectedCommandProperty =
DependencyProperty.Register("YesSlectedCommand", typeof(ICommand), typeof(AddinAutoAttachDialog), new PropertyMetadata(null, OnYesCommandChanged));
public static readonly DependencyProperty NoSelectedCommandProperty =
DependencyProperty.Register("NoSelectedCommand", typeof(ICommand), typeof(AddinAutoAttachDialog), new PropertyMetadata(null, OnNoCommandChanged));
public ICommand YesSlectedCommand
{
get { return (ICommand)GetValue(YesSelectedCommandProperty); }
set { SetValue(YesSelectedCommandProperty, value); }
}
public ICommand NoSlectedCommand
{
get { return (ICommand)GetValue(NoSelectedCommandProperty); }
set { SetValue(NoSelectedCommandProperty, value); }
}
public MyDialog()
{
this.InitializeComponent();
}
对应的xaml代码:
<Button Content="Yes"
Command="{x:Bind YesSlectedCommand, Mode=OneTime}"/>
<Button Content="No"
Command="{x:Bind NoSlectedCommand, Mode=OneTime}" />
我认为这样使用它:
<components:MyDialog Visibility="{x:Bind MainViewModel.ShowMyDialog, Mode=OneWay}"
YesSlectedCommand="{x:Bind MainViewModel.YesSelected, Mode=OneWay}"
NoSlectedCommand="{x:Bind MainViewModel.NoSelected, Mode=OneWay}" />
<InfoBar IsOpen="True"
Severity="Success"
Title="..."
Visibility="{x:Bind MainViewModel.ShowSuccessInfoBar, Mode=OneWay}"/>
<InfoBar IsOpen="True"
Severity="Error"
Title="..."
Visibility="{x:Bind MainViewModel.ShowErrorInfoBar, Mode=OneWay}" />
然后在我的视图模型中执行类似的代码(主要是更改 UI 元素的可见性):
internal partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private bool showMyDialog = false;
[ObservableProperty]
private bool showSuccessInfoBar = false;
[ObservableProperty]
private bool showErrorInfoBar = false;
[RelayCommand]
private void YesSelected()
{
bool succeeded = ExecuteSomeOperations();
ShowMyDialog = false;
ShowSuccessInfoBar = succeeded;
ShowErrorInfoBar = !succeeded;
}
[RelayCommand]
private void NoSelected()
{
ShowMyDialog = false;
ShowErrorInfoBar = true;
}
}
奇怪的是,当我选择“否”按钮或有时选择“是”按钮时,我的应用程序有时会崩溃。但其他时候它会正确显示所有内容。
当我连接调试器时,我唯一看到的是这行消息: 程序“[9964] MyWindow.exe”已退出,代码为 3221226107 (0xc000027b)。 我检查了事件查看器,发现了这个:
Faulting application name: MyWindow.exe, version: 1.0.0.0, time stamp: 0x661f0000
Faulting module name: Microsoft.ui.xaml.dll, version: 3.1.4.0, time stamp: 0x343afd70
Exception code: 0xc000027b
Fault offset: 0x00000000003b9584
Faulting process id: 0x0x4930
Faulting application start time: 0x0x1DAFE12C2C9EF06
Faulting module path: \...\Microsoft.ui.xaml.dll
Faulting package full name:
Faulting package-relative application ID:
我尝试将其包装在 try catch 块中,但没有捕获到任何异常,并且在 Yes/NoSelected 方法中修改 UI 元素可见性的命令中的代码可以成功执行。我还尝试查找绑定失败,因为当我修改 UI 时它似乎崩溃了。 (顺便说一句,我不会从另一个线程修改 UI)所以我也尝试了这个:
public App()
{
this.InitializeComponent();
this.DebugSettings.BindingFailed += (s, e) =>
{
File.AppendAllText(LogPath, $"BindingFailed: {e.Message}");
};
}
但是我的日志文件中没有关于绑定失败的日志。
这里可能出现什么问题?
问题出在数据绑定上。我忘记使用布尔值到可见性转换器来实现控件的可见性。但就我而言,根元素在我看来是一个窗口,无法将 x:bind 与转换器一起使用,因此我必须切换到绑定标记扩展。由于 x:bind 和 Binding 的数据上下文不同,我必须使用设置它的容器网格中的数据上下文。
所以我修改了代码:
<InfoBar IsOpen="True"
Severity="Success"
Title="..."
Visibility="{x:Bind MainViewModel.ShowSuccessInfoBar, Mode=OneWay}"/>
对此:
<InfoBar IsOpen="True"
Severity="Success"
Title="..."
Visibility="{Binding ElementName=myGrid, Path=DataContext.ShowSuccessInfoBar, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}" />
它解决了偶发的崩溃问题,但我仍然不明白为什么首先可以工作,因为有时它可以正常工作。