簡體   English   中英

WPF 綁定在屬性更改時不起作用

[英]WPF binding doesn't work when property changes

這是我在這里的第一個問題,請理解。 我已經在這個問題上花費了幾個小時來挖掘對我沒有用的東西,也許有人會向我解釋這個奇怪的(對我來說)問題? 我用 MVVM 在 WPF 中制作了我的應用程序

我使用用戶控件進入 MainWindow.xaml ,它通過綁定加載視圖:

<UserControl Content="{Binding CurrentView}" />

MainWindow DataContext 是 MainViewModel,它派生自 BaseViewModel,我從中設置和獲取 CurrentView 並實現 INotifyPropertyChanged。

第一個 CurrentView 是 LoginViewModel - 它正確加載到 MainViewModel 的構造函數中並設置視圖(Usercontrol Loginview.xaml)。 而且我不明白為什么當我從這個加載的 LoginViewModel 更改 CurrentView 屬性時(它肯定會改變 - 我檢查了它並且 NotifyPropertyChanged 引發了) - 我的視圖沒有改變 - 它仍然是 LoginView,但應該是 WelcomeView。 但是,當我使用 MainViewModel 中的相同代碼更改相同的屬性時 - 我的視圖會正確更改。 有人可以指出哪里有錯誤? 是否不可能從 MainViewModel 外部更改 CurrentView 屬性,即使它不是 MainViemodel 的一部分,而是另一個 class 或什么? 我在這里缺少什么?

代碼:

    public class BaseViewModel : NotifyPropertyChanged 
    {
        private object? _currentView;
        public object? CurrentView
        {
            get { return _currentView; }
            set
            {
                _currentView = value;
                OnPropertyChanged(nameof(CurrentView));
            }
        }
    }
    public class MainViewModel : BaseViewModel
    {
        public LoginViewModel LoginViewModel { get; set; }
        public WelcomeViewModel WelcomeViewModel { get; set; }
    [..]
        public ICommand LoginCommand { get; set; } //- this works 
        public MainViewModel()
        {
            LoginViewModel = new();
            WelcomeViewModel = new();
            CurrentView = LoginViewModel;
            // COMMANDS
            LoginCommand = new RelayCommand(o => DoLogin(), o => CanLogin()); // this works
        }
        private bool CanLogin()
        {
            return true;
        }
        private void DoLogin()
        {
            CurrentView = WelcomeViewModel;
        } 
    }
    public class LoginViewModel : BaseViewModel
    {
    [...]
        public WelcomeViewModel WelcomeViewModel { get; set; }
        // COMMANDS PROPERTIES
        public ICommand LoginCommand { get; set; }
        public LoginViewModel()
        {
            WelcomeViewModel = new();
            LoginCommand = new RelayCommand(o => DoLogin(), o => CanLogin());
        }
        private bool CanLogin()
        {
            return true;
        }
        private void DoLogin()
        {
            MessageBox.Show("Login!");  // message box test showes
            // here will be some authentication
            CurrentView = WelcomeViewModel; // property CurrentView changes
            // CurrentView = new MainViewModel().WelcomeViewModel; // this way also doesn't work
        }
    }

最后是來自 UserControl LoginView.xaml 的 XAML (命令運行正常,屬性 CurrentView 發生變化,但視圖保持不變:

<Button
    Width="200"
    Height="50"
    Margin="10"
    Command="{Binding LoginCommand}"
    Content="Login"
    FontSize="18" />
<!--  Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, 
Path=DataContext.LoginCommand}" THIS WORKS! -->

App.xaml 有:

 <DataTemplate DataType="{x:Type vievmodels:LoginViewModel}">
            <viewscontents:LoginView/>
        </DataTemplate>
 <DataTemplate DataType="{x:Type vievmodels:WelcomeViewModel}">
            <viewscontents:WelcomeView/>
        </DataTemplate>

您犯的錯誤是有兩個CurrentView變量。 一個在MainViewModel中,另一個在LoginViewModel中。 這兩個類都是從BaseViewModel派生的,但這並不意味着它們共享相同的CurrentView實例。 兩者都有CurrentView變量的新實例。 這意味着只有一個綁定到頁面的DataContext

在您將哪個CurrentView分配給DataContext時,我看不到它。 所以我無法完全回答這個問題。

但看起來你有 1 個 window 填充了 2 個控件。 要解決這個問題,您應該創建一個僅包含CurrentView的第三個 ViewModel。 在使用UserControl的父級上使用此實例。 並將其他 ViewModel 用於用戶控件本身。

問題是:如何設置 DataContext.... 這樣做。

主視圖模型:

    public class MainViewModel : BaseInpc
    {
        #region CurrentContent
        private object? _currentContent;
        public object? CurrentContent { get => _currentContent; set => Set(ref _currentContent, value); }

        private RelayCommand _setCurrentCommand;
        public RelayCommand SetCurrentCommand => _setCurrentCommand
            ??= new RelayCommand(content => CurrentContent = content);
        #endregion

        public LoginViewModel LoginViewModel { get; } = new LoginViewModel();
        public WelcomeViewModel WelcomeViewModel { get; } = new WelcomeViewModel();

        public MainViewModel()
        {
            CurrentContent = WelcomeViewModel;
        }
    }
    public class WelcomeViewModel: BaseInpc // Derived not from MainViewModel!
    {
        // Some Code
    }

    public class LoginViewModel: BaseInpc // Derived not from MainViewModel!
    {
        // Some Code
    }

在應用程序資源中創建 MainViewModel 的實例:

    <Application.Resources>
        <local:MainViewModel x:Key="mainVM"/>
    </Application.Resources>

在 Windows XAML 中:

<Window ------------
        ------------
        DataContext="{DynamicResource mainVM}">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:LoginViewModel}">
            <local:LoginViewUserControl/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type WelcomeViewModel}">
            <local:WelcomeViewUserControl/>
        </DataTemplate>
    </Window.Resources>
    <ContentControl Content="{Binding CurrentContent}"/>

切換 CurrentContent 的按鈕示例:

    <Button Command="{Binding SetCurrentCommand, Source={StaticResource mainVM}}"
            CommandParameter="{Binding LoginViewModel, Source={StaticResource mainVM}}"/>

BaseInpc 和 RelayCommand 類。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM