A cleaner way to automatically call one method after another? - c#

Is it possible to design a method in such a fashion, that it knows it must automatically call a next method in succession upon exiting?
In the following example, I must call Refresh() to cause my form to repaint after this event takes place. The problem is that, it's ugly to call Refresh() after, for example, 20 different events which must make the form refresh. e.g
private void PriorityLine_Click(object sender, EventArgs e)
{
_showPriorityLine = (_showPriorityLine) ? false : true;
Refresh(); // Must call refresh for changes to take effect.
}
I suppose what I'm looking for is some kind of signature I can apply to the method to cause it to automatically chain to the next method, regardless from where its called. e.g
(I know this isn't syntactically correct.)
private void PriorityLine_Click(object sender, EventArgs e).Refresh()
{
_showPriorityLine = (_showPriorityLine) ? false : true;
}
I want to seperate the interface of the method, from the logic contained within the method. I understand it would be the exact amount of effort, if not more. For example, if I were to edit the method and accidently removed Refresh, it would cause my application to break. Whereas, if the Refresh method was outside of the actual logic of the method, I could do anything within the method without worrying about removing the next chain of logic.

Sounds like what you want is Aspect Oriented Programming, there are a number of different frameworks to enable you to have stuff "magically" happen after some set of methods have run, have a look here AOP programming in .Net?

I'm not aware of any really clean way. One method would be to use PostSharp.

You could encapsulate the changes which would cause the form to refresh into form-level properties.
For instance,
private bool _showPriorityLine;
private bool ShowPriorityLine
{
get { return _showPriorityLine; }
set
{
_showPriorityLine = value;
Refresh();
}
}
Then your event would just be
private void PriorityLine_Click(object sender, EventArgs e)
{
ShowPriorityLine = !ShowPriorityLine;
}
Of course, that only cleans up your code if you have several events manipulating the same variables that cause the form to need refreshing.

Taking into consideration your particular problem and the solutions posted, I would say the "cleanest" approach here would be to implement a Property Changed Notification just for internal use in the form i.e. you don't need to expose the event like in the MSDN example.
This way you could maintain an internal list of properties that you know will require the form to be refreshed e.g.
private List<string> _refreshProps = new List<string>();
private bool _showPriority;
public void Form()
{
_refreshProps.Add("ShowPriority");
... etc
}
// only implement properties like this that need some extra work done
public bool ShowPriority
{
get { return _showPriority; }
set
{
if (_showPriority != value)
{
_showPriority = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("ShowPriority");
}
}
}
// basic property that doesn't require anything extra
public bool AnotherProperty { get; set; }
public void Refresh()
{
// refresh the form
}
protected void OnPropertyChanged(string name)
{
if (_refreshProps.Contains(name))
Refresh();
}
The benefit of this approach is if in the future you needed to do other "stuff" after particular properties you can simply introduce another list and handle it again in your OnPropertyChanged method.

Don't call Refresh, call Invalidate. The mechanism you need is already built into Windows. Calling Invalidate simply makes a note that the window needs repainting. The operating system will eventually post a WM_PAINT message (typically after the root DispatchMessage call finishes, but the exact implementation is irrelevant).

Use a property that calls Refresh in the setter.

Something like this:
private void RefreshAfter(Action action)
{
action();
Refresh();
}
UPDATED TO MAKE IT MORE OBVIOUS:
private void DoSomeUiShiznit(Action action)
{
action();
// other parts of the code don't realize that Refresh has to be called.
// But that's cool. I got it covered.
Refresh();
}
private void PriorityLine_Click(object sender, EventArgs e)
{
DoSomeUiShiznit(() => { _showPriorityLine = !_showPriorityLine; });
}
UPDATE -- Just a message to the down-voters:
What some of you are too blind to see is that this is not all that different from:
[SomeRefreshAttribute]
private void PriorityLine_Click(object sender, EventArgs e)
{
_showPriorityLine = !_showPriorityLine;
}
Except that it is simpler, and doesn't require adding another framework to the solution. And yet the other answer suggesting as much don't get down-voted!
What's wrong with you people?

Related

C# Separate GUI from Work logic

I'd like to find how to separate my GUI and work code.
The contrived example code, below, is the smallest starting point that
I can think of that covers the idea.
The example uses a Windows Form as the source of commands and as display
(consumer) of results. I want the work code to be capable of getting commands from, say, a command line interface instead. The work code should not depend on knowledge of the Form. The Form should know little about the work code. I'd like to have several consumers "see" when the property in the work code changes value.
I'm guessing this means using events for communication, and perhaps Interfaces as well, but I'm open to anything.
There are a million different suggestions out there. I've read the design pattern books, and I have tried many, and have yet to find a set that is well enough explained that I can fully implement it.
I don't want a universal solution. I want one as simple as possible
to implement and maintain for small, personal projects. I'm not
designing for a large corporation.
Most solutions I've found will hint at what to do, but not cover
the specifics like where an event is declared, and how the other
piece of code finds out about the event's existence so it can either issue
the event or respond to the event. I always end up needing, somewhere, what amounts to a global variable to hook things together.
The closest match I can find, here, to my question is this: C# Windows Forms App: Separate GUI from Business Logic But the solution uses the form to create an instance of the worker and returns a value directly, rather than informing any interested observers. The provided solution tightly bound the two classes.
The closest solution I've found anywhere is this: https://www.codeproject.com/Articles/14660/WinForms-Model-View-Presenter
which does some really cool work with interfaces and reflection, but didn't seem too maintainable nor flexible.
The comment lines in the source code below show the desired interaction
points but without the implementation.
File #1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// tell an instance of JustCounts to increment by 10
}
// Here, allow JustCounts to cause a call to this (or something
// similar, perhaps a property) to inform this code that the TotalCount
// property has changed.
public void ShowNewTotalCount(int NewTotal)
{
Console.WriteLine("New Total Count = {0}", NewTotal);
}
}
File #2
class JustCounts
{
private int m_TotalCount = 100;
// Inform other classes when the following property changes value,
// preferably including the value in the notification.
public int TotalCount { get => m_TotalCount; }
// The code in File #1 needs to result in a call to this method
// along with the correct argument value.
public void AddThisMuch(int increment)
{
m_TotalCount += increment;
}
}
I'm basing this on the current version of .Net (4.6.2).
If we implement INotifyPropertyChanged then we have an event that we can listen to for property changes. A bit like listening for key presses, we can filter then for the specific property that we want.
public class JustCounts : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private int m_totalCount = 100;
public int TotalCount
{
get { return m_totalCount; }
set
{
if (value != m_totalCount)
{
m_totalCount = value;
NotifyPropertyChanged();
}
}
}
}
There's no need to create a method to manipulate the TotalCount property as we're exposing it.
public class Form1 : Form
{
// justCounts is a reference to the object wherever it is coming from
justCounts.PropertyChanged += new PropertyChangedEventHandler(JustCountsChangedHandler);
private void JustCountsChangedHandler(object sender, PropertyChangingEventArgs e)
{
// process on event firing
Debug.WriteLine($"justCounts TotalCount changed value to {justCounts.TotalCount}");
}
// Example of where the handler will fire when called
private void button1_click(object sender, EventArgs e)
{
justCounts.TotalCount++;
}
}
In the code above, we've created an event in JustCounts to which listeners can subscribe.
Using the INotifyPropertyChanged interface, we fire the event each time TotalCount is changed.
In form 1 we create the handler to listen for property changes, and the handler then carries out any processing.
One note. You say
I'd like to have several consumers "see" when the property in the work
code changes value
so in order for this to work we have to assume that the work code can run independently of it's subscriber (something like a server). Otherwise, we'd have different instances for different subscribers.
You also mention interfaces, and they could be used but are not necessary in this instance.

