i have a winform application in which i have a lot of controls that needs continuos monitoring. For example there is a button and it should be enabled only when two other buttons are disabled, and they disable at separate instances. So what i am doing now is using a thread to monitor the two other buttons in a continuos while loop such as
while(true)
{
if(btn.enabled==false and btn1.enabled==false)
{
bt3.enabled==true
}
}
though it does what i need it seems wrong to me. Also its very expensive considering the number of threads i have to spawn to manage my controls, there are certain controls that needs to check five or six different things to before it can do an action and threading seems the only way possible to me.
Please tell me if there is any other way to do this
Not only is that inefficient, it is incorrect; you should never access a control's properties except from the UI thread, due to thread affinity. Setting properties (the enabled assignment) is especially bad, but reading them (the enabled check) is bad enough.
Rather than continuous monitoring, those forms should update themselves, based on event notifications. For example, by hooking EnabledChanged on the two buttons.
// (in the form initialization code)
btn.EnabledChanged += UpdateButtons;
btn1.EnabledChanged += UpdateButtons;
//...
private void UpdateButtons(object sender, EventArgs args)
{
bt3.Enabled = !btn.Enabled && !btn1.Enabled;
}
you could also (instead) do this at the code that causes the Enabled property to change.
Related
I am reusing the same code for clearing the screen many times in my program, and I thought about turning it into a class, but I still don't get how classes work and how to properly make one.
My code to clear buttons and other controls is as follows:
List<RichTextBox> _richTextBoxes = this.Controls.OfType<RichTextBox>().ToList();
List<Button> _buttons = this.Controls.OfType<Button>().ToList();
List<Label> _labels = this.Controls.OfType<Label>().ToList();
List<TextBox> _textBoxes = this.Controls.OfType<TextBox>().ToList();
foreach (var rich in _richTextBoxes)
{
this.Controls.Remove(rich);
}
foreach (var button in _buttons)
{
this.Controls.Remove(button);
}
foreach (var label in _labels)
{
this.Controls.Remove(label);
}
foreach (var textBox in _textBoxes)
{
this.Controls.Remove(textBox);
}
As others already mentioned, it's a rare practice to remove/create all controls of a container (Form, Panel, etc) at runtime, and a possible waste of PC resources.
Of course you can use:
Form.Controls.Clear();
Or
Panel.Controls.Clear();
But, what's wrong with placing all your controls in a Panel, for example, and simply hiding said panel? seen you get the same result in a more efficient way
If you opt for this, it's as simple as this line:
Panel.Visible = false; // or true
Hiding 100s or 1000s of controls is inefficient and wasteful
Contrary to the other answer's idea that
Panel.Visible;
...is somehow more efficient, it is not. Hiding a control does not release any resources the control might have requested the obvious being a window handle. Handles, fonts etc are all part of the GDI pool, a rather limited resource. Depending on what version of Windows you are running you might be limited to anything between 256 and 16,384 GDI handles per process.
So think before hiding 100s of controls. Much better to just destroy the unwanted and create the ones you need.
Back to the question
I am reusing the same code for clearing the screen many times in my program
The bigger question is why you want to do that?
Though a legitimate programming scenario, given that "but I still don't get how classes work" , it is unlikely you are at the dynamic-UI-apps stage based on say dynamic selection of a database table.
Consider creating multiple forms via the Designer and don't modify them at runtime. That's alot easier if dynamic UIs isn't really a requirement.
An easier way to change things at runtime
Otherwise if you are super keen to continue with changing things at runtime, consider placing all the controls in a child Panel belonging to the form.
Then you can just call Clear():
myPanel.Controls.Clear(); // Job done
Here is what my code looks like:
private void exportToExcelButton_Click(object sender, EventArgs e)
{
txtBox.Clear();
txtBox.AppendText("Beginning Export...");
ExportExcel(txtBox);
txtBox.AppendText("Export complete...");
}
The problem I am having is that whenever the button is clicked (to execute the function above), only part of the current text in the TextBox (System.Windows.Forms.TextBox) is cleared, and replaced with the first line: "Beginning Export ...".
However once the function ExportExcel(txtBox) is done executing, then the entire text is replaced by the new one generated in ExportExcel(txtBox).
Inside ExportExcel(txtBox); I have several txtBox.AppendText() statements explaining to the user the actions being made.
I have tried clearing the text with txtBox.Text = String.Empty; and txtBox.Text = "";and neither have worked.
Let me know if anything needs to be clarified, thanks.
Looks like you're blocking the GUI thread, preventing the text box from redrawing itself. This is one reason why you shouldn't perform long-running tasks on the GUI thread. Use a background thread instead. That way you leave the GUI thread free to perform important operations like drawing, responding to mouse clicks, etc.
Have you tried the textBox.Refresh , before calling txtBox.AppendText("Beginning Export...").
The method invalidates the control.
On the other hand, if you use a background thread, then you should update the UI only by overriding the Progress Changed event. Background threads are not meant for updating user interfaces. Try searching for Worker threads and UI threads. They correlate to MFC, but the concept is the same.
Also keep in mind the cross thread calls.
I agree with dvnrrs. However if you are unable to do this, try calling txtBox.Refresh();after adding each line of text.
There is another method called Application.DoEvents(); that has a similar behavior, but its use is not recommended since it sort of short-circuits the normal application flow and can cause your application to fail unexpectedly or do strange things.
EDIT 1 :
Apparently, i have started to achieve a 3D rendering in WPF environment in an incorrect way. Ofc there is a solution for my question here below, but i suggest to read the update of Sheridan's answer and use his recommendations to achieve this. It's not only secure, but also better for performance. Although it's a bit complex to understand it, but once you understand it, you can start rendering multiple 3D applications in WPF.
Thanks for your assistance Sheridan !
Question ;
i am quite new in WPF, and i want to design a continuous rendering (like in gaming applications) with WPF. I am using multithreading to provide a better UI control (start/stop button fe). Or the event could be disposed due of using an infinite loop to render the 3D world.
But, my problem is that when running the program, i get an Invalid operation was unhandled error. The issue is that there is an object which is a property of the main thread, thus the new thread might not access it.
from XAML file,
<Grid>
<!-- ui controls omitted ... -->
<Viewport3D Name="myViewport" ClipToBounds="True">
<!-- all inits, camera, pos, ... -->
</Viewport3D>
</Grid>
in main class;
/// <summary>this method is done to render the 3D app in other thread.</summary>
private void Runtime(Viewport3D vp) {
System.Diagnostics.Debug.WriteLine("runtime ");
Render3D r3d = new Render3D(vp);
// actual startup
while (keepRunning) {
r3d.Init3D();
}
}
/// <summary>this method toggles the game runtime</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StartOrStop(object sender, RoutedEventArgs e) {
keepRunning = !keepRunning;
if (keepRunning) {
buttonStartStop.Content = "Stop";
// thread
t1 = new Thread( () => Runtime(myViewport) );
t1.Start();
}
else {
buttonStartStop.Content = "Start";
t1.Abort();
}
}
The 3DViewport object is initialized in the XAML file. that's why i am passing it to the new thread, that it can create an object which is using that 3DViewport class.
Here below is a sample of the Render3D class.
// constructor
internal Render3D(Viewport3D v) {
currViewport = v;
}
/// <summary>get called in loops to render gfx</summary>
internal void Init3D() {
// clear rendered view
ClearRenderWindow();
// add landscape
AddLandScape();
}
/// <summary>clear window to re-render gfx</summary>
private void ClearRenderWindow() {
ModelVisual3D mv;
// ***** error got caught here below ******
for (int i = currViewport.Children.Count - 1; i >= 0; i--) {
mv = (ModelVisual3D)currViewport.Children[i];
if (mv.Content is DirectionalLight == false) currViewport.Children.Remove(mv);
}
}
The error is caught at the currViewport.Children.Count method. As previously said, the issue is that the current thread doesn't have ownership of that object. It's the first time in my multithreading experience to face with this issue. I have searched around, but couldn't find a solution.
Does anyone know how to pass the ownership of the Viewport3D-object, or a good workaround ?
Firstly, I would like to say that WPF is not a good framework for developing all but the simplest games... I would advise using a gaming framework like Microsoft's XNA instead.
However, if you insist on using WPF, then I would like to bring the CompositionTarget.Rendering event to your attention. It basically uses the frame rate of the host computer to render regulates graphics passes, avoiding the need to use timers.
You should also take a look at the How to: Render on a Per Frame Interval Using CompositionTarget page at MSDN for more helpful information and code examples.
Also, please read this extract from the book 'WPF Control Development Unleashed: Building Advanced User Experiences':
Some readers may recognize a similarity between this approach and higher-end graphics
subsystems like DirectX. Do not mistake CompositionTarget.Rendering for a good injection
point to create a WPF-based gaming graphics engine. High-end graphics and ultrahigh
frame rates are not the goal of this particular aspect of WPF animation.
Similar to the DispatcherTimer approach, animations based on CompositionTarget.Rendering
are also not time-bound. However, these events are synced with the render thread resulting
in smoother animations than the DispatcherTimer. Also there is no need to start and
stop a timer, although you may have to detach and attach the event handler to improve
performance.
UPDATE >>>
Having discovered that this is just for a course project, I would ignore my previous comment and your code example so far. Don't try to create a new rendering system when there is one already. Instead, you should follow this approach:
Create data objects that implement the INotifyPropertyChanged interface and have X, Y, and DirectionVector (could be a Size struct) public properties.
Add a Move method (or Swim method for your Fish class) in which you update the data objects' X and Y properties dependant on the value of the DirectionVector property.
Add a ListBox control to your UI.
Create a collection property to hold your data objects, add items and bind the collection to the ListBox.ItemsSource property.
Create a DataTemplate to define what your Fish objects look like... you can use the Path class to draw them and even use a RotateTransform to rotate them (the angle can be calculated from the DirectionVector property). In the DataTemplate, you can bind the X and Y properties to the `Margin' property.
Finally, add an infinite loop (possibly with a break out option) and in that loop, iterate through the collection of data objects and call Move() on each one. This will update the data objects' positions in the ListBox.
As a general rule, the only objects that can change thread allegiance in WPF are those that derive from Freezable. (E.g., Model3D is a freezable, and so, therefore, are things like Light and GeometryModel3D.)
Elements that participate directly in the visual tree do not derive from Freezable. They derive from Visual (usually, although not always, via FrameworkElement). Consequently, visual elements are forever associated with the thread on which you created them. Freezables are usually descriptive items that tell visual tree elements what to do. For example, brushes (whether solid, gradient fill, image brush or whatever) are freezables, but to do something with a brush you need to use it as a property of some visual element (i.e. something that's not a freezable) e.g., the Fill of a Rectangle.
So Model3D falls into this category - it is a description of a 3D model, but it doesn't actually know how to render itself. You provide this description to some visual element that does know how to render the model (e.g. Viewport3D).
So it's possible to build up Model3D on a worker thread, and then pass that over to the UI thread.
However, you can only start using a freezable object from some thread other than the one on which it was created after you have frozen it by calling Freeze. This, as the name suggests, prevents further modification. Once a freezable is frozen, it is no longer associated with any particular thread, so you can use it from whatever thread you like.
The intended usage model here is:
Build something complicated on a worker thread
Freeze it
Attach it to something that knows how to render it in the UI thread
This might be appropriate if you wanted to build a complex Model3D that was going to take a long time to construct, and you didn't want to make the application unresponsive while that was happening.
However, this is not of any use if you need the model to be modifiable as time goes on. If that's what you need (and it sounds like it is) then you tend to have no choice but to create the model on the UI thread - if you create a freezable that you never actually freeze (because you need the ability to change it) then you have to create it on the same thread that will render it. When you want to update the model, you either need to ensure updates are done on the UI thread, or you could use data binding, which is able to handle change notification events on any thread, and it will marshal those to the UI thread for you.
However, I'm wondering whether you really need multithreading at all. You give the reason as
to provide a better UI control (start/stop button fe).
That's not really a reason to use a separate thread. There's nothing stopping the UI thread from performing updates to the model and also responding to UI input. You just need to make sure the code that updates the model regularly returns control to the event loop.
The only reason for using a separate thread would be if the calculations that determine what the updates to the model should be are computationally expensive. For example, if you're writing code that performs a complex and highly detailed simulation of some process, and then renders the result, it might make sense to perform the calculations on a worker thread to enable the UI to remain responsive. But even then, once those calculations are complete, you'd need to ensure that the updates you make to the model based on the results of those calculations are done on the UI thread, not the worker thread.
It may be worth considering whether you can get away with building a new model each time round. The user might not actually notice if you drop the old model and instantly replace it with a newly built model. That can enable you to build the entire model on the worker thread, because you can then freeze it. If you build a new model each time, it's safe to freeze, because any time you want to change something, you just build a new model instead of updating the old one.
Yet another variation is to have a model that consists mostly of frozen pieces, contained by some unfrozen top-level elements.
I'm using the ICommand interface to perform binding on a couple of buttons in my application, the Run and Close button. I'm using a helper method that was mentioned in this question to hook up the ICommand to a delegate as follows:
_someCommand = new DelegateCommand(this.DoSomething, this.CanDoSomething);
private ICommand _someCommand;
public ICommand SomeCommand
{
get { return _someCommand; }
}
private void DoSomething(object state)
{
// do something here
}
private bool CanDoSomething(object state)
{
// return true/false here is enabled/disable button
}
This appears to work just fine as, in my implementation, CanDoSomething returns the value of a property that I have in my application.
If I set the initial value of the property to true, then the button is enabled and false it is disabled.
I have a series of events that are raised from a BackgroundWorker in the application layer back to the ViewModel that change the value of the property to true or false based on the current state of the application.
The current problem I'm having is that the button is not "re-enabling" when I set the value to true after the work has completed. If I click somewhere within the window, it will update. So, therefore, I'm thinking than manually refreshing the window will solve my problem, at least for the interim. This feels a bit gross to do it this way, but I'm kind of at a loss for what else I could try.
If anyone has any suggestions, I'm all ears.
Thanks for the help!
Ian
Edit -
A little bit more information on the application itself. It uses a background worker in the application thread to handle the "work". The application is a simple utility to manage the creating of tables and loading of data into the tables. We use a lot of pre-defined SQL scripts to setup our test environment, so this is a simple utility that allows us to do that sort of thing based on parameters provided by the user in the UI.
Hopefully that helps, because when I re-read my question it read as if I were doing everything in the UI thread, which is not the case. Progress reports are raised back up to the UI thread and everything is updated as expected, except the button..
CommandManager.InvalidateRequerySuggested() may be the answer - it tells all the commands to check whether they are enabled or not.
You have to raise the CanExecuteChanged event:
http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged.aspx
This may be more useful than the other answer in cases where you know you should re-evaluate a single control, and re-evaluating all the controls would be costly. The other answer is simpler if you don't have a case like that, though.
I am working on a text editor that is based on RichEditBox. I have implemented functionality "Go to line" which eventually resolves to
TextPointer.Paragraph.BringIntoView();
Along with this I also set the caret position.
What I found out is that BringIntoView only works when I click on the RichEditBox first (focus it). Otherwise it seems to get ignored. I can see that the caret position has been adjusted by the code around BringIntoView though.
Does anybody know what is the reason/nature of that problem? How can I overcome it?
Found a workaround for this, not sure if it will work in a pure WPF environment, in my case I'm running WPF inside a mainly Windows Forms solution using WPF UserControls where needed.
Instead of invoking BringIntoFocus() immediately, defer it to a later moment by adding it to a queue that gets handled by a timer. For example:
System.Windows.Forms.Timer DeferredActionTimer = new System.Windows.Forms.Timer() { Interval = 200 };
Queue<Action> DeferredActions = new Queue<Action>();
void DeferredActionTimer_Tick(object sender, EventArgs e) {
while(DeferredActions.Count > 0) {
Action act = DeferredActions.Dequeue();
act();
}
}
In your forms constructor, or in the OnLoad event add:
DeferredActionTimer.Tick += new EventHandler(DeferredActionTimer_Tick);
DeferredActionTimer.Enabled = true;
Finally, instead of calling TextPointer.Paragraph.BringIntoView(); directly, call it like this:
DeferredActions.Enqueue(() => TextPointer.Paragraph.BringIntoView());
Note that the Windows Forms timer kicks events off in the main thread (via the message pump loop). If you have to use another timer you need a bit of extra code. I'd recommend you to use System.Timers.Timer rather than the System.Threading.Timer (it's a little more thread-safe). You would also have to wrap the action in a Dispatcher.Invoke structure. In my case, the WinForms timer works like a charm.
Can't you just give the RichTextBox(?) focus first then, using Keyboard.Focus(richTextBox) or richTextBox.Focus()?