【问题标题】:WPF frame content not updating when source Uri is changed through binding通过绑定更改源 Uri 时 WPF 框架内容未更新
【发布时间】:2014-12-05 04:00:54
【问题描述】:

我已经在这个问题上工作了几天,似乎找不到任何适合我的应用程序的东西。

我的问题是我正在尝试使用包含按钮的用户控件来绑定到更改框架的源 Uri 的命令(两者都显示在同一个窗口中)。当我单击一个按钮时,它正在更改 ViewModel 中的 Uri,但框架不会更改页面以反映这一点。我相信它要么由于其绑定方式而没有接受更改,要么有一些东西阻止它更改框架中显示的页面。

我使用的 MVVM 模式一直很棒,直到我不得不开始处理导航。任何帮助将不胜感激!

导航用户控件视图按钮:

<Button Name="BtnMainDash" Content="Main Dashboard" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="180" Command="{Binding MainDashboard}"/>
<Button Name="BtnAccount" Content="Account" HorizontalAlignment="Left" Margin="10,40,0,0" VerticalAlignment="Top" Width="180" Command="{Binding EditAccount}"/>
<Button Name="BtnProjects" Content="Projects" HorizontalAlignment="Left" Margin="10,70,0,0" VerticalAlignment="Top" Width="180" Command="{Binding ProjectScreen}"/>

主窗口框架:

<Frame x:Name="FmePages" Margin="200,30,-0.4,0.4"
               Source="{Binding Path=CurrentPage, Mode=OneWay, UpdateSourceTrigger=PropertyChanged }"
               NavigationUIVisibility="Hidden"/>

Button ICommands(除了每个调用不同的 Uri 更改命令外都一样):

using System;
using System.Windows.Input;
using ScrumManagementApplication.Pages.MainWindow.ViewModel;

namespace ScrumManagementApplication.Pages.MainWindow.Commands
{
    class LoadEditAccount : ICommand
    {
        private readonly NavigationViewModel _navigationViewModel;

        public LoadEditAccount(NavigationViewModel navigationViewModel)
        {
            _navigationViewModel = navigationViewModel;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter)
        {
            return _navigationViewModel.CommandsEnabled;
        }

        public void Execute(object parameter)
        {
            _navigationViewModel.LoadEditAccount();
        }
    }
}

视图模型:

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using ScrumManagementApplication.Pages.MainWindow.Commands;
using ScrumManagementApplication.SessionData;
using MessageBox = System.Windows.MessageBox;

namespace ScrumManagementApplication.Pages.MainWindow.ViewModel
{
    public class NavigationViewModel : INotifyPropertyChanged, INotifyPropertyChanging
    {
        public bool CommandsEnabled = false;

        public NavigationViewModel()
        {
            MainDashboard = new LoadMainDashboard(this);
            EditAccount = new LoadEditAccount(this);
            ProjectScreen = new LoadProjectScreen(this);
            LogOut = new LoadLogOut(this);

            CommandsEnabled = true;

            LoadEditAccount();
        }

        #region ICommands

        public ICommand MainDashboard { get; private set; }
        public void LoadMainDashboard()
        {
            _currentPage = null;
            _currentPage = new Uri("pack://application:,,,/Pages/MainWindow/View/MainDashboardView.xaml", UriKind.Absolute);
        }

        public ICommand EditAccount { get; private set; }
        public void LoadEditAccount()
        {
            _currentPage = null;
            _currentPage = new Uri("pack://application:,,,/Pages/EditUserDetailsPage/View/EditUserDetailsView.xaml", UriKind.Absolute);
        }

        public ICommand ProjectScreen { get; private set; }
        public void LoadProjectScreen()
        {
            _currentPage = null;
            _currentPage = new Uri("pack://application:,,,/Pages/ProjCreationPage/View/ProjectCreationPage.xaml", UriKind.Absolute);
        }