Get label in a static method inside class c#

I know there are many similar questions but I am kind of restricted in many terms for this case. I am using SharpBox to upload files to Dropbox and in order to create a progress bar visible to user, I am using a static method where SharpBox returns the percenatge. It is all fine but I need to return this information to the aspx page or javascript somehow.
I cannot add a parameter to the method. I could remove the static from the method however it does still gives an exception null on the label which is quite strange (probably because the method fires from SharpBox dynamically).
So the method UploadDownloadProgress is the part where I have problems with.
public class docUpload
{
static public void Doc_Upload()
{
dropBoxStorage.UploadFile(stream, filename, entry, UploadDownloadProgress);
}
static void UploadDownloadProgress(Object sender, FileDataTransferEventArgs e)
{
// I need the e.PercentageProgress on aspx page
System.Diagnostics.Debug.WriteLine(e.PercentageProgress);
// This wont work since it is a static method
myLabel.Text = e.PercentageProgress.ToString();
e.Cancel = false;
}
}
I need the e.PercentageProgress in the label. I also alternatively tried to call javascript without success. Could you suggest any other alternatives?
Try something like this:
public class docUpload
{
static public void Doc_Upload()
{
dropBoxStorage.UploadFile(stream, filename, entry, ProgressInformer.UploadDownloadProgress);
}
}
public class ProgressInformer {
public static string Progress = "0";
static void UploadDownloadProgress(Object sender, FileDataTransferEventArgs e)
{
// print a dot
System.Diagnostics.Debug.WriteLine(e.PercentageProgress);
// Need to show this on a label or return to front end somehow
ProgressInformer.Progress = e.PercentageProgress.ToString();
e.Cancel = false;
}
}
Now since you are setting the static variable with value you can access it from somewhere else. Then you can use that value to echo on the front-end using some method or service. Possibly like this:
public string EchoToFrontEnd()
{
return ProgressInformer.Progress;
}
Limitation: If this works for you still this solution is not thread safe. That means, you cannot echo progress for multiple downloads. You'll have to work with a single download at a time.
Hope this helps...!

