如何在 WinUI 3 (MVVM) 中显示 ContentDialog

问题描述 投票:0回答:1

基本上我的问题是:我正在尝试在我的 WinUI 3 应用程序中显示 ContentDialog。

打开对话框的命令是由我的“主页”(LibraryPage)上的上下文菜单按钮触发的 对话框的内容是一堆文本框,我在另一个xaml中定义它们(LibraryAddSongControl)

但我无法直接从 ViewModel 打开对话框,因为我无法从那里访问所需的 XamlRoot。我无法访问对话框文本框内容,因为它们位于 LibraryAddSongViewModel

LibraryPage.xaml
<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>
LibraryAddSongControl.xaml
<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
        }

    }
LibraryAddSongViewModel 中的逻辑获取文本框的内容并将它们保存在 sqlite DB 中
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 的对话框

c# xaml winui-3 contentdialog
1个回答
0
投票

我会尽量避免使用与 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>
© www.soinside.com 2019 - 2024. All rights reserved.