我想通过代码隐藏调用 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,但我也对它能正常工作的变体感到满意。
感谢您的帮助。
我假设,您想要某种
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 方法,但它确实有效。欢迎纠正我。