如何从 UserControl 访问 WPF 页面中的公共变量?

Posted

技术标签:

【中文标题】如何从 UserControl 访问 WPF 页面中的公共变量?【英文标题】:How to access a public variable in a WPF Page from a UserControl? 【发布时间】:2021-12-06 16:46:57 【问题描述】:

我有一个承载用户控件的 WPF 页面。 UserControl 有一个按钮,它可以做一些事情并最终想要更改绑定到页面上 DataGrid 的列表。不幸的是,我无法访问此列表/数据网格。似乎页面可以访问托管用户控件中的所有公共变量,但用户控件无法访问页面中的任何变量...有人可以帮我吗?

这是我页面的 xaml:

<Page x:Class="SchrauberDB.View.Werkzeug_Window"
  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:local="clr-namespace:SchrauberDB.Controls"
  xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
  Title="Werkzeug_Window"
  mc:Ignorable="d" 
  FontFamily="../Fonts/Poppins/#Poppins"

  d:DesignWidth="1400" d:DesignHeight="950"
  >

    <StackPanel
                Orientation="Horizontal"
                VerticalAlignment="Center"
                HorizontalAlignment="Right"
                Margin="0,0,0,0.2" Width="404"
                >

        <Button 
            Height="45" Width="150"  
            Margin="20,0,0,0"
            Background="x:Null" 
            Click="FilterMaske_Click"
            >

            <StackPanel Margin="0,0,0,0" HorizontalAlignment="Left" Width="90">

                <Image Height="30" Width="30" HorizontalAlignment="Left" Source="Assets\filter-outline_black.png" />
                <Label Content="Filter" HorizontalAlignment="Center" Margin="20,-30,0,0"  FontSize="14" Height="30"/>
            </StackPanel>
        </Button>

    </StackPanel>

    <DataGrid x:Name="DataGrid" 
              AlternatingRowBackground="#F8F8F8" Margin="40,60,40,45"
              Grid.Column="2" Grid.ColumnSpan="10"
              Grid.Row="1" Grid.RowSpan="12"
              RowHeight="47"
              ColumnHeaderHeight="47"
              Padding="0"
              FontSize="18"
              ColumnWidth="150"
              HorizontalScrollBarVisibility="Visible"
              AutoGenerateColumns ="False"
              >
        
        <DataGrid.Columns >
            <DataGridTextColumn x:Name="filter" Header="Filter" FontSize="18"   IsReadOnly="True" Binding="Binding Path=filter"></DataGridTextColumn>
            <DataGridTextColumn x:Name="bemiNr" Header="Bemi Nr" FontSize="18"   IsReadOnly="True" Binding="Binding Path=bemiNr"></DataGridTextColumn>
        </DataGrid.Columns>

    </DataGrid>

    <local:FilterWerkzeug x:Name="Filtermaske" Padding="10" Visibility="Collapsed"></local:FilterWerkzeug>
</Grid>

在页面后面的代码中,我有一个绑定到 DataGrid 的列表:

public List<DataModel.Werkzeug> werkzeuge = new List<DataModel.Werkzeug>();
//Some logic...
DataGrid.ItemsSource = werkzeuge;

在 UserControl 后面的代码中,我有一个最终想要更新列表 werkzeuge 的按钮

这是 UserControl 的 xaml:

<UserControl x:Class="SchrauberDB.Controls.FilterWerkzeug"
         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:local="clr-namespace:SchrauberDB.Controls"
         mc:Ignorable="d" 
         d:DesignHeight="200" d:DesignWidth="1100"
         FontFamily="../Fonts/Poppins/#Poppins">

<Grid Background="#FEFFFF" >
    <StackPanel Grid.Row="0" Grid.Column="0">
        <TextBlock>filter</TextBlock>
        <ComboBox x:Name="cbFilter" Width="150"/>
    </StackPanel>

    <Button Height="45" Width="130" Margin="17.6,14.4,10,7" Click="Button_Click">
        <TextBlock  FontSize="14" FontWeight="Medium">
            Filter
        </TextBlock>
    </Button>
</Grid>
</UserControl>

【问题讨论】:

【参考方案1】:

这是一个非常简单的例子:

页面数据上下文是一个带有公共列表的 Model 对象 页面显示该列表 页面显示一个用户控件 此用户控件有一个按钮 该按钮更新Model 中的列表

型号:

using System.Collections.ObjectModel;

namespace WpfApp1

    public class Model
    
        public ObservableCollection<string> Strings  get;  = new();
    

用户控件 XAML:

<UserControl x:Class="WpfApp1.UserControl1"
             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:wpfApp1="clr-namespace:WpfApp1"
             mc:Ignorable="d"
             d:DataContext="d:DesignInstance wpfApp1:Model"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button Content="Add" Click="ButtonBase_OnClick" />
    </Grid>
</UserControl>

用户控制代码:

using System;
using System.Windows;

namespace WpfApp1

    public partial class UserControl1
    
        public UserControl1()
        
            InitializeComponent();
        

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        
            var model = DataContext as Model ?? throw new InvalidOperationException();
            model.Strings.Add(DateTime.Now.ToString("O"));
        
    

页面 XAML:

<Page x:Class="WpfApp1.Page1"
      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:local="clr-namespace:WpfApp1"
      mc:Ignorable="d"
      d:DesignHeight="450" d:DesignWidth="800"
      Title="Page1"
      d:DataContext="d:DesignInstance local:Model">

    <Page.DataContext>
        <local:Model />
    </Page.DataContext>

    <StackPanel>
        <local:UserControl1 />
        <ItemsControl ItemsSource="Binding Strings" />
    </StackPanel>

</Page>

页面代码:

namespace WpfApp1

    public partial class Page1
    
        public Page1()
        
            InitializeComponent();
        
    

XAML 窗口:

<NavigationWindow x:Class="WpfApp1.MainWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                  mc:Ignorable="d"
                  Source="Page1.xaml">
</NavigationWindow>

窗口代码:

namespace WpfApp1

    public partial class MainWindow
    
        public MainWindow()
        
            InitializeComponent();
        
    

【讨论】:

你有MVVM的M和V,为什么不加VM呢? 是的,你是对的。我将实现视图模型。谢谢 我会得到一个 InvalidOperationException:“由于对象的当前状态,该操作无效”。这是为什么呢? @Antelito 这是因为我检查了数据上下文确实是一个模型,如果我没有,你会得到一个 NullReferenceException 。在您的项目中复制我的代码时,您肯定忘记了一些东西,我发布的示例运行良好。【参考方案2】:

您可以在可视化树中找到父Page,然后直接访问DataGrid

private void Button_Click(object sender, RoutedEventArgs e)

    Werkzeug_Window page = FindParent<Werkzeug_Window>(this);
    page.DataGrid = ...;


private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject

    var parent = VisualTreeHelper.GetParent(dependencyObject);

    if (parent == null) return null;

    var parentT = parent as T;
    return parentT ?? FindParent<T>(parent);

【讨论】:

以上是关于如何从 UserControl 访问 WPF 页面中的公共变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何从UserControl访问父级的DataContext

如何从 xaml 访问 UserControl 内的按钮?

(WPF)如何打开另一个窗口

UserControl在wpf中使用父元素?

如何使 WPF UserControl 中的一个 DependencyProperty 更改另一个 [重复]

WPF UserControl响应窗体的PreviewKeyDown事件