如何在静态c#窗口中打开WPF窗口并从文本框读取

问题描述 投票:0回答:1

我想通过代码隐藏调用 WPF 窗口并将其显示在我的应用程序中,无论我在应用程序中的哪个位置。我失败的是,在我单击按钮之前,不允许调用窗口的静态方法在代码中继续。确定或取消。我现在无法处理这个问题。 然后,“确定”按钮应该从文本框中读取一个值,然后在方法中对其进行处理。打开 WPF 窗口时,应在文本框中输入一个值(如果可用)。

我尝试过以下方法:

我的 WPF 窗口。

<Grid Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <!-- Textbox -->
        <RowDefinition Height="Auto" />
        <!-- Buttons -->
    </Grid.RowDefinitions>

    <TextBox Name="textBoxInput" IsEnabled="True" Width="175" Height="30" />
    
    <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Transparent">
        <Button Name="OkBtn" Content="Ok" Click="OkBtn_Click" Width="80" Height="30" Margin="0,0,15,0" />
        <Button Name="CancelBtn" Content="Cancel" Click="CancelBtn_Click" Width="80" Height="30" />
    </StackPanel>
</Grid>

我的任何类的静态方法:

            string enteredText = null;


            TextboxAndButtons textBoxWindow = new TextboxAndButtons();
            textBoxWindow.Owner = System.Windows.Application.Current.Windows.Cast<Window>().LastOrDefault(x => x.IsActive) ?? System.Windows.Application.Current.MainWindow;
            textBoxWindow.SetText(setValue);

            ManualResetEvent waitEvent = new ManualResetEvent(false);

            
            textBoxWindow.Closed += (sender, e) =>
            {
                enteredText = textBoxWindow.GetText();
                waitEvent.Set(); 
            };

            
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                textBoxWindow.Show();
            });
            
            waitEvent.WaitOne();

            if (!string.IsNullOrEmpty(enteredText))
            {
                string a = enteredText;
            }               

我还尝试了其他方法,使用 ShowDialog 而不是 Show,但是我无法在文本框中输入任何内容。按钮“确定”会做出反应,但文本框不允许任何输入。 现在我尝试使用任务。运行,但随后它不知何故挂在无限循环中,并且窗口未完全显示。文本框和按钮丢失。

我的目标是将其构建为 MVVM,但我也对它能正常工作的变体感到满意。

感谢您的帮助。

c# wpf textbox window static-methods
1个回答
0
投票

我假设,您想要某种

MessageBox
,但用于用户输入而不是通知。

有大量“如何创建自定义

MessageBox
”的示例。对于当前问题,我将使用我的自己的的想法。

因此,首先您需要使用

TextBox
创建一个单独的窗口(视图),用于用户输入和确定/取消按钮。

XAML:

<Window x:Class="YourNamespace.Views.UserTextInputWindow"
        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"
        xmlns:viewModels="clr-namespace:YourNamespace.ViewModels"
        mc:Ignorable="d"
        Title="UserTextInputWindow" 
        Name="This"
        WindowStyle="None"
        WindowStartupLocation="CenterScreen"
        Height="200" 
        Width="400">
    <Window.DataContext>
        <viewModels:UserTextInputViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="24"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="32"/>
        </Grid.RowDefinitions>
        <TextBlock Text="Please, input some text if you wish" 
                   FontWeight="Bold"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"/>
        <TextBox Grid.Row="1" 
                 Margin="5" 
                 Text="{Binding UserText}"/>
        <StackPanel Grid.Row="2" 
                    Orientation="Horizontal" 
                    HorizontalAlignment="Center">
            <Button Content="OK" 
                    Width="100" 
                    Height="24" 
                    Margin="10,0"
                    Command="{Binding OkCommand}" 
                    CommandParameter="{Binding ElementName=This}"/>
            <Button Content="Cancel" 
                    Width="100" 
                    Height="24" 
                    Margin="10,0"
                    Command="{Binding CancelCommand}" 
                    CommandParameter="{Binding ElementName=This}"/>
        </StackPanel>

    </Grid>
</Window>

那个看起来像下面这样。 Ofc,您可以根据需要定制。

从 XAML 中可以看出,我使用

UserTextInputViewModel
类作为此窗口的 ViewModel,其隐藏代码是:

using System.Windows;
using System.Windows.Input;
using YourNamespace.Commands;

namespace YourNamespace.ViewModels
{
    public class UserTextInputViewModel : INotifyPropertyChanged
    {
        private string _userText;
        // Our binded to TextBox property, which stores user's input
        public string UserText
        {
            get => _userText;
            set
            {
                _userText = value;
                NotifyPropertyChanged();
            }
        }

        // A command for OK button
        // ParameterizedCommand is custom implementation of ICommand interface
        private ICommand _okCommand;
        public ICommand OkCommand => _okCommand ??= new ParameterizedCommand(commandParameter => (commandParameter as Window)?.Close() );

        // A command for Cancel button
        // ParameterizedCommand is custom implementation of ICommand interface
        private ICommand _cancelCommand;
        public ICommand CancelCommand => _cancelCommand ??= new ParameterizedCommand(commandParameter =>
        {
            // On Cancel we should "reset" user input
            UserText = string.Empty;
            (commandParameter as Window)?.Close();
        });

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

ParameterizedCommand
实施:

using System;
using System.Windows.Input;

namespace YourNamespace.Commands
{
    public class ParameterizedCommand : ICommand
    {
        private readonly Action<object> _action;

        public ParameterizedCommand(Action<object> action) => _action = action;

        public bool CanExecute(object parameter) => true;
        public void Execute(object parameter) => _action(parameter);

        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }
}

因此,事实上,ViewModel 只是存储用户输入并通过关闭窗口(并在取消时重置用户输入)来处理“确定”/“取消”按钮单击。

最后,

UserTextInputWindow
本身的代码隐藏,我隐藏了默认构造函数并用静态方法替换了它的调用
Show

using System.Windows;
using YourNamespace.ViewModels;

namespace YourNamespace.Views
{
    public partial class UserTextInputWindow
    {
        // Hiding default constructor by making it private
        private UserTextInputWindow() => InitializeComponent();

        // Introducing static method, which should display window and return
        // user input string.
        // Note, that this is blocking call (because of ShowDialog inside).
        // Also note, that I add some useless parameter "topmost" just to avoid
        // conflicts with standart Window.Show method
        public static string Show(bool topmost = false)
        {
            // Securing ourselves by checking access from non-STA thread
            return Application.Current.Dispatcher.CheckAccess() 
                 ? ShowWindowAndGetUserInput() 
                 : Application.Current.Dispatcher.Invoke(ShowWindowAndGetUserInput);
        }
    
        private static string ShowWindowAndGetUserInput()
        {
            var userText = string.Empty;

            var  window = new UserTextInputWindow();
            window.Closing += delegate
            {
                if (window.DataContext is UserTextInputViewModel vm)
                    userText = vm.UserText;
            };

            window.ShowDialog();

            return userText;
        }
    }
}

用法与

MessageBox
非常相似。它也可以从任何线程安全地使用。

var userInput = UserTextInputWindow.Show();
if (string.IsNullOrEmpty(userInput)) { /* User input nothing or cancel */ }

不确定这是纯粹且正确的 MVVM 方法,但它确实有效。欢迎纠正我。

© www.soinside.com 2019 - 2024. All rights reserved.