Send method as the parameter to another method (clarification)

I know this question has been asked many times in many forms, but now I want to clarify a few things.
2 methods: default method and additional method. First we perform default method with basic operations. Then, if additional method was passed to the default method as the parameter, perform it too.
???void Default_Method(???Additional_Method)
{
//default logic
//after default logic, call additional method if its name was specified
??? - how to call the specified additional method
}
???void Additional_Method()
{
//additional logic
}
...
And now the most interesting place
protected void Page_Load(object sender, EventArgs e)
{
Default_Method(???Additionl_Method???) //here I need to pass Additional_Method as a parameter
}
THE QUESTION:
How to declare the methods properly. I marked places to clarify with ??? mark.
And how to make the additional method be an optional parameter of the default method? Is it enough to pass Null when it is not needed?
REASON FOR THIS QUESTION
I didn't find a general instruction on how to do it. All examples are full of individual specifics, people use their own understanding when give names to methods and it is not really clear which one does what in their examples, that's why I decided to create the thread with neutral names.
void Default_Method(Action additionalMethod = null)
{
//default logic
//after default logic, call additional method if its name was specified
//This if is needed to avoid NullReferenceException
if (additionalMethod != null)
additionalMethod();
}
void Additional_Method()
{
//additional logic
}
protected void Page_Load(object sender, EventArgs e)
{
Default_Method(Additional_Method);
//OR
Default_Method();
//OR
Default_Method(null);
//OR
Default_Method(() => { /*Do something*/});
}
In this example I assumed that both methods are defined in the same class as Page_Load.
IMHO best to use an Action:
void Additional_Method()
{
}
void Default_Method(Action a)
{
a.Invoke();
}
in your main code use
Default_Method(Additional_Method);

C# ListView doesn't update

