Change ToolStripButtomItem Attributes Using EventHandler - c#

this is my first question on stackoverflow, therefore errors in asking-style aren't on purpose.
I'm both, new to C# as to the concept of event-handling, so
I would like to know, if there is any possibility to have the value of an ToolStripButtonItem-attribute changed by an EventHandler.
The context is the following:
The code starts by initializing the UI which contains some Windows.Forms- Elements.
The ToolStripButtomItem that is of interest for me, has it's Enabled-attribute set to false as default-value. The functionality of this button is to switch into a comparision-view as soon as a certain reference file exists.
This allready can be the case when the programm-start, otherwise the reference file might be created during runtime.
Of course, you could perform
Button.Enabled=System.IO.File.Exists(Reference-File)
with the initilization and than do something like
CreateFile(ReferenceFile){
...
Button.Enabled = true;
}
but this seems rather crude to me.
Instead I would like to something like:
Button.Enabled = new System.EventHandler(this.EnableButton);
with
private void EnableButton(Object sender, EventArgs e){
if(System.IO.FileExists(ReferenceFile)
Button.Enabled = true;
}
What I intend is, to have the button get enabled as soon as the reference-file existst. There are multiple ways to create the reference-file, and there are goint to be even more in the future. To avoid setting the enable-value in each of those createReferenceFile()-Methods, the concept of EventHandling seems quite like the deal to me.
The program I'm trying to run is quite comprehensive, so "polling" is no option at this place.

I suggest to use the FileSystemWatcher and set the enabled property every time it goes of.
private void StartListening(string path)
{
var watch = new FileSystemWatcher();
watch.Path = path;
watch.Filter = "*.*";
watch.Created += UpdateState;
watch.Deleted += UpdateState;
}
void UpdateState(object sender, FileSystemEventArgs e)
{
MyButton.Enabled = File.Exists(#"C:\Folder\File.txt");
}
PS: this is just some very basic example code, you will need to make sure that you have a reference to MyButton and have the correct path there as well ...

Related

Way to clean up this pattern? Using private bool flag to denote when user changes UI vs when the class changes UI?

I find myself using this pattern often
private boolean cbDeviceIndexInternal = false;
private void cbDevices_SelectedIndexChanged(object sender, EventArgs e)
{
if (!cbDeviceIndexInternal)
DeviceChanged();
cbDeviceIndexInternal = false;
}
...
cbDeviceIndexInternal = true;
cbDevices.SelectedIndex = 0;
As a result of the SelectedIndex being changed an event is thrown no matter if the index is changed by the user or by the class itself. I really hate using this code but it is the best solution I have found.
Has anyone else encountered this, how did you solve it?
You don't necessarily have to add and remove the handlers. Just don't add the handlers until the end of the Load event. That way they aren't present and firing while you're making programmatic changes to your controls.

How to use PlayStateChanged event of BackgroundAudioPlayer?

In my app, I want to be know when play state changes. But I don't know how to subscribe to the event and get the current state. How can I do that? thanks.
I see an statement in MSDN, but couldn't understand what it means and how to implement it:
In Windows Phone 8, you can check the PlayStateChangedEventArgs to
determine both the CurrentPlayState and the IntermediatePlayState that
occurred before the audio player entered the current play state.
Details:
in the main page I do this:
public MainPage()
{
BackgroundAudioPlayer.Instance.PlayStateChanged += new EventHandler(Instance_PlayStateChanged);
}
then
private void Instance_PlayStateChanged(object sender, EventArgs e)
{
var playerState = BackgroundAudioPlayer.Instance.PlayerState;
}
But I feel this is not the correct way to use event and eventargs. it also doesn't give me the correct latest value.
The PlayerStateChanged event is definitely the right way to determine changes, but it won't fire when you subscribe to it so you won't get the current state. Try something like this instead:
BackgroundAudioPlayer audioPlayer = BackgroundAudioPlayer.Instance;
public MainPage()
{
audioPlayer += OnPlayStateChanged;
OnPlayStateChanged(audioPlayer.PlayerState);
}
private OnPlayStateChanged(object sender, EventArgs e)
{
OnPlayStateChanged(audioPlayer.PlayerState);
}
private OnPlayStateChanged(PlayState state)
{
// Process state here
}
Having said that, there are two major things worth pointing out.
Firstly, BackgroundAudioPlayer is an extremely volatile API. It will commonly throw exceptions if not in the correct internal state. Feel free to use the extension methods I developed for Podcaster: https://gist.github.com/richardszalay/8552812
Secondly, PlayerStateChanged is not fired when the playback position changes. For that, I'd recommend using a DispatcherTimer and updating your display via my TryGetPosition method (but only when GetTrackOrDefault() returns non-null). I'd also recommend using a sub-second timer (200-300ms) to keep the "ticking" correct. When the PlayerState changes to FastForwarding or Rewinding, update the timer to 20-30ms, and restore it when it returns to Playing.
Use this solution as well as link you would get solution:-
enter link description here
void Instance_PlayStateChanged(object sender, EventArgs e)
{
switch (BackgroundAudioPlayer.Instance.PlayerState)
{
case PlayState.Playing:
playButton.Content = "pause";
break;
case PlayState.Paused:
case PlayState.Stopped:
playButton.Content = "play";
break;
}
if (null != BackgroundAudioPlayer.Instance.Track)
{
txtCurrentTrack.Text = BackgroundAudioPlayer.Instance.Track.Title +
" by " +
BackgroundAudioPlayer.Instance.Track.Artist;
}
}

Temporarily stop form events from either being raised or being handled?

I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.

multi thread issue

I'm using a timer to reset a lable I use as a warning box. Basically, if the user does something (more specifically, something goes wrong, ex : He uses a word not recognized by the program), this catches what went wrong early and returns to him what happened so he can change the input.
The reset blanks out the label after 5 seconds to prevent him from seeing something like "please do not use chinese characters" and maybe still thinking an old error is still up. This is what I got reading the invoke (since I hear begininvoke requires an endinvoke, I chose invoke).
private void lblWrn_TextChange(object sender, EventArgs e)
{
Timee = new System.Timers.Timer(5000);
Timee.Elapsed += new ElapsedEventHandler(timerClearWrn);
Timee.Enabled = true;
}
string empty = "";
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(new Action<Label>(lblWrn), new object[] { lblWrn, "" });
}
I am not too sure where I am going wrong with this, and looking up examples, cannot figure out which part to change. Can someone explain to me the error or invoke a bit more?
If it's a Windows Forms application, use System.Windows.Forms.Timer, then you don't need Invoke, as the timer callback is executed on the main thread.
Also, don't create a new timer on every text change.
Actually, Control.BeginInvoke does not need an EndInvoke; it is Delegate.BeginInvoke that does.
First, I would also recommend using a Windows.Forms.Timer, since it looks like you are using winforms - that will automatically fire on the UI thread, making all the problems go away - just run the code you want to run in the handler (don't use Invoke etc)
The problem in your example is that the parameters don't match; an Action<> expects a method name (more accurately: a method group) to be invoked, and the parameters in the array must be suitable. Since you don't show the method you plan to invoke, I can't help there - but lblWarn isn't a method (it is a field).
on this line
lblWrn.Invoke(new Action(lblWrn), new object[] { lblWrn, "" });
shouldn't the bold part be a function and not a object?
You have a few options. Option 1 is a little clunky. Options 2 and 3 are better.
Option 1: Continue with general strategy of using Control.Invoke but use code that calls Invoke correctly, disable auto resetting of the timer, and removes the event handler.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Invoke(
(MethodInvoker)(()=>
{
lblWrn.Text = "";
}), null);
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}
Option 2: Use a System.Windows.Forms.Timer instead of System.Timers.Timer.
Option 3: Use the SynchronizingObject property of System.Timers.Timer. This is my preferred option when timers are to be created and used dynamically from a UI thread.
private void lblWrn_TextChange(object sender, EventArgs e)
{
var Timee = new System.Timers.Timer(5000);
Timee.Elapsed += this.timerClearWrn;
Timee.AutoReset = false; // Raise the Elapsed event only once
Timee.SynchronizingObject = this; // Tell the Timer to raise the Elapsed event on the UI thread
Timee.Enabled = true;
}
private void timerClearWrn(object sender, ElapsedEventArgs elapsed)
{
lblWrn.Text = "";
var Timee = (System.Timers.Timer)sender;
Timee.Elapsed -= this.timerClearWrn;
}

How can I prevent an event from causing its own event from firing in C#?

I have a treeview with checkboxes and I have the following handler for the "AfterCheck" event:
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
if (!_isCheckingInProgress)
{
trvAvailableFiles.BeginUpdate();
var nodePath = e.Node.Tag.ToString();
bool isChecked = e.Node.Checked;
e.Node.Nodes.Clear();
try
{
_fileTreeLogic.GetChildNodes(e.Node, true);
e.Node.ExpandAll();
_isCheckingInProgress = true;
SetChildrenCheckState(e.Node, isChecked);
_isCheckingInProgress = false;
}
finally
{
trvAvailableFiles.EndUpdate();
}
}
}
If you look closely you'll see that I'm checking if "_isCheckingInProgress". If it is not, then I proceed and expand all the nodes and call the SetChildrenCheckState() method. The problem I have encountered is that SetChildrenCheckState() will subsequently cause each child node to all fire the AfterCheck event for its own node.
My question is, is there a more clean way to allow the first AfterCheck event to fire but not the subsequent ones? It seems kind of hackish that I have to have an instance bool variable to check and set.
Use: if(e.Action != TreeViewAction.Unknown) instead of if (!_isCheckingInProgress). See TreeViewAction .
When the user uses the keyboard or mouse to check the checkboxes, e.Action will be TreeViewAction.ByKeyboard or TreeViewAction.ByMouse.
The MSDN provides this as example code for the TreeView.AfterCheck Event.
Edit 1: Obviously, if you're setting the checkbox yourself in code, move the code in the event handler to a new function and have the code that sets the checkbox call it directly. The point of this solution is to let you use event handlers for user input without having those events get triggered when you set the checkboxes yourself via code.
Edit 2: See Spencer's answer for an explanation of my comment in Edit 1
One recommendation you'll see occasionally around SO is to not put a lot of code into event handlers themselves. There are a number of reasons for this. First, in your case it would be easier to understand a call like:
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
if (!_isCheckingInProgress)
{
_isCheckingInProgress = true;
try { GetAvailableFiles(); } catch {}
_isCheckingInProgress = false;
}
}
And to place the rest of your code in GetAvailableFiles(). This creates a separation between event code and action code which most people would agree is a worthwhile distinction to make.
Second, which may or may not be applicable in your case is that multiple events can cause the same action. Such as mnuFileQuit_Click and btnClose_Click as an obvious example. If both make calls to CloseApplication() it removes a lot of redundant code.
Personally, I use a function that removes and then adds the event.
private void trvAvailableFiles_AfterCheck(object sender, TreeViewEventArgs e)
{
EnableEvents(false);
trvAvailableFiles.BeginUpdate();
var nodePath = e.Node.Tag.ToString();
bool isChecked = e.Node.Checked;
e.Node.Nodes.Clear();
try
{
_fileTreeLogic.GetChildNodes(e.Node, true);
e.Node.ExpandAll();
SetChildrenCheckState(e.Node, isChecked);
}
finally
{
trvAvailableFiles.EndUpdate();
}
EnableEvents(true);
}
private void EnableEvents(bool bEnable)
{
if(bEnable)
cbWhatever.OnChecked += EventHandler;
else
cbWhatever.OnChecked -= EventHandler;
}
No, there's no cleaner way to do what you've shown. I'm not really sure why you feel that variables are a "hack" approach. Setting a flag is a common technique used when writing UI code.
The real hack would be some obscure way to prevent raising the event the first time, but not subsequent times. Future maintenance programmers are guaranteed to understand how setting a flag works; they're not guaranteed to appreciate the "elegance" of your alternative approach.

Categories