了解将数据从页面传递到用户控件

Posted

技术标签:

【中文标题】了解将数据从页面传递到用户控件【英文标题】:Understanding passing data from a page to a user control 【发布时间】:2020-12-29 07:58:27 【问题描述】:

我一直在努力解决如何在页面和 UserControl 之间传递数据,并且可以使用一些说明来说明我可能做错了什么。如果我删除代码的数据部分并仅用 TextBlocks 替换它,我知道 UserControl 正在工作。

我知道 NetWorth 正在显示,但就是不明白为什么没有传递 List<Account>。我不知道我在这里做错了什么。

PanelNavigation.xaml

        <!-- Navigation Section -->
        <ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Auto" CanContentScroll="True" Grid.Row="1" Margin="0,0.333,0,0" Grid.ColumnSpan="2">
            <UserControls:NavigationPanel DataContext="Binding Accounts"/>
        </ScrollViewer>

        <!-- Networth Panel -->
        <Grid Grid.Row="2" Height="40">
            <Border BorderThickness="0,1,0,0" BorderBrush="StaticResource MenuBarBrush" Background="StaticResource BackgroundLightBrush">
                <Grid Margin="5,5,10,5">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>

                    <TextBlock Margin="10,1,10,1" FontSize="16" FontWeight="Normal" Grid.Column="0" Foreground="#FF346C9C" Text="Net Worth" />
                    <TextBlock FontSize="16" FontWeight="Normal" Grid.Column="1"
                               HorizontalAlignment="Right" Text="Binding NetWorth, Converter=StaticResource CurrencyConverter"
                               Foreground="Binding NetWorth, Converter=StaticResource ChangeColor"/>
                </Grid>
            </Border>
        </Grid>

PanelNavigation.xaml.cs


using APTest.ViewModels;
using System.Windows.Controls;

namespace APTest.Views

    /// <summary>
    /// Interaction logic for PanelNavigation.xaml
    /// </summary>
    public partial class PanelNavigation : Page
    
        public PanelNavigation()
        
            InitializeComponent();

            this.DataContext = new VMNavigationPanel(this);

        
    

NavigationPanel.xaml

<UserControl x:Class="APTest.UserControls.NavigationPanel"
             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:UserControls="clr-namespace:APTest.UserControls"
             mc:Ignorable="d" 
             d:DesignHeight="800" d:DesignWidth="400">

    <StackPanel>
        <StackPanel>
            <ItemsControl x:Name="DataTest" ItemsSource ="Binding Accounts">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="Binding TotalBalance" />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </StackPanel>
    </StackPanel>

</UserControl>
    

NavigationPanel.xaml.cs

using System.Windows.Controls;

namespace APTest.UserControls

    public partial class NavigationPanel : UserControl
    
        public NavigationPanel()
        
            InitializeComponent();
        
    

最后

VMNavigationPanel.cs

using AccountingPlus.ViewModels;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace APTest.ViewModels

    class VMNavigationPanel  : BaseViewModel
    
        private Page mPage;
        private ObservableCollection<Account> accounts;
        public ObservableCollection<Account> Accounts 
            get => accounts;
            set 
                accounts = value;
                OnPropertyChanged();
            
        

        public double NetWorth  get; set;  = 127492.30;

        public VMNavigationPanel(Page page)
        
            mPage = page;

            Accounts = new ObservableCollection<Account>();

            Account test = new Account();
            test.TotalBalance = 100234.23;

            Accounts.Add(test);
        
    

    public class Account
    
        public double TotalBalance get; set;
    

BaseViewModel.cs

using PropertyChanged;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace APTest

    [AddINotifyPropertyChangedInterface]
    class BaseViewModel : INotifyPropertyChanged
    

        public event PropertyChangedEventHandler PropertyChanged = (Sender, e) =>  ;

        protected void OnPropertyChanged([CallerMemberName] string name = null)
        
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        
    

【问题讨论】:

Itemssource 应该绑定 Accounts。 Observablecollection 而不是列表。始终在视图模型上实现 inotifypropertychanged 并在 setter 中引发 propertychanged。不要将页面等 ui 元素传递到视图模型中。 如果您不需要像向导那样向前向后导航,则框架和页面会产生开销。 social.technet.microsoft.com/wiki/contents/articles/… @Andy 你能解释一下将 UI 元素(如页面)传递给 ViewModel 是什么意思吗?我有一个带有 UIControl 的页面,它通过 嵌入到 mainWindow 中,仍在争论这是否是一件好事。至于 INotifyPropertyChanged,我实际上有,但将其删除以进行测试并将其放回去没有任何区别,而 ObservableCollection 没有任何区别。 @AndyAnd 我还在学习,那么你推荐什么而不是框架/页面。举一个我想要达到的目标的例子。想想带有信息和链接列表的侧边导航。然后,这些链接可以将更多内容打开到主要内容部分。我知道我需要在这里使用一个框架,因为我没有看到任何其他方式来更改该主要内容区域,并且该页面必须具有 UserControls。 Vmnavigationpanel 是一个视图模型。它应该完全独立于 ui 工作,并且页面是 ui。我在上面的评论中放了一个链接。这不是随机的。阅读。然后首先谷歌视图模型。取字框。取词页。把它们放在一个盒子里。合上盖子..想想 contentcontrol 和 usercontrol。具有数据类型的数据模板定义了您为视图模型获得的用户界面。 【参考方案1】:

首先,您需要模型中的属性来绑定对象。不能绑定字段。

这样您的 Account 类应该是这样的:

public class Account

    public double TotalBalance  get; set; 


还请记住,要在 WPF 中更新 UI,我们需要使用 ObservableCollections。例如,如果您单击一个按钮并将一个项目添加到您的 Accounts 集合中,则该项目将在您的集合中,但在 UI 中,如果您使用 List 集合,您将看不到更新的结果,这就是您需要使用 ObservableCollections 的原因。 https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1?view=netcore-3.1

另一件事是您的绑定拼写错误。在您的 NavigationPanel.xaml 中,它应该是 TotalBalance。

<TextBlock Text="Binding TotalBalnce" />

这是您的视图模型的工作代码:

我还为您实现了 INotifyPropertyChanged 接口,但没有与任何其他属性一起使用。

class VMNavigationPanel : INotifyPropertyChanged


    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
    
        property = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    

    private Page mPage;

    public double NetWorth  get; set;  = 127492.30;

    private ObservableCollection<Account> accounts;
    public ObservableCollection<Account> Accounts
    
        get => accounts;
        set
        
            accounts = value;
        
    

    public VMNavigationPanel(Page page)
    
        mPage = page;

        Accounts = new ObservableCollection<Account>();

        Account test = new Account();
        test.TotalBalance = 100234.23;

        Accounts.Add(test);
    



public class Account

    public double TotalBalance  get; set; 


【讨论】:

已经进行了这些更改,属性更改位于基类中,我一开始省略了,但没有区别。我已经按照建议反映了帖子的当前更改,并回到了我开始摆弄之前的内容。你没有做任何事情,我不是唯一一个我做了不同的事情并改变了你所拥有的东西,如果它是 null 来创建集合,get 有一个条件。而不是将它放在构造函数中。我移到构造函数没有任何区别。

以上是关于了解将数据从页面传递到用户控件的主要内容,如果未能解决你的问题,请参考以下文章

如何将数据从 MainWindow 传递到 MainWindow 内的用户控件?

WPF 将对象从窗口的用户控件传递到另一个窗口的用户控件

从 wpf 页面中的组合框(在用户控件中)获取数据

WPF:将变量从父 xaml 传递到用户控件

输入错误的用户名和/或密码后,如何将用户从登录控件重定向到错误页面?

如何在运行时将值从窗口传递到用户控件?