C# 从WPF中的主窗口访问UserControl中的对象

C# 从WPF中的主窗口访问UserControl中的对象,c#,wpf,xaml,C#,Wpf,Xaml,我有这个简单的XAML,如何从主窗口更改TextBlock中UserControl1的Text属性 <Window x:Class="RefactorXAML.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:refa

我有这个简单的XAML,如何从
主窗口
更改
TextBlock
UserControl1
Text
属性

<Window x:Class="RefactorXAML.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:refactorXaml="clr-namespace:RefactorXAML"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="100"></RowDefinition>
            <RowDefinition Height="100"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid>
            <refactorXaml:UserControl1></refactorXaml:UserControl1>
        </Grid>
        <Grid Grid.Row="1">
            <Button>Change Text</Button>
        </Grid>
    </Grid>
</Window>

<UserControl x:Class="RefactorXAML.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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

        <TextBlock x:Name="MyTextblock">StackOverflow</TextBlock>
</UserControl>

更改文本
堆栈溢出

用户控件设置名称:

<refactorXaml:UserControl1 x:Name="MyUserControl1" />
或者短得多:

var text = MyUserControl1.FindName("MyTextblock") as TextBlock;

if (text != null)
    text.Background = Brushes.Red;

如果您讨厌
MVVM
,可以添加公共属性。但是MVVM将使更新源和目标变得容易。在这种情况下,您可以使用
dependencProperty

无MVVM

这是你的UC的外观

 public partial class UserControl1 : UserControl
            {
                public UserControl1()
                {
                    InitializeComponent();
        
                }
                public string TextBlockContent { get; set; }
            }
您的主窗口是XAML

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:refactorXaml="clr-namespace:WpfApplication3"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="100"></RowDefinition>
            <RowDefinition Height="100"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid>
            <refactorXaml:UserControl1 Name="MyUserControl1"></refactorXaml:UserControl1>
        </Grid>
        <Grid Grid.Row="1">
            <Button Click="ButtonBase_OnClick">Change Text</Button>
        </Grid>
    </Grid>
</Window>

WPF中的控件通常意味着某种“黑匣子”。你不应该(通常也不应该)关心里面发生了什么。相反,如果希望允许通信,可以定义该控件的用户可以与之通信的公共接口

在WPF中,此公共接口通常使用。您也可以对许多事情使用普通(CLR)属性,但通常您希望允许数据绑定之类的事情,然后需要依赖性属性。定义依赖属性比定义普通属性要复杂一些,因为它们需要额外完成一些工作。在VisualStudio中,有一个
propdp
代码段,它可以帮助您在几个简单的步骤中添加所有样板代码

因此,对于您的用户控件,我们有一个字符串属性,我们希望将其公开给外部。因此,在该用户控件的代码隐藏中,我们将该属性定义为依赖属性:

public partial class ExampleUserControl : UserControl
{
    public static readonly DependencyProperty SomeTextProperty =
        DependencyProperty.Register("SomeText", typeof(string), typeof(ExampleUserControl), new PropertyMetadata("default value"));

    public string SomeText
    {
        get { return (string)GetValue(SomeTextProperty); }
        set { SetValue(SomeTextProperty, value); }
    }

    // this is the same old stuff
    public ExampleUserControl()
    {
        InitializeComponent();
    }
}
这就是我们要声明属性并使其可供组件用户使用所需的全部操作。现在我们只需要添加功能。由于不涉及一些额外的逻辑,但是您只想将值委托给其他组件的属性(TextBlocks的
Text
property),因此我们可以使用数据绑定。这在大多数情况下都适用:

<UserControl x:Class="SomeNamespace.ExampleUserControl" …
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel>
        <TextBlock Text="{Binding SomeText}" />
    </StackPanel>
</UserControl>
由于它是一个依赖属性,我们还可以使用数据绑定从其他地方获取值(例如,使用MVVM时的视图模型):


最后,我们有了一个可重用组件,它与所使用的应用程序完全分离。应用程序不需要知道该组件的实现,组件也不需要知道它将在何处使用。

不幸的是,我讨厌MVVM:(MVVM实际上与此无关。当您创建用户控件时,您创建的是一个独立的组件。该组件具有一些接口,您可以使用不同的方式使用这些接口。一种是使用数据绑定,另一种是使用代码(例如,在代码隐藏中).Data binding非常有用,即使您不使用MVVM。(使用Data binding并不意味着您正在使用MVVM。Data binding是允许MVVM的核心部分,但Data binding以前就已经存在,并且在没有MVVM的情况下被广泛使用).@poke感谢poke的澄清,我知道我在抵制所有这些新概念,但最终我会放弃。我记得我很难理解OOP的新概念,现在没有它我什么都做不了。@Vahid别担心,慢慢来。我知道一开始真的很困惑,尤其是自从你倾向于一次发现所有的东西,这给了你一堵需要克服的巨大的新东西墙。但是一旦你通过了它,你就会看到更干净的代码、分离关注点和提高速度的所有好处,你会想知道为什么你之前拒绝这样做:)这是真的,当我拒绝学习OOP时,我收到了同样的建议,现在我想知道我是如何使用OOP完成所有这些的。所以诀窍是首先在代码隐藏中创建一个用户控件的实例。对吗?@Vahid:不,用户控件的实例是在XAML中创建的,你只需在
控件
中的代码隐藏中获得它。稍后,尝试to使用
FindName()查找控件
函数。这不是一个很好的解决方案……没有分离关注点,而且您正在向家长泄露实现细节。@poke:我同意,OP说他讨厌MVVM,所以我展示了最简单的解决方案。我刚刚发现,如果我在
主窗口中创建
UserControl1
代码的实例,我可以请不要更改它的任何属性。但正如@poke所说,这违反了关注点分离规则。我现在很难理解所有这些。但我知道这是正确的方法。谢谢,但是什么是
TextBlockContent
?这是您定义的新属性吗?我想正确地更改
文本ty?很遗憾,这个答案缺少CLR属性
TextBlockContent
和用户控件的文本块之间的连接。@是的,我猜是这样的。
public partial class ExampleUserControl : UserControl
{
    public static readonly DependencyProperty SomeTextProperty =
        DependencyProperty.Register("SomeText", typeof(string), typeof(ExampleUserControl), new PropertyMetadata("default value"));

    public string SomeText
    {
        get { return (string)GetValue(SomeTextProperty); }
        set { SetValue(SomeTextProperty, value); }
    }

    // this is the same old stuff
    public ExampleUserControl()
    {
        InitializeComponent();
    }
}
<UserControl x:Class="SomeNamespace.ExampleUserControl" …
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <StackPanel>
        <TextBlock Text="{Binding SomeText}" />
    </StackPanel>
</UserControl>
<my:ExampleUserControl SomeText="Foo" />
<my:ExampleUserControl SomeText="{Binding SomeTextProperty}" />
<my:ExampleUserControl x:Name="myControl" SomeText="Foo" />
<Button Click="ChangeText_Click" Content="Change text" />
private void ChangeText_Click (object sender, RoutedEventArgs e)
{
    myControl.SomeText = "Bar";
}