如何从我的.cs文件中访问WPF MainWindow控件

27

我是一名新手,正在为看似简单的任务而苦苦挣扎。 我应该如何从另一个cs文件修改MainWindow TextBlock的属性。 提供准确的代码解决方案将非常有帮助。

以下是精简的代码。 我使用静态类是否会给我带来额外的问题?

在文件中:MainWindow.xaml

<Window x:Class="TestApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" Margin="107,71,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
    </Grid>
</Window>

在文件:MainWindow.xaml.cs 中。
namespace TestApp1  
{  
public partial class MainWindow : Window  
    {  
        public MainWindow()  
        {  
            InitializeComponent();  
            TextBlock1.Text = "Setting Text from MainWindow";  
            MyProgram.myProgramStart();  
        }  
    }  
}  

在文件:CodeFile1.cs中

namespace TestApp1
{
    public static class MyProgram
    {
        public static void myProgramStart()
        {
            // ... blah blah blah

            // I want to do something like follows, but won't compile
            MainWindow.TextBlock1.Text = "Setting Text from My Program";
        }
    }
}  
8个回答

51

因为没有其他人真正回答了这个问题,所以我将告诉你如何实现你想要的,但请听取那些说在真实应用程序中您会使用MVVM的张贴者的建议。然而,有时候您需要做您所要求的事情,所以您需要的代码是:

((MainWindow)System.Windows.Application.Current.MainWindow).TextBlock1.Text = "Setting Text from My Program";

11
很好的回答,但我只想用粗体字重复一遍:“在实际应用中,确实要使用MVVM。” - Sharky
5
非常好的回答,但是如果有人真的错过了......"但要听取那些说在真实应用中你会使用MVVM的发布者的建议"。 - Mateen Ulhaq
5
我不理解奖励建议的概括。有几个原因需要那种方式的代码后台,例如自定义style.xaml的代码后台使用过程性代码来处理样式中xaml无法算法化的方法。 - j riv
有时,MVVM选项需要很多代码来代替一行“糟糕”的代码。 TheStatusbar.MouseUp += ((MainWindow)System.Windows.Application.Current.MainWindow).SBMouseUp; - Paul McCarthy
@jriv 这不是关于代码后台的问题。它甚至与MVVM无关。它涉及到您应用程序中控件的引用方式。从很多方面来看,这是一种代码异味。MVVM只是众多解决方案之一,可以优雅地避免这种情况发生。当然,MVVM和代码后台并不互斥。也没有人说过这一点。 - user21970328

17

您可以使用MVVM来轻松实现此目标。不应该直接使用另一个类中的名称访问视图中的控件。您必须使用绑定属性。

首先,添加一个类。这将是您的ViewModel。在此类中添加要绑定到View输入控件的属性。

学生 ViewModel

public class Student
{
    public string Name
    {
        get { return "Setting Text from My Program"; }
    }
}

应用配置文件(App.Config)

现在,您需要将此视图模型添加为资源到您的应用配置文件(App.Config)中。首先,在包含VM的app.config中添加名称空间引用[xmlns:local="clr-namespace: WpfApplication2]。通过指定您的视图模型类名(student),将VM类添加为资源。

<Application x:Class="WpfApplication2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml"
             xmlns:local="clr-namespace:WpfApplication2">
    
    <Application.Resources>
        <local:Student x:Key="Student" />
    </Application.Resources>
</Application>

MainWindow.xaml

使用添加到App.config的资源键设置DataContext,并将绑定设置为在Student View Model中定义的属性。

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        DataContext="{StaticResource Student}"
        Title="MainWindow" Height="350" Width="525">
    
    <Grid>
        <TextBlock Text="{Binding Name}" Height="23" HorizontalAlignment="Left" Margin="127,124,0,0" Name="textBlock1" VerticalAlignment="Top" Width="214" />
    </Grid>
</Window>

请注意,如果绑定源不是DependencyObject的扩展,它必须实现INotifyPropertyChanged接口,即使没有触发PropertyChanged事件。如果源扩展了DependencyObject,则用作绑定源/目标的属性必须实现为依赖属性。否则,数据绑定将导致内存泄漏。 - user21970328

4

基本上有超过2-3种方法。以下是比较容易理解和处理的方法。你可以通过(1),(2),(3),(4)的代码访问MainWindow控件。

在文件:MainWindow.xaml.cs中。

  public partial class MainWindow
  {
   internal static MainWindow Main; //(1) Declare object as static
   public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
       Main =this; //(2) Defined Main (IMP)
      var AnyClassORWindow=new Class1(); //Initialize another Class
      AnyClassORWindow.ShowValue();
    }
  }

