如何在wpf mvvm中将现有页面导航到另一个页面

yqkkidmi  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(262)

在窗口中主窗口.xaml有一个框架

<Frame x:Name="Main" 
       Source="/View/Home.xaml"
       NavigationUIVisibility="Hidden"
       DataContext="{StaticResource MainWindowVM}">
</Frame>

在页面Home.xaml中有一个列表视图

<ListView x:Name="ListViewStore" 
          ItemsSource="{Binding ListStore}"
          SelectionMode="Single"
          ScrollViewer.HorizontalScrollBarVisibility="Disabled"
          ScrollViewer.VerticalScrollBarVisibility="Hidden">
    <ListView.ItemTemplate>
        <DataTemplate>
          <!--2 textblocks that display StoreName and StoreAddress-->
        </DataTemplate>
    </ListView.ItemTemplate>
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding StoreDetailCommand}"
                                   CommandParameter="{Binding ElementName=Home}">         </i:InvokeCommandAction>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListView>

家庭视图模型

public ICommand StoreDetailCommand { get; set; }
public HomeViewModel()
{
    StoreDetailCommand = new RelayCommand<Home>((p) => { return p.ListViewStore.SelectedItem == null ? false : true; }, (p) => displayStoreDetail(p));
}
void displayStoreDetail(Home parameter)
{
    
}

当用户单击ListViewStore中的商店项目时,主框架上将显示包含该商店项目信息的页面StoreDetail.xaml。如何导航并将数据传递到StoreDetail.xaml?我很高兴有可能在保持数据顺序的同时返回到以前的Home.xaml。感谢您花时间与我交谈。
我在谷歌上搜索了一下,但似乎没有什么想法

**编辑:**我在MainWindow.xaml.cs中创建静态示例,然后使用MainWindow.Instance.Main.NavigationService.Navigate(new Page()),它成功导航到新页面。我想知道每次我访问另一个视图中的元素时:我应该创建一个新的静态示例吗?在这种情况下,我想学习良好的编码习惯。谢谢

wswtfjt7

wswtfjt71#

评论有点长,但一些进一步的解释似乎是可取的。
我链接到的那篇文章对视图模型进行了过于简单的示例化。
在商业应用程序中,您需要一些更复杂的东西,但是让我们忽略依赖注入这样的大主题。
必须有一些东西示例化视图模型,这可能最好由工厂来处理,工厂可以像lambda一样简单。
大部分都是航空代码,所以很抱歉拼写错误。目的是给予你一个你需要做什么的味道。这是相当广泛的。
在您的应用中,您可以添加一个字典:

Dictionary<Type, Object> AllMyViewModels;

这将存储先前使用的视图模型的状态,这反过来又允许您存储用户在FooView中最后执行的任何操作的状态,因为您将您关心的所有内容绑定到FooViewModel。
假设这是一个主窗口和一个单窗口应用程序,它有主窗口视图模型,如果是一个小应用程序,它可能包含视图模型字典。
这是一个原型窗口,loginuc和useruc是用户控件。

Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:LoginViewModel}">
            <local:LoginUC/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:UserViewModel}">
            <local:UserUC/>
        </DataTemplate>
    </Window.Resources>
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Button Content="{Binding Name}"
                        Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                        CommandParameter="{Binding VMType}"
                    />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
        <ContentPresenter Grid.Column="1"
                          Content="{Binding CurrentViewModel}"
                          />
    </Grid>
</Window>

单击itemscontrol中的其中一个按钮,导航将通过切换CurrentViewModel来进行。这将被模板化到视图中的用户控件中。
这是我碰巧有的一些代码,所以可以很容易地通过使用社区mvvm工具包之类的东西来改进,这只是给予你(或任何人)一个想法。
主窗口视图模型

public class MainWindowViewModel : INotifyPropertyChanged
{
    public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";

    public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
        (
        new List<TypeAndDisplay>
        {
           new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
           new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
        }
        );

    private object currentViewModel;

    public object CurrentViewModel
    {
        get { return currentViewModel; }
        set { currentViewModel = value; RaisePropertyChanged(); }
    }
    private RelayCommand<Type> navigateCommand;
    public RelayCommand<Type> NavigateCommand
    {
        get
        {
            return navigateCommand
              ?? (navigateCommand = new RelayCommand<Type>(
                vmType =>
                {
                    CurrentViewModel = null;
                    CurrentViewModel = Activator.CreateInstance(vmType);
                }));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

实际上,type是用来示例化一个没有任何参数的视图模型的,在上面的代码中每次都是这样,不过我们可以使用字典来扩展它。
添加

private Dictionary<Type, Object> viewModels = new Dictionary<Type, Object>();

并更改命令

?? (navigateCommand = new RelayCommand<Type>(
                vmType =>
                {
                    CurrentViewModel = null;
                    if (viewModels.ContainsKey(vmType))
                    {
                        CurrentViewModel = viewModels[vmType];
                        return;
                    }
                    CurrentViewModel = Activator.CreateInstance(vmType);
                    viewModels.Add(vmType, CurrentViewModel);
                }));

现在,如果字典中没有视图模型,我们可以将其添加到字典中,如果有合适的视图模型,则返回字典中的内容。
您可以为每个传入参数的视图模型添加一个工厂。
其他可以改进的地方包括使用依赖注入。有些视图模型你可能不想保留状态。当你注册你的视图模型时,你可以在依赖注入容器中进行区分,并指定它们应该是单例的还是 transient 的(以及一个新的示例)。DI容器将取代字典。
通常,视图模型需要的参数是像repository这样的服务,而repository又有依赖项,您需要使用DI。

相关问题