簡體   English   中英

在 ViewModel 和更新視圖之間共享數據

[英]Share data between ViewModels and Update View

我只需要一點點幫助,因為我的第二個觀點沒有自我更新。

只是有兩個視圖和相應的視圖模型。 在 ViewA 中,我按下按鈕,在我的 UserController(或 CurrentContext)中設置 CurrentUser 屬性

class 實現了棱鏡 BindableBase(女巫包括 INotiefyPropertyChanged),並且 Fody 和 PropertyChanged/Fody 已正確安裝和設置。

 public class UserController : BindableBase
{

    private static UserController instance;

    public User CurrentUser { get; set; }


    public static UserController Instance
    {
        get
        {
            if (instance == null)
                instance = new UserController();
            return instance;
        }
    }
    private UserController()
    {
    }

}

在 ViewModel A 中設置 CurrentUser:

 private void ShowDetails(User user)
    {
        UserController.Instance.CurrentUser = user;
    }

嘗試在 ViewModel B 中顯示用戶:

public User CurrentUser => UserController.Instance.CurrentUser; 

視圖B:

<Page.DataContext>
    <viewModels:UserInfoPageViewModel />
</Page.DataContext>

<Grid Background="Black">
    <StackPanel>
        <TextBox Text="{Binding CurrentUser.UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
</Grid>

我沒有看到為什么 ViewB 沒有更新?

基本的:

MainViewModel -->  UserController --> UserDetailViewModel

你在哪里有這個

public User CurrentUser =>

就目前而言,這是一次性的。

您必須使用“CurrentUser”在代碼中引發屬性更改事件

編輯:

我認為您可以通過對用戶 object 使用不同的方法來減少問題。

與其使用 static 實現 singleton 模式,不如在任何大型項目中進行依賴注入更為常見。 在商業應用程序中幾乎是一個給定的自動化測試,如 TDD 是一種預期。

應用程序的通常模式是,在您登錄之前,您最初什么都不做。您輸入名稱和密碼並通過身份驗證。 您的用戶在數據庫中查找。 您可以在應用程序中執行的操作以某種方式獲得授權。

然后您就可以開始使用該應用程序了。 用戶界面已構建,您可以做一些事情。

如果您切換用戶,那么您將拆除所有內容並重新開始。 只需要注銷並關閉應用程序是很常見的。 重新啟動它。 因為對於大多數 PC,當用戶 1 停止使用它時,他會退出 windows。用戶 B 出現並登錄。應用程序在其間關閉。 考慮到這一點,我假設如果用戶可以切換用戶,您會想要處理現有的視圖模型。

我把一些代碼放在一起,只使用 mainviewmodel 來實例化兩個傳入用戶的視圖模型。 我們可以想象將我們的視圖模型解析為一個依賴注入容器,而不是直接解析出該視圖模型。

為此,我還使用了 mvvm 社區工具包。 部分原因是我更喜歡它,但也因為我認為任何讀者都更清楚一些代碼生成正在進行以及它在哪里。 這些屬性在部分 class 中使用 inpc 實現驅動屬性代碼生成。

我本可以添加另一個控件來獲得焦點,但因為它只有一個文本框,所以我有 UpdateSourceTrigger=PropertyChanged。 整個目的是演示通知與實例 class 一起使用,因此在我鍵入時更改的文本塊是一個加號。

    <Window.DataContext>
            <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
         <StackPanel>
            <TextBlock Text="Input:"/>
            <TextBox Text="{Binding TheUser.UserName, UpdateSourceTrigger=PropertyChanged}"/>
            <TextBlock Text="First:"/>
            <ContentPresenter Content="{Binding FirstViewModel}"/>
            <TextBlock Text="Second:"/>
            <ContentPresenter Content="{Binding SecondViewModel}"/>
        </StackPanel>
    </Grid>
</Window>

用戶

public partial class User : ObservableObject
{
    [ObservableProperty]
    private string userName = string.Empty;
}

正如我所提到的,使 User 成為實例 class 的單個實例在這里以一種快速而骯臟的方式實現。 DI 容器中的 singleton 生命周期聲明將是執行此操作的正確方法。

public partial class MainWindowViewModel : ObservableObject
{

    [ObservableProperty]
    private User theUser = new User();

    [ObservableProperty]
    public object firstViewModel;

    [ObservableProperty]
    public object secondViewModel;

    public MainWindowViewModel()
    {
        firstViewModel = new AViewModel(theUser);
        secondViewModel = new BViewModel(theUser);
    }
}

因此,所有三個視圖模型都有一個 User 實例。

兩個用戶控件中只有一個文本塊

  <TextBlock Text="{Binding TheUser.UserName}"/>

兩種視圖模型都非常相似,但是它們中沒有發生屬性更改的提升。 沒有屬性。

public partial class AViewModel : ObservableObject
{
    public User TheUser { get; set; }

    public AViewModel(User user)
    {
        TheUser = user;
    }
}

Bviewmodel 幾乎完全一樣。

當我旋轉它並在文本框中鍵入時,更改會立即在文本塊中看到。

在此處輸入圖像描述

不確定您希望 go 進行多遠的更改,但也許這是需要考慮的事情,或者會幫助其他人稍后遇到這個問題。

對“為什么”的簡短回答是因為當UserController.CurrentUser被更改時,它不會告訴任何人。 通常,如果您想通知其他元素屬性更改,您需要使用INotifyPropertyChanged接口並在屬性的設置器中調用PropertyChanged事件。

你的模式有點復雜,因為你甚至沒有綁定到正在更改的屬性,而是綁定到視圖 model 屬性,該屬性從UserController獲取它的值。 因此,不僅UserController不會在CurrentUser更改時告訴任何人,而且您的視圖 model 同樣不知道這何時發生,也無法通知其綁定目標。

總是有多種方法可以做這樣的事情,但我會這樣處理:

  1. UserController需要實現INotifyPropertyChanged如果它還沒有通過BindableBase

  2. 對於UserController.CurrentUser ,而不是簡單的get; set; get; set; ,您需要按如下方式重寫它:

private User _currentUser;
public User CurrentUser 
{ 
    get => _currentUser;
    set
    {
        _currentUser = value;
        NotifyPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentUser));
    }
}
  1. 在您的視圖 model 中,公開一個UserController屬性,而不是CurrentUser屬性:
public UserController UserController => UserController.Instance;
  1. 在 XAML:
    <StackPanel>
        <TextBox Text="{Binding UserController.CurrentUser.UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>

這樣做是將UserController視為CurrentUser的真實來源,而不是您的視圖 model。之所以有效,是因為UserController (顯然)在應用程序的生命周期內永遠不會改變,因此您不必擔心綁定路徑的一部分,而UserController.CurrentUser的設置器將負責在該屬性更改時通知 WPF 更新綁定。

相比之下,您的視圖模型的CurrentUser屬性就是我所說的派生屬性; 這些綁定起來很棘手,因為即使UserController正確地為CurrentUser提供了屬性更改通知,您仍然需要某種方式在UserController.CurrentUser發生更改時通知每個視圖 model,然后他們必須依次通知每個人他們自己的,派生的CurrentUser屬性已更改。 基本上你需要兩層綁定,這不容易正確完成(如果你不小心,可能會導致 memory 泄漏)。

暫無
暫無

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

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