I'm a newbie in C# bu I'm experienced Delphi developer.
In Delphi I can use same code for MenuItem and ToolButton using TAction.OnExecute event and I can disable/enable MenuItem and ToolButton together using TAction.OnUpdate event.
Is there a similar way to do this in C# without using external libraries? Or more - How C# developers share code between different controls?
Ok, may be I write my question in wrong way. I want to know not witch property to use (I know about Enabled property) but I want to know on witch event I should attach to if I want to enable/disable more than one control. In delphi TAction.OnUpdate event ocurs when Application is idle - is there similar event in C#?
Try the a modification of the command pattern:
public abstract class ToolStripItemCommand
{
private bool enabled = true;
private bool visible = true;
private readonly List<ToolStripItem> controls;
protected ToolStripItemCommand()
{
controls = new List<ToolStripItem>();
}
public void RegisterControl(ToolStripItem item, string eventName)
{
item.Click += delegate { Execute(); };
controls.Add(item);
}
public bool Enabled
{
get { return enabled; }
set
{
enabled = value;
foreach (ToolStripItem item in controls)
item.Enabled = value;
}
}
public bool Visible
{
get { return visible; }
set
{
visible = value;
foreach (ToolStripItem item in controls)
item.Visible = value;
}
}
protected abstract void Execute();
}
Your implementations of this command can be stateful in order to support your view's state. This also enables the ability to build "undo" into your form. Here's some toy code that consumes this:
private ToolStripItemCommand fooCommand;
private void wireUpCommands()
{
fooCommand = new HelloWorldCommand();
fooCommand.RegisterControl(fooToolStripMenuItem, "Click");
fooCommand.RegisterControl(fooToolStripButton, "Click");
}
private void toggleEnabledClicked(object sender, EventArgs e)
{
fooCommand.Enabled = !fooCommand.Enabled;
}
private void toggleVisibleClicked(object sender, EventArgs e)
{
fooCommand.Visible = !fooCommand.Visible;
}
HelloWorldCommand:
public class HelloWorldCommand : ToolStripItemCommand
{
#region Overrides of ControlCommand
protected override void Execute()
{
MessageBox.Show("Hello World");
}
#endregion
}
It's unfortunate that Control and ToolStripItem do not share a common interface since they both have "Enabled" and "Visible" properties. In order to support both types, you would have to composite a command for both, or use reflection. Both solutions infringe on the elegance afforded by simple inheritance.
You can enable or disable a control and all its children by setting its Enabled property.
You can hook the code for the MenuItem and the ToolButton in the same handler. For example:
menuItem.Click += HandleClick;
toolbarButton.Click += handleClick;
This way clicking both the MenuItem and the Button will execute the same code and provide the same functionality.
Related
I have multiple forms with the same method called "UpdateTheme" which changes the back colour of the form. I want to be able to call all of these methods from another form.
I tried to make a base form with the "UpdateTheme" method then have all other forms inherit from the base form, But I didnt know how/ if it was possible to then call every instance of the derived forms methods from a separate "Settings" form.
public abstract class CustomForm : Form
{
public void UpdateTheme(string theme)
{
if (theme == "dark")
{
this.BackColor = Color.Black;
}
else if (theme == "light")
{
this.BackColor = Color.White;
}
}
}
In the settings form I would have something like
public void btnSetThemeToDark_Click(object sender, EventArgs e)
{
foreach (instance of derived form)
{
derivedForm.UpdateTheme("dark");
}
}
Whats the best way to do this?
You could create a singleton called StyleManager that contains the global style properties. This singleton has an event called style changed that can be handled by all forms, or a base form. So all of your forms get the information from one source.
StyleManager
public class StyleManager
{
#region singleton
public static StyleManager Instance { get; } = new StyleManager();
private StyleManager()
{
}
#endregion
#region events
public event EventHandler StyleChanged;
private void OnStyleChanged()
{
this.StyleChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region properties
public Color BackColor { get; set; }
#endregion
#region methods
public void UpdateBackColor(Color color)
{
this.BackColor = color;
this.OnStyleChanged();
}
#endregion
}
and use it in your forms like this:
public Form()
{
this.InitializeComponent();
//Attach to the event
StyleManager.Instance.StyleChanged += this.StyleChanged;
}
//Handle event
private void StyleChanged(object sender, EventArgs eventArgs)
{
this.BackColor = StyleManager.Instance.BackColor;
}
//set backcolor of all forms
StyleManager.Instance.UpdateBackColor(Color.Yellow);
Assuming this forms are MdiChildren of the form, you would do it this way:
foreach (var form in this.MdiChildren)
{
var castedForm = form as CustomForm;
if (myObjRef != null)
{
castedForm.UpdateTheme("dark");
}
}
This functionality works beyond themes to any common method of the child forms.
I don't think this is the best way to do this. But, you can archive what you want by using this code.
CustomForm mainFrm = (CustomForm)Application.OpenForms["YouCustomFormNameHere"];
mainFrm.UpdateTheme("dark");
Replace 'YouCustomFormNameHere' with your CustomForm form name.
Today I got a problem in my development.
I have a Windows Form like this :
I need to enable the button "Appliquer" when the content of one of my textbox change.
I know that I can put the KeyPress event on each textbox and enable my button with that. In this window it can be easy to do that because there is only 10 textbox but I have an other window with more of 100 textbox and I think there is a better solution.
I tried to put the Keydown event directly in my windows form but it doesn't work.
So my question is, how can I do this. If someone have an idea ?
Thank you in advance !
Thomas
Since you already have 100+ textboxes in your form. I am assuming performance is not an issue for you.
In your form constructor, call this method. It will attach the event to all the textbox controls present in your form & inside sub controls such as groupbox, panel etc. (if you require)
There could be better ways of iteration..
public Form1()//your constructor
{
InitializeComponent();
AttachEvent(this);
}
void AttachEvent(Control CTrl)
{
foreach (Control c in CTrl.Controls)
{
if (c is TextBox)
{
c.TextChanged += new EventHandler(c_TextChanged);
continue;
}
if (c.HasChildren)
{
AttachEvent(c);
}
}
}
void c_TextChanged(object sender, EventArgs e)
{
//Your Code here btnGo.Enabled = !btnGo.Enabled;
}
What you can do is to extend TextBox make a field ( accessible from the designer ) to bind that TextBox into some other control.
public class MeTextBox
: TextBox
{
public override string Text
{
get { return base.Text; }
set
{
if ( m_DependantControl != null )
{
m_DependantControl.Enabled = !string.IsNullOrWhiteSpace(value);
}
base.Text = value;
}
}
Control m_DependantControl;
[Browsable(true)]
public Control DependantControl
{
get { return m_DependantControl; }
set { m_DependantControl = value; }
}
}
Now you can use MeTextBox as a regular TextBox. And if you want to make it control Enabled flag of some other Control you can just specify DependantControl property which will be accessible in the designer.
Fitting this into your example (code):
// assume you have a Button named btnConfirm
// and want to enable this button only when your `TextBox` has some text
MeTextBox mtb = new MeTextBox();
mtb.DependantControl = btnConfirm;
And if you do not want to make it in the code you can use designer directly.
To make it other way around ( one button dependant on many text boxes ) you can extend Button object :
public class MeButton
: Button
{
List<TextBox> m_DependantOn = new List<Control>();
[Browsable(true)]
public List<TextBox> DependantOn
{
get { return m_DependantOn; }
set { RemoveEvents(); m_DependantOn = value; AssignEvents(); }
}
void RemoveEvents()
{
foreach(TextBox ctrl in m_DependantOn)
ctrl.TextChanged -= WhenTextChanged;
}
void AssignEvents()
{
foreach(TextBox.ctrl in m_DependantOn)
ctrl.TextChanged += WhenTextChanged;
}
void WhenTextChanged(object sender, TextChangedEventArgs e)
{
this.Enabled = true;
}
}
I lately had the problem of creating add and edit dialogs for my wpf app.
All I want to do in my code was something like this. (I mostly use viewmodel first approach with mvvm)
ViewModel which calls a dialog window:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
// Do anything with the dialog result
How does it work?
First, I created a dialog service:
public interface IUIWindowDialogService
{
bool? ShowDialog(string title, object datacontext);
}
public class WpfUIWindowDialogService : IUIWindowDialogService
{
public bool? ShowDialog(string title, object datacontext)
{
var win = new WindowDialog();
win.Title = title;
win.DataContext = datacontext;
return win.ShowDialog();
}
}
WindowDialog is a special but simple window. I need it to hold my content:
<Window x:Class="WindowDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="WindowDialog"
WindowStyle="SingleBorderWindow"
WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight">
<ContentPresenter x:Name="DialogPresenter" Content="{Binding .}">
</ContentPresenter>
</Window>
A problem with dialogs in wpf is the dialogresult = true can only be achieved in code. That's why I created an interface for my dialogviewmodel to implement it.
public class RequestCloseDialogEventArgs : EventArgs
{
public bool DialogResult { get; set; }
public RequestCloseDialogEventArgs(bool dialogresult)
{
this.DialogResult = dialogresult;
}
}
public interface IDialogResultVMHelper
{
event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
}
Whenever my ViewModel thinks it's time for dialogresult = true, then raise this event.
public partial class DialogWindow : Window
{
// Note: If the window is closed, it has no DialogResult
private bool _isClosed = false;
public DialogWindow()
{
InitializeComponent();
this.DialogPresenter.DataContextChanged += DialogPresenterDataContextChanged;
this.Closed += DialogWindowClosed;
}
void DialogWindowClosed(object sender, EventArgs e)
{
this._isClosed = true;
}
private void DialogPresenterDataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
var d = e.NewValue as IDialogResultVMHelper;
if (d == null)
return;
d.RequestCloseDialog += new EventHandler<RequestCloseDialogEventArgs>
(DialogResultTrueEvent).MakeWeak(
eh => d.RequestCloseDialog -= eh;);
}
private void DialogResultTrueEvent(object sender,
RequestCloseDialogEventArgs eventargs)
{
// Important: Do not set DialogResult for a closed window
// GC clears windows anyways and with MakeWeak it
// closes out with IDialogResultVMHelper
if(_isClosed) return;
this.DialogResult = eventargs.DialogResult;
}
}
Now at least I have to create a DataTemplate in my resource file(app.xaml or something):
<DataTemplate DataType="{x:Type DialogViewModel:EditOrNewAuswahlItemVM}" >
<DialogView:EditOrNewAuswahlItem/>
</DataTemplate>
Well thats all, I can now call dialogs from my viewmodels:
var result = this.uiDialogService.ShowDialog("Dialogwindow Title", dialogwindowVM);
Now my question, do you see any problems with this solution?
Edit: for completeness. The ViewModel should implement IDialogResultVMHelper and then it can raise it within a OkCommand or something like this:
public class MyViewmodel : IDialogResultVMHelper
{
private readonly Lazy<DelegateCommand> _okCommand;
public MyViewmodel()
{
this._okCommand = new Lazy<DelegateCommand>(() =>
new DelegateCommand(() =>
InvokeRequestCloseDialog(
new RequestCloseDialogEventArgs(true)), () =>
YourConditionsGoesHere = true));
}
public ICommand OkCommand
{
get { return this._okCommand.Value; }
}
public event EventHandler<RequestCloseDialogEventArgs> RequestCloseDialog;
private void InvokeRequestCloseDialog(RequestCloseDialogEventArgs e)
{
var handler = RequestCloseDialog;
if (handler != null)
handler(this, e);
}
}
EDIT 2: I used the code from here to make my EventHandler register weak:
http://diditwith.net/2007/03/23/SolvingTheProblemWithEventsWeakEventHandlers.aspx
(Website no longer exists, WebArchive Mirror)
public delegate void UnregisterCallback<TE>(EventHandler<TE> eventHandler)
where TE : EventArgs;
public interface IWeakEventHandler<TE>
where TE : EventArgs
{
EventHandler<TE> Handler { get; }
}
public class WeakEventHandler<T, TE> : IWeakEventHandler<TE>
where T : class
where TE : EventArgs
{
private delegate void OpenEventHandler(T #this, object sender, TE e);
private readonly WeakReference mTargetRef;
private readonly OpenEventHandler mOpenHandler;
private readonly EventHandler<TE> mHandler;
private UnregisterCallback<TE> mUnregister;
public WeakEventHandler(EventHandler<TE> eventHandler,
UnregisterCallback<TE> unregister)
{
mTargetRef = new WeakReference(eventHandler.Target);
mOpenHandler = (OpenEventHandler)Delegate.CreateDelegate(
typeof(OpenEventHandler),null, eventHandler.Method);
mHandler = Invoke;
mUnregister = unregister;
}
public void Invoke(object sender, TE e)
{
T target = (T)mTargetRef.Target;
if (target != null)
mOpenHandler.Invoke(target, sender, e);
else if (mUnregister != null)
{
mUnregister(mHandler);
mUnregister = null;
}
}
public EventHandler<TE> Handler
{
get { return mHandler; }
}
public static implicit operator EventHandler<TE>(WeakEventHandler<T, TE> weh)
{
return weh.mHandler;
}
}
public static class EventHandlerUtils
{
public static EventHandler<TE> MakeWeak<TE>(this EventHandler<TE> eventHandler,
UnregisterCallback<TE> unregister)
where TE : EventArgs
{
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
if (eventHandler.Method.IsStatic || eventHandler.Target == null)
throw new ArgumentException("Only instance methods are supported.",
"eventHandler");
var wehType = typeof(WeakEventHandler<,>).MakeGenericType(
eventHandler.Method.DeclaringType, typeof(TE));
var wehConstructor = wehType.GetConstructor(new Type[]
{
typeof(EventHandler<TE>), typeof(UnregisterCallback<TE>)
});
IWeakEventHandler<TE> weh = (IWeakEventHandler<TE>)wehConstructor.Invoke(
new object[] { eventHandler, unregister });
return weh.Handler;
}
}
This is a good approach and I used similar ones in the past. Go for it!
One minor thing I'd definitely do is make the event receive a boolean for when you need to set "false" in the DialogResult.
event EventHandler<RequestCloseEventArgs> RequestCloseDialog;
and the EventArgs class:
public class RequestCloseEventArgs : EventArgs
{
public RequestCloseEventArgs(bool dialogResult)
{
this.DialogResult = dialogResult;
}
public bool DialogResult { get; private set; }
}
I've been using an almost identical approach for several months now, and I'm very happy with it (i.e. I haven't yet felt the urge to rewrite it completely...)
In my implementation, I use a IDialogViewModel that exposes things such as the title, the standad buttons to show (in order to have a consistent apparence across all dialogs), a RequestClose event, and a few other things to be able to control the window size and behavior
If you are talking about dialogue windows and not just about the pop-up message boxes, please consider my approach below. The key points are:
I pass a reference to Module Controller into the constructor of each ViewModel (you can use injection).
That Module Controller has public/internal methods for creating dialogue windows (just creating, without returning a result). Hence to open a dialogue window in ViewModel I write: controller.OpenDialogEntity(bla, bla...)
Each dialogue window notifies about its result (like OK, Save, Cancel, etc.) via Weak Events. If you use PRISM, then it's easier to publish notifications using this EventAggregator.
To handle dialogue results, I'm using subscription to notifications (again Weak Events and EventAggregator in case of PRISM). To reduce dependency on such notifications, use independent classes with standard notifications.
Pros:
Less code. I don't mind using interfaces, but I've seen too many projects where excessiveness of using interfaces and abstraction layers cause more trouble than help.
Open dialogue windows through Module Controller is a simple way to avoid strong references and still allows to use mock-ups for testing.
Notification through weak events reduce number of potential memory leaks.
Cons:
Not easy to distinguish required notification from others in the handler. Two solutions:
send a unique token on opening a dialogue window and check that token in the subscription
use generic notification classes <T> where T is enumeration of entities (or for simplicity it can be type of ViewModel).
For a project should be an agreement about using notification classes to prevent duplicating them.
For enormously large projects the Module Controller can be overwhelmed by methods for creating windows. In this case it's better to split it up in several modules.
P.S. I have been using this approach for quite a long time now and ready to defend its eligibility in comments and provide some examples if required.
I've built a class that derives from System.Web.UI.WebControl. It basically renders pagination links (same as what you see on top of GridView when enabled) for use above a repeater.
I'm creating some anchor tags explicitly inside my nav control obviously, but they don't perform ajax postbacks. My understanding is that ajax requires POSTS to work right? Well, these would be GETs which I think is the problem.
Is there a way to achieve what I'm trying to do?
Thanks!
To take this advantage, you have to inherit the ICallbackEventHandler and implement its methods as follows.
public class CtlTest : WebControl, ICallbackEventHandler
{
private static readonly object EventClick = new object();
public CtlTest() : base(HtmlTextWriterTag.A) { }
public event EventHandler Click
{
add { base.Events.AddHandler(EventClick, value); }
remove { base.Events.RemoveHandler(EventClick, value); }
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + this.Page.ClientScript.GetCallbackEventReference(this, null, "null", null));
}
protected override void RenderContents(HtmlTextWriter writer)
{
base.RenderContents(writer);
writer.Write("Submit Query");
}
protected virtual void OnClick(EventArgs e)
{
EventHandler handler = this.Events[EventClick] as EventHandler;
if (handler != null)
handler(this, e);
}
#region ICallbackEventHandler Members
string ICallbackEventHandler.GetCallbackResult()
{
return string.Empty;
}
void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
this.OnClick(EventArgs.Empty);
}
#endregion
}
Whereas you are working on a data pager control and it requires to update some portions of the page, it's better to write a non Ajax enabled control and put it and its relative controls within an UpdatePanel.
Ok, I figured it out. I simply made my class implement the IPostBackEventHandler. This makes your control fire an event when the user takes action on something. In my case, it's clicking a nav pagenumber: [1][2][3][4][5][Next >]
Then, inside my render where I create the Anchor tags, I add this to each href (pageStartRow is different for each):
PostBackOptions options = new PostBackOptions(this, pageStartRow.ToString());
writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" + Page.ClientScript.GetPostBackEventReference(options));
writer.RenderBeginTag(HtmlTextWriterTag.A);
The key is to pass something that uniquely identifies which link they clicked. This is done as the 2nd constructor parameter to the PostBackOptions class.
I then added the following items in my WebControl class:
// Defines the Click event.
public delegate void ClickHandler(object sender, GridPagingNavClickedEventArgs e);
public event ClickHandler Click;
//Invoke delegates registered with the Click event.
protected virtual void OnClick(GridPagingNavClickedEventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
public void RaisePostBackEvent(string eventArgument)
{
GridPagingNavClickedEventArgs e = new GridPagingNavClickedEventArgs(Convert.ToInt32(eventArgument));
OnClick(e);
}
The GridPagingNavClickedEventArgs contains a single item (pageNumber in my case).
Finally, in my aspx page (where I use the webcontrol), I do this in the Page_OnLoad:
gridNavTop.Click += new GridPagingNavigation.ClickHandler(gridNavTop_Click);
and this is the event code:
private void gridNavTop_Click(object sender, GridPagingNavClickedEventArgs e)
{
StartRow = e.PageStartRow;
}
As long as everything is inside an UpdatePanel, then it works perfectly!
Is there a way to programmatically generate a click event on a CheckBox? I am looking for an equivalent to Button.PerformClick();
Why do you need to simulate a click, doesn't this line of code fits your need?
myCheckBox.Checked = !myCheckBox.Checked;
If you need to execute logic when the state of the CheckBox changes, you should use CheckedChanged event instead of Click.
private void CheckBox1_CheckedChanged(Object sender, EventArgs e)
{
MessageBox.Show("You are in the CheckBox.CheckedChanged event.");
}
Those solutions above calls Checkbox.CheckedChanged event.
If you want to explicitly call Click event you can this:
checkBox1_Click(checkBox1, null);
Why do you want to generate a click event on the CheckBox?
If you want to toggle it's value:
theCheckBox.Checked = !theCheckBox.Checked;
If you want to trigger some functionality that is connected to the Click event, it's a better idea to move the code out from the Click event handler into a separate method that can be called from anywhere:
private void theCheckBox_Click(object sender, EventArgs e)
{
HandleCheckBoxClick((CheckBox)sender);
}
private void HandleCheckBoxClick(CheckBox sender)
{
// do what is needed here
}
When you design your code like that, you can easily invoke the functionality from anywhere:
HandleCheckBoxClick(theCheckBox);
The same approach can (and perhaps should) be used for most control event handlers; move as much code as possible out from event handlers and into methods that are more reusable.
I'm still setting up a new workstation so I can't research this properly at the moment, but with UI Automation maybe it's possible that the checkbox supports the IInvokeProvider and you can use the Invoke method?
I don't think you can generate a click event in that way without calling the checkBox_Click event handler directly. But you can do this:
checkBox.Checked = !checkBox.Checked;
The CheckedChanged handler will still be called even if you do this.
The Button PerformClick() method validates the active control, testing whether the active control can lose the current focus. There are two ways to possibly do the same thing for a CheckBox. Approach #1 is to use reflection to call the methods that are internal to the Control class:
public class CheckBoxPerformClick : CheckBox {
private readonly static MethodInfo callValidateActiveControl;
private readonly static PropertyInfo propValidationCancelled;
static CheckBoxPerformClick() {
try {
Type ty = typeof(Control);
Type outBool = Type.GetType("System.Boolean&");
callValidateActiveControl = ty.GetMethod("ValidateActiveControl", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { outBool }, null);
propValidationCancelled = ty.GetProperty("ValidationCancelled", BindingFlags.Instance | BindingFlags.NonPublic);
} catch {}
}
public CheckBoxPerformClick() : base() {
this.Text = "Checkbox";
this.Appearance = Appearance.Button;
}
public void PerformClick() {
if (callValidateActiveControl != null && propValidationCancelled != null) {
try {
Object[] args = new Object[1];
bool validate = (bool) callValidateActiveControl.Invoke(this, args);
bool validatedControlAllowsFocusChange = (bool) args[0];
if (validate || validatedControlAllowsFocusChange) {
bool cancelled = (bool) propValidationCancelled.GetValue(this);
if (!cancelled) {
ResetFlagsandPaint();
OnClick(EventArgs.Empty);
}
}
} catch {
}
}
}
}
Approach #2 tries to do the same thing, but without reflection:
public class CheckBoxPerformClick2 : CheckBox {
public CheckBoxPerformClick2() : base() {
this.Text = "Checkbox";
this.Appearance = Appearance.Button;
}
public void PerformClick() {
bool validate = CanPerformClick();
if (validate) {
ResetFlagsandPaint();
OnClick(EventArgs.Empty);
}
}
// before allowing a click, make sure this control can receive the focus, and that other controls don't require validation
public bool CanPerformClick() {
if (!CanSelect)
return false;
Control c = this.Parent;
while (c != null) {
if (c is ContainerControl)
break;
c = c.Parent;
}
bool valid = true;
if (c is ContainerControl) {
var cc = (ContainerControl) c;
valid = cc.Validate(true);
}
return valid;
}
}