基本上我的问题是:我正在尝试在我的 WinUI 3 应用程序中显示 ContentDialog。
打开对话框的命令是由我的“主页”(LibraryPage)上的上下文菜单按钮触发的 对话框的内容是一堆文本框,我在另一个xaml中定义它们(LibraryAddSongControl)
但我无法直接从 ViewModel 打开对话框,因为我无法从那里访问所需的 XamlRoot。我无法访问对话框文本框内容,因为它们位于 LibraryAddSongViewModel
<controls:ListDetailsView
x:Uid="Library"
x:Name="ListDetailsViewControl"
BackButtonBehavior="Manual"
Background="Transparent"
DetailsTemplate="{StaticResource DetailsTemplate}"
ListHeader="Songs"
ItemsSource="{x:Bind ViewModel.AllSongs}"
ItemTemplate="{StaticResource ItemTemplate}"
ListHeaderTemplate="{StaticResource MinimalListHeaderTemplate}"
NoSelectionContentTemplate="{StaticResource NoSelectionContentTemplate}"
SelectedItem="{x:Bind ViewModel.Selected, Mode=TwoWay}"
ViewStateChanged="OnViewStateChanged">
<controls:ListDetailsView.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Add new Song" Icon="Add" Command="{x:Bind ViewModel.AddNewSongCommand}" />
<MenuFlyoutItem Text="Library Statistics" Icon="Help"
Command="{x:Bind ViewModel.ShowLibraryStatisticsCommand}" />
<MenuFlyoutSeparator />
<MenuFlyoutItem Text="Delete Selected" Icon="Delete" Command="{x:Bind ViewModel.DeleteSongCommand}" />
</MenuFlyout>
</controls:ListDetailsView.ContextFlyout>
</controls:ListDetailsView>
<UserControl
x:Class="Maestro.Views.Dialogs.LibraryAddSongControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Maestro.Views.Dialogs"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:extensions="using:CommunityToolkit.WinUI.UI"
mc:Ignorable="d">
<Grid>
<StackPanel>
<TextBox Header="Titel" Text="{x:Bind ViewModel.Title, Mode=TwoWay}"/>
<TextBox Header="Beschreibung" Text="{x:Bind ViewModel.Description, Mode=TwoWay}"/>
<TextBox Header="Album" Text="{x:Bind ViewModel.Album, Mode=TwoWay}"/>
<TextBox Header="Artist(s)" Text="{x:Bind ViewModel.Artists, Mode=TwoWay}"/>
<TextBox Header="Dauer" Text="{x:Bind ViewModel.Duration, Mode=TwoWay}" extensions:TextBoxExtensions.Mask="99:99" />
<TextBox Header="URL" Text="{x:Bind ViewModel.Url, Mode=TwoWay}"/>
<!--<TextBox Header="Titel" Text="{x:Bind ViewModel.ThumbnailPath, Mode=TwoWay}"/>-->
</StackPanel>
</Grid>
</UserControl>
用于从 LibraryViewModel 打开 ContentDialog 的 ContextMenu 命令
[RelayCommand]
public async Task AddNewSong()
{
ContentDialog dialog = new();
dialog.XamlRoot = //Not accessible from ViewModel!
dialog.Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style;
dialog.Title = "Add a new Song to the Library";
dialog.PrimaryButtonText = "Add";
dialog.CloseButtonText = "Cancel";
dialog.IsSecondaryButtonEnabled = false;
dialog.DefaultButton = ContentDialogButton.Primary;
dialog.Content = App.GetService<LibraryAddSongControl>();
ContentDialogResult result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
//Logic to handle Save command from Dialog
}
}
public void AddSong()
{
SqliteHelper.AddSong(new Song
{
SongTitle = Title,
SongDescription = Description,
SongAlbum = Album,
SongArtists = [$"{Artists}"],
SongDuration = (60 * Convert.ToInt32(Duration.Substring(0, 2)) + Convert.ToInt32(Duration.Substring(3, 2))),
SongURL = Url,
});
}
我尝试从 LibraryViewModel 调用 LibraryAddSongViewModel 的 AddSong 方法,但这导致绑定到 Tedtbox 内容的 ViewModel 属性为空,并且每当我在文本框中键入内容时都不会更新
我的最终问题是,当对话框本身也有像文本框这样的元素需要处理的值时(在我的例子中,只需将它们保存到数据库),什么是一个干净的 MVVM 风格的解决方案来显示来自 Xaml1 的对话框
我会尽量避免使用与 UI 相关的代码,例如
XamlRoot
中的 ViewModel
。让我给你举个例子:
public class Item
{
public string Name { get; set; }
}
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Item> _items =
[
new Item { Name = "Item 1" },
new Item { Name = "Item 2" },
new Item { Name = "Item 3" },
];
[RelayCommand]
private void AddNewItem(Item item) => Items.Add(item);
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private MainViewModel ViewModel { get; } = new();
private async void AddNewItemMenuFlyoutItem_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
var contentDialog = new ContentDialog
{
Title = "Add New Item",
Content = new TextBox { PlaceholderText = "Enter item name" },
PrimaryButtonText = "Add",
CloseButtonText = "Cancel",
XamlRoot = XamlRoot,
};
if (await contentDialog.ShowAsync() is not ContentDialogResult.Primary ||
(contentDialog.Content as TextBox)?.Text is not string itemName ||
itemName.Length is 0)
{
return;
}
var newItem = new Item { Name = itemName };
ViewModel.AddNewItemCommand.Execute(newItem);
}
}
<Page
x:Class="App2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:App2"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<ListView ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Item">
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem
Click="AddNewItemMenuFlyoutItem_Click"
Text="Add new item" />
</MenuFlyout>
</ListView.ContextFlyout>
</ListView>
</Grid>
</Page>