【问题标题】:UserControl navigate in owning window wpfUserControl在拥有窗口wpf中导航
【发布时间】:2016-06-19 17:17:18
【问题描述】:

在 XAML/WPF 中,我有一个包含 Frame 的主窗口,我打算在其中放置一个用于给定应用程序视图的用户控件。

<Window x:Class="MyApp.MainWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ignore="http://www.galasoft.ch/ignore"
    mc:Ignorable="d ignore"
    DataContext="{Binding Main, Source={StaticResource Locator}}">
    <Grid x:Name="LayoutRoot">
        <Frame Source="Main/MainUserControl.xaml" Name="Main" />
    </Grid>
</Window>

现在我想将此 Frame 导航到 MainUserControl 中的其他源:

<UserControl x:Class="MyApp.View.MainMenu.MainMenuUserControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:lex="http://wpflocalizeextension.codeplex.com"
         xmlns:command="clr-namespace:MyApp.Command"
         mc:Ignorable="d" 
         Style="{StaticResource Localizable}"
         DataContext="{Binding MainMenu, Source={StaticResource Locator}}">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Button Content="{lex:Loc About}" FontSize="28" Grid.Row="1" Command="NavigationCommands.GoToPage" CommandParameter="/Menu/AboutUserControl.xaml" />
    </Grid>
</UserControl>

但导航按钮 About 在执行期间保持不活动状态。我正确验证了 /Menu/AboutUserControl.xaml 存在。

我显然做错了什么。如何从用户控件中导航拥有窗口的框架?最好通过 XAML?

【问题讨论】:

    标签: wpf xaml


    【解决方案1】:

    我假设您使用的是 MVVM 框架。 (我在这里添加了关键元素,以防你没有)。

    您的 MainWindow.xaml 应该使用“ItemsControl”而不是“Frame”。框架可以工作,但更好的方法是像这样使用 ItemsControl:

    <!-- Main Frame -->
    <Grid Grid.Column="1" Margin="10" Name="MainWindowFrameContent">
        <ItemsControl ItemsSource="{Binding Path=MainWindowFrameContent}" >
    
            <!-- This controls the height automatically of the user control -->
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <UniformGrid Columns="1" IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
    

    在我的 MainWindow.cs 的构造函数中,我将窗口的 DataContext 设置为 MainViewModel:

    using myProject.ViewModel;
    
    public partial class MainWindow : Window
    {
        MainViewModel mMainViewModel;
    
        public MainWindow()
        {
            InitializeComponent();
    
            // Initialize MainViewModel and set data context to the VM
            mMainViewModel = new MainViewModel();
            DataContext = mMainViewModel;
        }
    }
    

    (我不确定下一部分是否必须是可观察的集合,但我已经实现了它并且它似乎运行良好。唯一的缺点是我需要在添加新的之前手动清除 ItemsControl用户控制)

    我的 MainViewModel 实现了名为“MainWindowFrameContent”的绑定。我的所有用户控件都在 MainViewModel.cs 代码中初始化。我为每个 UserControl 提供了一个额外的 ViewModel,并在将 UserControl 显示到主窗口之前将 UserControl 的 DataContext 分配给各个 ViewModel。

    我的 MainViewModel.cs:

    public class MainViewModel : ObservableObject
    {
        public MainViewModel()
        {
        }
    
        // This handles adding framework (UI) elements to the main window frame
        ObservableCollection<FrameworkElement> _MainWindowFrameContent = new ObservableCollection<FrameworkElement>();
        public ObservableCollection<FrameworkElement> MainWindowFrameContent
        {
            get
            {
                return _MainWindowFrameContent;
            }
            set
            {
                _MainWindowFrameContent = value;
                RaisePropertyChangedEvent("MainWindowFrameContent");
            }
        }
    
        // This handles opening a generic user control on the main window
        // The ICommand implementation allows me to bind the command of a button on the main window to displaying a specific page
        public ICommand MainWindowDataEntryView
        {
            get
            {
                return new DelegateCommand(_MainWindowDataEntryView);
            }
        }
        void _MainWindowDataEntryView(object obj)
        {
            DataEntryVM wDataEntryVM = new DataEntryVM();
            DataEntryView wDataEntryView = new DataEntryView();
            wDataEntryView.DataContext = wDataEntryVM;
    
            MainWindowFrameContent.Clear();
            MainWindowFrameContent.Add(wDataEntryView);
        }
    }
    

    然后你需要确保你有一个 ObservableObject.cs 作为你的项目的一部分:

    using System.ComponentModel;
    public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void RaisePropertyChangedEvent(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    您需要一个 DelegateCommand.cs 类作为您项目的一部分:

    using System;
    using System.Windows.Input;
    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _action;
    
        public DelegateCommand(Action<object> action)
        {
            _action = action;
        }
    
        public void Execute(object parameter)
        {
            _action(parameter);
        }
    
        public bool CanExecute(object parameter)
        {
            return true;
        }
    
    #pragma warning disable 67
        public event EventHandler CanExecuteChanged { add { } remove { } }
    #pragma warning restore 67
    }
    

    所以,这是一个有点冗长的解释,但一旦你设置了前面的项目,你可以添加一堆按钮到 MainWindow.xaml,将每个按钮绑定到一个命令,将一个新的 UserControl 添加到你的 ItemsControl .当您的 UserControl 显示时,您可以根据需要添加控件并使用它们。

    我希望这会有所帮助!

    【讨论】:

    • 首先:这会创建从视图模型到视图的强引用。据我了解,这是非常不可取的,viewmodel 应该是抽象的并且不知道实际的视图实现;第二:这使得视图模型紧密耦合,因为所有视图模型都需要知道主视图模型
    • 它确实在 VM 和视图之间创建了一个强引用。但是,它所做的只是控制在不同视图中显示哪个视图。或者,每个用户控件都可以像您所做的那样是资源,但是您需要一些代码来确定何时或显示什么(如果您有多个用户控件)。根据我在您上面的代码中看到的内容,我将验证您的用户控件上的 DataContext。它设置为“MainMenu”,主窗口上的 DataContext 为“Main”。如果您尝试从用户控件访问“主要”项目,这可能是问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多