在文件:Class1.cs 中
  internal class Class1 : MainWindow //(3) Inherited
    {
      internal void Display()
        {
           MessageBox.Show(Main.TextBox1.Text); //(4) Access MainWindow Controls by adding 'Main' before it.
         }
     }

注意事项:

  1. 最佳实践是在窗口加载后而不是在构造函数中使用代码(2)。
  2. 构造函数中的代码(2)可能会导致运行时问题。
  3. 另一种简单的方法是通过将“ref MainWindow_field”传递给每个类的构造函数或将“(MainWindow) Application.Current.MainWindow”分配给静态Main来使用。

1
使用 MVVM 模式 访问控件属性并修改它们:
public class Student
{
    public Student()
    {
    }

    public string Name
    {
        get { return "Setting Text from My Program"; }
    }
}

在代码后台设置XAMLDataContext

this.DataContext = new Student();

将Text属性绑定到名称:
<TextBlock Text="{Binding Name}"/>

请注意,如果绑定源不是扩展DependecyObject,它必须实现INotifyPropertyChanged,即使PropertyChanged事件没有被触发。如果源扩展了DependencyObject,在作为绑定源/目标的属性必须作为依赖属性来实现。否则数据绑定会导致内存泄漏。 - user21970328

0
为了扩展Nathan所说的,我使用了安全转换:
(System.Windows.Application.Current.MainWindow as MainWindow)?.TextBlock1.Text = "Setting Text from My Program";

请注意 Nathan 给出的答案上的注释。这不是理想的,但它可以工作。

0

你可以在你的应用程序中的任何地方使用 App.Current.MainWindow :)

你还应该使用 FindChild 方法来查找你的组件...

MyProgram.cs :

 public static class MyProgram
    {
        public static void myProgramStart()
        {
            // ... blah blah blah

            // I want to do something like follows, but won't compile
            //MainWindow.TextBlock1.Text = "Setting Text from My Program";
            var tb = FindChild3<TextBlock>(App.Current.MainWindow, "TextBlock1");
            tb.Text = "Setting Text from My Program";
        }

        public static T FindChild3<T>(DependencyObject depObj, string childName)
            where T : DependencyObject
        {
            // Confirm obj is valid. 
            if (depObj == null) return null;

            // success case
            if (depObj is T && ((FrameworkElement)depObj).Name == childName)
                return depObj as T;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);

                //DFS
                var obj = FindChild3<T>(child, childName);

                if (obj != null)
                    return obj;
            }

            return null;
        }

    }

MainWindow.xaml.cs:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            MyProgram.myProgramStart();
        }
    }

这个技巧对我有用 :)

祝你好运 ✌️


0

你需要创建一个MainWindow的实例。

但是在WPF应用程序中,不需要这样做,因为它会自动完成。除非你有特定的原因要这样做(我对此表示怀疑,因为你提出了这个问题并且说你是新手)。


0
关于为什么它无法编译,我会假设您收到的编译器错误是...
"需要非静态字段、方法或属性 'TestApp1.MainWindow.TextBlock1' 的对象引用"
这是因为您试图访问一个 TextBlock1,就像它是静态的一样。正如 @JeffRSon 所述,首先创建 MainWindow 类的实例即可解决此问题。
// Create an instance of MainWindow
var mainWindow = new MainWindow();
mainWindow.TextBlock1.Text = "Setting Text from My Program";

我猜你可能也想显示窗口...

mainWindow.ShowDialog();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接