        public ICommand LogOut { get; private set; }
        public void LoadLogOut()
        {
            var dialogResult = MessageBox.Show("Are you sure you want to log out?", "Log Out", MessageBoxButton.YesNo);

            if (dialogResult == (MessageBoxResult) DialogResult.Yes)
            {
                App.Current.Shutdown();
            }
        }

        #endregion // ICommands

        #region MainFrame

        private Uri _currentPage;
        public Uri CurrentPage
        {
            get { return _currentPage; }
            set
            {
                _currentPage = value;
                OnPropertyChanged("CurrentPage");
            }
        }

        #endregion // MainFrame

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion // INotifyPropertyChanged Members

        public event PropertyChangingEventHandler PropertyChanging;

        protected void OnPropertyChanging(String propertyName)
        {
            PropertyChangingEventHandler handler = PropertyChanging;

            if (handler != null)
            {
                handler(this, new PropertyChangingEventArgs(propertyName));
            }
        }
    }
}

感谢所有帮助,即使它不能完全解决我的问题,任何帮助我更接近解决方案的东西都是好的!

提前致谢

【问题讨论】:

  • 您必须引发PropertyChanged 事件,以便绑定系统更新目标属性。您只是在更改支持字段。请改用您编写的自动引发 PropertyChanged 事件的属性设置器。
  • @mikez 嗨,迈克,感谢您的帮助。我想我可能已经盯着屏幕太久了,因为我不是 100% 你的意思,你能详细说明一下吗?谢谢!
  • LoadEditAccount 为例,您正在更改_currentPage 而没有引发CurrentPage 的属性更改事件。您需要在此之后的某个时间致电OnPropertyChanged("CurrentPage")。你可以把它写成CurrentPage = new Uri(...);,它会调用你的setter并引发事件。
  • @mikez 我已经尝试了这两种方法,但它仍然没有更改框架中的页面,我在 MessageBoxes 中添加以确保 Uri 正在正确更新并且那里没有问题。无论出于何种原因,框架似乎都不想改变它的初始来源

标签: c# wpf mvvm data-binding visual-studio-2013


【解决方案1】:

而不是这样做

_currentPage = //some value;

这样做:

CurrentPage = //some value

当您更改属性而不是支持字段时将引发该事件。

编辑

另一个建议是创建一个命令类,因为您的所有命令都将字符串值设置为属性。您可以使用CommandParameter 获取按钮名称。基于该设置Uri

【讨论】:

  • 嗨 Piyush,感谢您的帮助。我已经尝试过了,但它似乎不起作用,我认为我不会错误地执行您的指示,因为它们相当简单,尽管只是为了仔细检查,我应该如何处理 get;放;当前页面?再次感谢!
  • getset 对于 CurrentPage 将保持不变。但是您应该在 LoadProjectScreenLoadEditAccount 等方法中将值设置为属性 (CurrentPage) 而不是支持字段 (_currentPage)。
  • 这也不起作用,由于某种原因,它似乎没有更新源,除非设置它的命令在初始化方法 NavigationViewModel() 中运行,一旦它被设置在那个它不再改变。我想不出任何原因,因为我在该方法内外使用相同的命令。
【解决方案2】:

遇到了类似的问题,有一个主窗口,其中有一个框架,例如,当我打开一个 openfiledialog 并关闭它时,即使我调用 PropertyChanged() 和绑定,这个框架(也有其他一些)也没有更新财产具有正确的价值。对我来说唯一的解决方案是删除绑定并使用 NavigationService 处理内容。如果您需要此属性,您必须使用其他一些方法,您可以执行以下操作:

    private Page _dataExplorerContent;
    public Page DataExplorerContent
    {
        get { return _dataExplorerContent; }
        set
        {
            if(value != null)
            {
                contentFrame.Navigate(value)
                SetField(ref _dataExplorerContent, value);
            }
        }
    }

【讨论】:

    猜你喜欢
    • 2011-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多