My program is consists of a userControl
and a class
in C# winform environment. when I click on a button
in userControl
, an instance of my class
is created and the main process starts. I want to interact with my class
with userControl
through the program is running. for example, I want to show some massages from class
to a textBox
property in the designed userControl
. I can not instantiate from my userControl
in my class
because it is wrong and can not access running userControl
properties. So, How can I do it? Is it a good solution to set userControl
a singleton class? Do u recommend a better solution?
Edit: For more clarification: I want to call a method from the class
and pass an argument ( string ) into it. Then textBox
in the userControl
should show the passed argument! This procedure would be repeated whenever it has an output in the class
!
Update: here is code sample: In the class definition:
class MyClass
{
public MyClass()
{
//do sth
if (a condition)
{
//It is wrong to create an instance but I want to explain my purpose. It is what I need!
searchPanel sp = new searchPanel();
sp.showMessage("...");
}
}
}
And here is userControl:
public partial class searchPanel : UserControl
{
public searchPanel()
{
InitializeComponent();
}
public void showMessage(string message)
{
textBox.AppendText(message);
textBox.AppendText(Environment.NewLine);
}
}
So, How can I do it?
You could do this by implementing events . This way you can signal every class that is subscribing to your " class
" events when whatever you want happens. Imagine your " class
" just completes to sum up 10 integer numbers; you fire a event informing this operation just happened. Your " userControl
" class is subscribing to this event and will be able to execute a function to change whatever control in the form you want. By using events you keep class isolation, avoiding things like global state in your code.
Is it a good solution to set userControl a singleton class?
No, you should avoid using singleton as a pattern in your code. Above all using singleton as global state to expose your " class
" to " userControl
" is probably evidence of code smell
This is usually done using the WPF Command Pattern and binding ( Data Binding Overview ). This is the recommended approach.
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<SearchPanel />
</Window>
SearchPanel.xaml
<UserControl>
<UserControl.DataContext>
<ViewModel />
</UserControl.DataContext>
<StackPanel>
<Button Command="{Binding ShowMessageCommand}" />
<TextBlock Text="{Binding Message}" />
<StackPanel>
</UserControl>
SearchPanel.xaml.cs
public partial class SearchPanel : UserControl
{
public SearchPanel()
{
InitializeComponent();
}
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
private string message;
public string Message
{
get => this.message;
set
{
this.message = value;
OnPropertyChanged();
}
}
ICommand ShowMessageCommand => new RelayCommand(CreateMessage);
// Command callback
private void CreateMessage()
{
this.Message = "This is a message for display in the UI";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The helper class to create a reusable command:
RelayCommand.cs
/// <summary>
/// An implementation independent ICommand implementation.
/// Enables instant creation of an ICommand without implementing the ICommand interface for each command.
/// The individual Execute() an CanExecute() members are supplied via delegates.
/// <seealso cref="System.Windows.Input.ICommand"/>
/// </summary>
/// <remarks>The type of <c>RelayCommand</c> actually is a <see cref="System.Windows.Input.ICommand"/></remarks>
public class RelayCommand : ICommand
{
protected readonly Func<Task> executeAsyncNoParam;
protected readonly Action executeNoParam;
protected readonly Func<bool> canExecuteNoParam;
private readonly Func<object, Task> executeAsync;
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
/// <summary>
/// Raised when RaiseCanExecuteChanged is called.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, (param) => true)
{
}
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="executeNoParam">The execution logic.</param>
public RelayCommand(Action executeNoParam)
: this(executeNoParam, () => true)
{
}
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="executeAsync">The awaitable execution logic.</param>
public RelayCommand(Func<object, Task> executeAsync)
: this(executeAsync, (param) => true)
{
}
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="executeAsyncNoParam">The awaitable execution logic.</param>
public RelayCommand(Func<Task> executeAsyncNoParam)
: this(executeAsyncNoParam, () => true)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="executeNoParam">The execution logic.</param>
/// <param name="canExecuteNoParam">The execution status logic.</param>
public RelayCommand(Action executeNoParam, Func<bool> canExecuteNoParam)
{
this.executeNoParam = executeNoParam ?? throw new ArgumentNullException(nameof(executeNoParam));
this.canExecuteNoParam = canExecuteNoParam;
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
this.canExecute = canExecute;
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="executeAsyncNoParam">The awaitable execution logic.</param>
/// <param name="canExecuteNoParam">The execution status logic.</param>
public RelayCommand(Func<Task> executeAsyncNoParam, Func<bool> canExecuteNoParam)
{
this.executeAsyncNoParam = executeAsyncNoParam ?? throw new ArgumentNullException(nameof(executeAsyncNoParam));
this.canExecuteNoParam = canExecuteNoParam;
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="executeAsync">The awaitable execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Func<object, Task> executeAsync, Predicate<object> canExecute)
{
this.executeAsync = executeAsync ?? throw new ArgumentNullException(nameof(executeAsync));
this.canExecute = canExecute;
}
/// <summary>
/// Determines whether this RelayCommand can execute in its current state.
/// </summary>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute()
{
return this.canExecuteNoParam == null || this.canExecuteNoParam();
}
/// <summary>
/// Executes the RelayCommand on the current command target.
/// </summary>
public async void Execute()
{
if (this.executeAsyncNoParam != null)
{
await ExecuteAsync();
return;
}
this.executeNoParam();
}
/// <summary>
/// Executes the RelayCommand on the current command target.
/// </summary>
public async Task ExecuteAsync()
{
if (this.executeAsyncNoParam != null)
{
await this.executeAsyncNoParam();
return;
}
await Task.Run(() => this.executeNoParam());
}
/// <summary>
/// Determines whether this RelayCommand can execute in its current state.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to null.
/// </param>
/// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
/// <summary>
/// Executes the RelayCommand on the current command target.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to null.
/// </param>
public async void Execute(object parameter)
{
if (parameter == null)
{
Execute();
return;
}
if (this.executeAsync != null)
{
await ExecuteAsync(parameter);
return;
}
this.execute(parameter);
}
/// <summary>
/// Executes the RelayCommand on the current command target.
/// </summary>
/// <param name="parameter">
/// Data used by the command. If the command does not require data to be passed,
/// this object can be set to null.
/// </param>
public async Task ExecuteAsync(object parameter)
{
if (this.executeAsync != null)
{
await this.executeAsync(parameter);
return;
}
await Task.Run(() => this.execute(parameter));
}
}
I recommend some reading on MVVM and Commanding eg Basic MVVM and ICommand Usage Example or Commanding Overview
Alternative approach using a simple Button.Click
event handler:
SearchPanel.xaml
<UserControl>
<UserControl.DataContext>
<ViewModel />
</UserControl.DataContext>
<StackPanel>
<Button Click="ShowMessage_OnButtonClick" />
<TextBlock x:Name="MessageTextBlock" />
<StackPanel>
</UserControl>
SearchPanel.xaml.cs
public partial class SearchPanel : UserControl
{
public SearchPanel()
{
InitializeComponent();
}
private void ShowMessage_OnButtonClick(object sender, RoutedEventArgs e)
{
this.MessageTextBlock = "This is a message for display in the UI";
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.