wpf 在视图模型和更新视图之间共享数据

tjvv9vkg  于 2023-01-27  发布在  其他
关注(0)|答案(2)|浏览(261)

我只是需要我微小的帮助,因为我的第二个视图不是更新本身。
只有两个视图和相应的视图模型。在ViewA中,我按下一个按钮,设置我的UserController(或CurrentContext)中的CurrentUser属性
该类实现棱镜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中设置当前用户:

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
2eafrhcq

2eafrhcq1#

简单地说,“为什么”是因为UserController.CurrentUser被更改时,它不会告诉任何人,通常如果你想通知其他元素属性的更改,你需要使用INotifyPropertyChanged接口,并在属性的setter中调用PropertyChanged事件。
你的模式稍微复杂一点,因为你甚至没有绑定到正在被改变的属性,而是绑定到一个视图模型属性,该属性从UserController获取它的值。所以,不仅UserController不告诉任何人CurrentUser何时发生了改变,而且你的视图模型同样不知道什么时候发生了改变,也不能通知它的绑定目标。
做这样的事情总是有很多种方法,但我会这样做:

  1. UserController需要实现INotifyPropertyChanged(如果尚未通过BindableBase实现)
    1.对于UserController.CurrentUser,而不是简单的get; set;,您需要将其重写如下:
private User _currentUser;
public User CurrentUser 
{ 
    get => _currentUser;
    set
    {
        _currentUser = value;
        NotifyPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentUser));
    }
}

1.在视图模型中,公开UserController属性,而不是CurrentUser属性:

public UserController UserController => UserController.Instance;

1.在XAML中:

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

这样做的目的是将UserController作为CurrentUser的事实来源,而不是视图模型。(显然)在应用程序的生命周期中永远不会改变,因此您不必担心绑定路径的这一部分,而UserController.CurrentUser的setter将负责在该属性改变时通知WPF更新绑定。
相比之下,视图模型的CurrentUser属性就是我所说的派生属性;这些绑定是棘手的,因为即使UserController正确地给出了CurrentUser的属性改变通知,当UserController.CurrentUser已经改变时,你仍然需要某种方式来通知每个视图模型,然后它们将不得不依次通知每个人它们自己的、派生的CurrentUser属性已经改变。这并不容易正确执行(如果不小心,可能会导致内存泄漏)。

xzlaal3s

xzlaal3s2#

你有这个的地方

public User CurrentUser =>

这是一个一次性的约束力,因为它的立场。
您必须在代码中使用"CurrentUser"引发属性更改事件

    • 编辑:**

我认为您可以通过对User对象使用不同的方法来减少问题。
与使用static实现单例模式不同,在任何大规模的项目中使用依赖注入更为常见。在商业应用中,自动化测试(如TDD)是一种预期。
一个应用程序的通常模式是,在登录之前,你最初什么都不能做。你输入用户名和密码,然后通过身份验证。你的用户会在数据库中被查找。你在应用程序中可以做的事情会以某种方式获得授权。
然后你就可以开始使用这个应用了,用户界面是内置的,你可以做任何事情。
如果你切换用户,那么你会把所有的东西都拆下来,然后重新启动。这是很常见的,只是必须注销并关闭应用程序。重新启动它。因为对于大多数电脑,当用户1停止使用它时,他会注销windows。用户B出现并登录。应用程序在两者之间关闭。考虑到这一点,我假设如果用户可以切换用户,你会想处理现有的视图模型。
我把一些代码放在一起,只是使用mainviewmodel来示例化传入user的两个视图模型。我们可以想象将视图模型解析为依赖注入容器,而不是直接解析为视图模型。
我也使用了mvvm社区工具包,部分是因为我更喜欢它,但也因为我认为它对任何读者来说都更清楚一些代码生成正在进行以及它在哪里,属性驱动属性代码生成,inpc实现在一个partial类中。
我本来可以添加另一个控件来获取焦点,但由于它只有一个文本框,所以我有UpdateSourceTrigger = PropertyChanged。整个目的是演示通知与示例类一起工作,所以文本块在我键入时发生变化是一个优点。

<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成为示例类的单个示例是以一种快速而肮脏的方式实现的,在DI容器中声明单例生命周期将是实现这一点的正确方式。

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几乎完全一样。
当我把它旋转起来并在文本框中键入内容时,文本块中的更改会立即显示出来。

不知道你会想走多远的变化,但也许这是一些考虑或将帮助别人遇到这个问题后。

相关问题