I reckon the problem of C# win forms not being repainted well in certain circumstances is covered in different places, however, I didn't manage to solve my problems by using the simple snippets I found on the web.
My problem : on a form I have a listView, which I associate to a custom data holder (2 columns, a key and a last update date). From diverse places, I need to call the updateTime(key) method which then replicated the changes in the GUI. The model gets changed but my listView never.
I have a form containing a ListView that looks like that :
partial class VolsPane : UserControl, IGUIPane
{
private ListView listView1;
private ListModel listModel1; //ListModel is 100% homemade
...
public VolsPane()
{
...
listModel1.setList(listView1);
}
}
And the class holding data for my listView is like that :
class ListModel
{
private Dictionary<string, DateTime> Underlying;
private ListView list;
...
public ListModel(string nexusKey)
{
...
}
...
public void setList(ListView list)
{
this.list = list;
}
public void updateTime(string ric)
{
Underlying[ric] = DateTime.UtcNow;
updateView();
}
public void updateView()
{
this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
...
public ListViewItem[] underlyingToListItems()
{
ListViewItem[] res = new ListViewItem[Underlying.Keys.Count];
int i = 0;
foreach (string ric in Underlying.Keys)
{
res[i] = new ListViewItem(new string[] { ric, Underlying[ric].ToString("MMM-dd hh:mm:ss") });
i++;
}
return res;
}
}
I do realize the problem is in my updateView(). In debug, the code goes there definitely. Believing the problem was to be solved with an asynchronous "invoke", I referred to this post which appeared to be a reference : Stack overflow : Automating the invoke...
Then tried this :
private void updateView()
{
if (this.list.InvokeRequired)
{
this.list.Invoke(new MethodInvoker(() => { updateView(); }));
}
else
{
this.list.Items.Clear();
//this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
}
It builds but has no effect. In debug mode, never goes in the 'if' branch, always the 'else'.
Then this :
private void updateView()
{
this.list.Invoke((MethodInvoker)delegate
{
this.list.Items.Clear();
//this.list.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
});
}
I get an "InvalidOperationException : Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
What is the obvious thing I must be missing here ? Or is my problem actually not the one i think ?
Thanks guys !
You are correct, the problem lies in the updateView() code. You do need to Invoke to the UI thread but the issue is the handle has not yet been created for the control. One gotcha when working with WinForms is that the InvokeRequired will actually return false if the handle has not yet been created. See this explanation form the MSDN documentation:
If the control's handle does not yet exist, InvokeRequired searches up the control's parent chain until it finds a control or form that does have a window handle. If no appropriate handle can be found, the InvokeRequired method returns false.
That's why your check for InvokeRequired was always failing. I've seen this problem fixed in a couple of ways. One solution is to attach a callback to the Control's handle created event:
public class HandleHookedListView: ListView
{
private EventHandler _handleCreatedEvent;
public HandleHookedListView(): base()
{
_handleCreatedEvent = new EventHandler(HandleHookedControl_HandleCreated);
this.HandleCreated += _handleCreatedEvent;
}
private bool _handleIsCreated;
public bool HandleIsCreated
{
get { return _handleIsCreated; }
set { _handleIsCreated = value; }
}
void HandleHookedControl_HandleCreated(object sender, EventArgs e)
{
Debug.Print("Handle Created");
this.HandleIsCreated = true;
// Unhook the delegate
if (_handleCreatedEvent != null)
this.HandleCreated -= _handleCreatedEvent;
}
}
You will then have to modify your updateView to check if the handle has been created. In this case your instance of ListView (list) has been replaced with the new HandleHookedListView
private void updateView()
{
var handleCreated = this.list.HandleIsCreated;
if (this.list.InvokeRequired && handleCreated)
{
// Handle is created and invoke is required.
this.list.Invoke(new MethodInvoker(() => { updateView(); }));
}
else if (handleCreated)
{
// In this case your control's handle has been created and invoke really
// isn't required go ahead and do the update
this.list.Items.Clear();
this.list.Items.AddRange(this.underlyingToListItems());
}
else
{
// You cannot update yet. The handle has not been created. Depending on if
// you need to "queue" these updates you can either collect them or just
// ignore them if a subsequent call to updateView() after the handle has been
// created will be sufficient
}
}
The real key here is that you are trying to do an update to the control before it has been fully initialized.
First of all
Doesn't build, invoke doesn't exist for my list model.
As far as I remember Invoke is the method of Control class.
So you can't call it in ListModel class without any instance of class inherited from Control.
Use
this.list.Invoke(
In debug mode, never goes in the 'if' branch, always the 'else'.
This may mean that this.list.InvokeRequired was called in GUI thead.
But it is also may mean that list.InvokeRequired was called before this.list was painted at least once.
This is tricky moment. If instance of Control class hasn't painted yet than gdi+ (or what is under the hood of C# WinForm painting) has not initialized yet. So nothing to synchronize.
Please double check this.

How can I change components of a form from within another object?

I got an object, which is called in my form1-window. How can I change anything from the form1-object from within this object, for example a label or a processbar?
It would be a bad (circular) design to give your object a reference to your form. Use an interface or a delegate (callback).
// untested code
class MyObjectClass
{
public delegate void Reportback(int percentage);
public void DoSomething(Reportback callBack) { ...; callback(i*100F/total); ...}
}
class Form1: Form
{
private void reportProgress(int percent) { ...; progressbar1.value = percent; }
void SomeMethod() { myObject1.DoSomething(reportprogress); }
}
Generally speaking, when you find yourself with a need for one object to manipulate the private fields of another, your design needs work.
For instance, an class that's performing some kind of long-running business logic shouldn't be updating a ProgressBar. First, that's not its job. Second, that couples the functioning of the business logic to the implementation details of the user interface.
It's much better if the class simply raises events as it performs its long-running task. For instance, look at this class:
public class BusinessLogic
{
public event EventHandler ProgressChanged;
private int _Progress;
public int Progress
{
get { return _Progress; }
private set
{
_Progress = value;
EventHandler h = ProgressChanged;
if (h != null)
{
h(this, new EventArgs());
}
}
}
}
Whenever any method in this class sets the Progress property, the ProgressChanged event gets raised. In your form, you can instantiate the object with logic like this:
private BusinessLogic Task;
private void Form1_Load(object sender, EventArgs e)
{
Task = new BusinessLogic();
Task.ProgressChanged += Task_ProgressChanged;
}
void Task_ProgressChanged(object sender, EventArgs e)
{
taskProgessBar.Value = ((BusinessLogic) sender).Progress;
}
Now, every time a method in the Task object sets the Progress property, the ProgressBar in the form will get updated.
This is quite a bit more code to write than just having the object update the ProgressBar, sure. But look what you get out of it. If you have to refactor your form into two forms, and move the task to a new form, you don't have to touch the BusinessLogic class. If you move your ProgressBar from being a control on the form to being a ToolStripProgressBar on a ToolStrip, you don't have to touch the BusinessLogic class. If you decide that progress reporting isn't important, you don't have to touch the BusinessLogic class.
Essentially, this approach protects the BusinessLogic class from having to know anything about the user interface. This makes it a lot easier to change both the business logic and the user interface as your program evolves.
I agree with Henk... but anyway, you can modify any item on a form if you change the visibility of the controls, there is even a property for doing this.

Categories