0

我想制作一个带有 ListBox 导航的 UWP 应用程序,其所选项目确定框架的内容。When the selection changes, the frame content should change. 我找到了一个示例(下面的方法 1),但它在后面的代码中使用了事件处理程序。我想通过这个项目学习 MVVM,因此想使用 MVVM 解决这个问题。我是 MVVM 的新手,我目前的理解是,为了将 View 与 ViewModel 分离,ViewModel 不应引用任何特定于 View 的内容。这是一个正确的理解吗?我能想到的使用 ViewModel 更改框架视图的唯一方法是基本上将事件处理程序代码移动到 ViewModel 并将 Frame 传递给构造函数(下面的方法 2)。但这违反了我对 ViewModel 与 View 关系的理解,因为 ViewModel 引用了 View 中事物的特定实例;此外,这似乎是毫无意义的开销,而且它会给组织带来很少的好处。

您将如何针对此问题实施 MVVM 解决方案?或者这是使用事件处理程序更好的情况?

方法 1 - 事件处理程序: 此代码基于 Microsoft 提供的示例。(这里是相关代码的链接:https ://github.com/microsoft/Windows-universal-samples/tree/master/Samples/Playlists/cs )

public sealed partial class MainPage : Page
{
    List<Scenario> scenarios = new List<Scenario>
    {
        new Scenario() { Title = "Scenario 1", ClassType = typeof(Scenario1) },
        new Scenario() { Title = "Scenario 2", ClassType = typeof(Scenario2) }
    };

    public MainPage()
    {
        this.InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        ScenarioControl.ItemsSource = scenarios;
    }

    private void ScenarioControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ListBox scenarioListBox = sender as ListBox;
        Scenario s = scenarioListBox.SelectedItem as Scenario;
        if (s != null)
        {
            ScenarioFrame.Navigate(s.ClassType);
        }
    }
}

public class Scenario
{
    public string Title { get; set; }
    public Type ClassType { get; set; }

    public override string ToString()
    {
        return Title;
    }
}

<!-- MainPage.xaml -->
<Grid>
    <SplitView x:Name="Splitter" IsPaneOpen="True" DisplayMode="Inline">
        <SplitView.Pane>
            <RelativePanel>
                <ListBox x:Name="ScenarioControl" SelectionChanged="ScenarioControl_SelectionChanged"/>
            </RelativePanel>
        </SplitView.Pane>
        <RelativePanel>
            <Frame x:Name="ScenarioFrame" />
        </RelativePanel>
    </SplitView>
</Grid>

方法 2 - MVVM(?):

<!-- MainPage.xaml -->
<Grid>
    ...
    <ListBox x:Name="ScenarioControl" SelectionChanged="{x:Bind MyViewModel.SwitchScenario}"/>
    ...
</Grid>
// MainPage.xaml.cs
...
    public MainPage()
    {
        this.InitializeComponent();
        MyViewModel = new MyViewModel(ScenarioFrame);
    }
...
    MyViewModel MyViewModel { get; set; }
}

// MyViewModel.cs
public class MyViewModel
{
    public MyViewModel(Frame scenarioFrame)
    {
        ScenarioFrame = scenarioFrame;
    }

    public void SwitchScenario(object sender, SelectionChangedEventArgs e)
    {
        ListBox scenarioListBox = sender as ListBox;
        Scenario s = scenarioListBox.SelectedItem as Scenario;
        if (s != null)
        {
            ScenarioFrame.Navigate(s.ClassType);
        }
    }

    public Frame ScenarioFrame { get; set; }
}
4

2 回答 2

0

您将如何针对此问题实施 MVVM 解决方案?或者这是使用事件处理程序更好的情况?

要实现 MVVM 导航,您可以参考Template 10Template Studio工作流程。

Click在模板 10 中,它将事件与导航方法绑定。

<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
                     RelativePanel.AlignRightWithPanel="True"
                     RelativePanel.AlignTopWithPanel="True" Text="Main Page">

    <!--  secondary commands  -->
    <controls:PageHeader.SecondaryCommands>
        <AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
        <AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
        <AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
    </controls:PageHeader.SecondaryCommands>

</controls:PageHeader>

视图模型

 public void GotoDetailsPage() =>
     NavigationService.Navigate(typeof(Views.DetailPage), Value);

 public void GotoSettings() =>
     NavigationService.Navigate(typeof(Views.SettingsPage), 0);

NavHelper在 Template Studio 中,它使用类导航。

<winui:NavigationViewItem x:Uid="Shell_Main" Icon="Document" helpers:NavHelper.NavigateTo="views:MainPage" />
<winui:NavigationViewItem x:Uid="Shell_Blank" Icon="Document" helpers:NavHelper.NavigateTo="views:BlankPage" />
<winui:NavigationViewItem x:Uid="Shell_MediaPlayer" Icon="Document" helpers:NavHelper.NavigateTo="views:MediaPlayerPage" />
<winui:NavigationViewItem x:Uid="Shell_WebView" Icon="Document" helpers:NavHelper.NavigateTo="views:WebViewPage" />


<ic:EventTriggerBehavior EventName="ItemInvoked">
                <ic:InvokeCommandAction Command="{x:Bind ViewModel.ItemInvokedCommand}" />
            </ic:EventTriggerBehavior>

视图模型

private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args)
{
    if (args.IsSettingsInvoked)
    {
        NavigationService.Navigate(typeof(SettingsPage));
        return;
    }

    var item = _navigationView.MenuItems
                    .OfType<WinUI.NavigationViewItem>()
                    .First(menuItem => (string)menuItem.Content == (string)args.InvokedItem);
    var pageType = item.GetValue(NavHelper.NavigateToProperty) as Type;
    NavigationService.Navigate(pageType);
}
于 2019-06-24T07:55:52.227 回答
0

当您的模型属性更改时,您将需要 PropertyChangedNotification - 任何绑定到模型属性的内容都会自动更新。

Bindings.Update() 有时也是你的朋友。

如果您要拥有一个 View 和一个 ViewModel,您需要将 View 的 DataContext 设置为您要绑定的 ViewModel 或 Model 的实例。

于 2019-06-23T11:47:07.397 回答