I got a small program which uses 2 transparent windows.
To position them i added a small colored label.
I got this on both transparent windows.
Transparent window1 has 2 buttons increase and decrease opacity.
This button works for transparent window1 but not for transparent windows2.
private void BtnIncreaseOpacity_Click(object sender, RoutedEventArgs e)
{
lblDrag.Opacity = 100;
win2.lblDrag2.Opacity = 100;
}
In the Public partial class
TrackerMessage win2 = new TrackerMessage();
The code is accepted, but it does not work.
So i am pretty sure this is doing something different then i think.
The other problem is similar.
the transparent windows2(win2) needs to make its labels visible when a timer on transparent window1 reaches 0.
But thats more of the same problem, since right now you cannot access anything from window1 on window2
So the question is, what am i doing wrong.
Really, both Opacity properties should be bound to a view model and then the button's command modifies the backing properties (allowing the UI to update).
That said, at the very least you should have each window handling its own UI (and I suspect that is the problem). Instead of setting the property directly for win2, have it call a function:
win2.SetOpacity(100);
Where SetOpacity looks like:
public void SetOpacity(int newValue)
{
lblDrag2.Opacity = newValue;
}
You may also need to do a Dispatcher.BeginInvoke (since this is being called from the other window's UI thread). In that case, the function is:
public void SetOpacity(int newValue)
{
Dispatcher.BeginInvoke(new Action(() =>
{
lblDrag2.Opacity = newValue;
}));
}
Let me know if I can clarify anything!
Related
I have been working on a WPF application, and my application freezes as I Navigate from one window to another, The first window is for Login and the second window is the main window where work is carried out (the application is a form based application where Data is Written to and from a Database like MySQL)
2nd Window Operations
In the second window, I have created a Menu by placing Icons in a List and then Handling their click events to navigate to a page
These Pages are not the UI Element 'Page' but are Usercontrols I refer to them by Pages for Simplicity, and these usercontrols are added to the grid by grid.children.Add(UsercontrolName);. . . . . . This part of adding usercontrols as you can see is done in code behind(C#) and not by XAML
Click Event Handling
All i do is I simply shift the visibilty of a Usercontrol, so for example an icon that is related to a say Usercontrol A, that usercontrols visibity is set to visible and all other usercontrols visibilty is set to Collapsed, by default all usercontrols Visibilty is set to Collapsed
The Problem
What I do not Understand is what is causing this Lag or Freeze or even how to find or figure this out, I understand that WPF is an STA and UI elements such as a Grid/usercontrols should be run on the main thread , so if we Used Dispatcher to resume to the Main thread the UI wont freeze but it would Still take time to load, and I want to stop this delay
Although Contradictory I still think its the Adding of Usercontrols to the Grid is causing slow speeds, bcz these Usercontrols have numerous UI elements that I am using from a library called MaterialDesigninXAML, its a form based application so have like 8 Usercontrols all of them with textboxes and Imageboxes and buttons etc, and adding these Usercontrols at once is causing the Overhead
And I am stuck at this Delay / Freeze of the UI
The code below is a ditto example of my original code, I posted the example bcz my code is lengthier with the same problem
this is the part of the code that lags
Dispatcher.InvokeAsync(() =>
{
UserControl usc;
usc = new Y1();
usc.Tag = "Memeber";
no.Children.Add(usc);
Y4 usc2 = new Y4();
usc2.Tag = "CheckBoxList";
no.Children.Add(usc2);
Y5 usc3 = new Y5();
usc3.Tag = "Reports";
no.Children.Add(usc3);
ShowUserContro("Memeber");
}
);
private void ShowUserContro(string v)
{
foreach (UIElement item in no.Children)
{
if (item is UserControl)
{
UserControl x = (UserControl)item;
if (x.Tag != null)
{
if (x.Tag.ToString().ToUpper() == v.ToUpper())
{
x.Visibility = Visibility.Visible;
}
else
{
x.Visibility = Visibility.Collapsed;
}
}
}
}
}
private void ListViewMenu_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ShowUserContro((((ListViewItem)((ListView)sender).SelectedItem).Name));
}
I'm using the WinForms version of <WebBrowser> in my WPF app, a la <WindowsFormsHost> because in general it works a lot better than the Windows.Controls version. However I have one problem that has to do with touch screens.
Normally I set the ManipulationBoundaryFeedback event handler on controls to immediately handle the event, thereby preventing any boundary feedback, and I've tried to do so with this code:
MainWindow.xaml
<WindowsFormsHost IsManipulationEnabled="True" ManipulationBoundaryFeedback="WindowsFormsHost_OnManipulationBoundaryFeedback">
<forms:WebBrowser />
</WindowsFormsHost>
MainWindow.xaml.cs
private void WindowsFormsHost_OnManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
e.Handled = true;
}
On ordinary WPF controls, generally speaking, this works. And what I mean by "works" is that if you use your finger on the touch screen and drag up or down, you don't get the effects of touch screen intertia; that is, it doesn't shift the entire window up or down once you hit the boundary.
Here's a picture to illustrate what's happening:
As you can see, if I drag down within the browser, it pulls the entire window with it. What can I do to prevent this?
Not sure if you still need this answer but to prevent this unnecessary behavior simply inherit WindowsFormsHost class and override OnManipulationBoundaryFeedback like this:
public class MyClass : WindowsFormsHost
{
// Override is optional to remove unnecessary behavior
protected override void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e)
{
// uncomment this to use base class implementation
//base.OnManipulationBoundaryFeedback(e);
e.Handled = true;
}
}
Edited
I have made a small test and added this code for my control
protected override void OnManipulationBoundaryFeedback(ManipulationBoundaryFeedbackEventArgs e)
{
Debug.WriteLine("OnManipulationBoundaryFeedback");
}
private void MyControl_ManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
Debug.WriteLine("MyControl_ManipulationBoundaryFeedback");
}
and Output was the following
OnManipulationBoundaryFeedback
MyControl_ManipulationBoundaryFeedback
OnManipulationBoundaryFeedback
MyControl_ManipulationBoundaryFeedback
OnManipulationBoundaryFeedback
MyControl_ManipulationBoundaryFeedback
OnManipulationBoundaryFeedback
MyControl_ManipulationBoundaryFeedback
So you can see that OnManipulationBoundaryFeedback launched first and then it invokes ManipulationBoundaryFeedbackEvent handlers
You could turn off this behavior for the whole system:
Open registry ( run regedit command ) and set HKEY_CURRENT_USER\Software\Microsoft\Wisp\Touch Bouncing to 0;
if HKEY_CURRENT_USER\Software\Microsoft\Wisp\Touch not exist Bouncing item, add it( DWORD type, Not QWORD or String) and set it value to 0.
However, this is not a good approch, even if it solve my problem. Look at this manipulationboundaryfeedback-does-not-work-in-webbrowser-in-wpf.
I've got two user controls. First one, called "Indicator" is a simple Control that paints a square using OnPaint(...); No place for an error in first UserControl.
public partial class Indicator : UserControl
{
public Indicator()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.Black, 0,0,this.Width,this.Height);
}
}
The second one is a test control and consists of a panel, which contains a picture box and the first user control brought to front.
public partial class testIndicator: UserControl
{
private static Bitmap bmp;
public Indicator()
{
InitializeComponent();
loadImage();
pictureBox.Image = bmp;
indicator.BringToFront();
}
}
When I launch the test control in a dialog (From a form to make sure the program remains running) it displays fine the first time. However if the dialog is closed and opened again (while the program is still running) the OnPaint Method on Indicator doesn't get fired.
It works fine if indicator is placed without a panel onto UserControl.
And just to be clear, I've tried running Invalidate() and Invalidate()+Update() manually while testing, no changes.
Could anyone explain this behavior or possibly know a similar container that doesn't have such issue?
Edit: The mistake was so stupid that i failed to even consider it as a possibility;
The PictureBox uses a static Image, and i had code to only initialize the image once. The problem is it had a "Size largeScaleSize" object that is not static and gets initialized together with images. Since images already exist on 2nd run largeScaleSize does not get initiaized and is basically {0,0}. Scaling method has this line:
float xRatio = (float)this.tankPanel.Width / (float)largeScaleSize.Width;
//same as "xRatio = this.tankPanel.Width / 0" which is infinity
I only wonder why I never got a Division By Zero exception.
I made a small project from your description. You can download it here.
Panel was anchored to every edge in the second user control. Picture box and Indicator control were docked top and bottom inside the panel. A form containing the composite user control is run modally/modelessly. Could not reproduce faulty behavior.
I use the OnPaint (c#) event to draw something in my form. I want to get the value of a variable during the OnPaint process. But I cant get it during, only before or after the OnPaint process...
In fact, the variable is like a counter that I want to get to increase the value of a ProgressBar.
I tried adding a Thread, a Timer and "ValueChanged" events, I still can't get the value.
The code is quite long (it's for generating a HeatMap from some data).
I increase the value in some for loops during the event, and I call the OnPaint event by the "Invalidate()" function.
I hope to be clear without pasting my code (it's very long) !
Thanks.
With the code this is better : (Simplified)
public partial class HeatPainter : UserControl
{
public long _progress = 0; //My counter
public HeatPainter()
{
InitializeComponent();
}
public void DrawHeatMap(List<List<int>> Items, decimal Value, int MaxStacks, int Factor, string FileName)
{
if (_allowPaint) //If the control is ready to process
{
timer1.Start();
_progress = 0;
_allowPaint = false;
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
for (int Pass = _factor; Pass >= 0; Pass--)
{
//Some draw stuff
//...
_progress++;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
Console.WriteLine(_progress);
}
}
It looks like your repainting takes a lot of time. You are unable to change anything on the form during the repainting (it won't be changed until the end).
So you should look at the task from other point of view. What if you will create the image(like How to create a jpg image dynamically in memory with .NET?) in the parallel thread(or another parallelization construct http://www.dotnetperls.com/backgroundworker)? After it is painted you will set it as background or the .Image of some PictureBox. The form will be responsive all the time.
You will have to synchronize(to update your progress bar) but that isn't such a difficult task (Thread not updating progress bar control - C#, C# Windows Forms Application - Updating GUI from another thread AND class?).
And for the future: Threads and BackgroundWorkers are slowly going away from the.NET world. They are still used in .NET < 4.0 but .NET 4.0 and higher provide better abstractions for asynchronous operations. I recommend you to read about Tasks and Async Await. They are more appropriate for many launch-the-work-get-the-result-on-completion scenoarios.
You should use only one asyncronous construct(for example BackgroundWorker) that will paint the image. You user control should provide an event (actually it is better to refactor this functionality out of the user interface) like
public event EventHandler ProgressChanged;
and raise this event in the code that creates the image when you modify the property of Progress. Just don't forget about synchronization and dispatching(see above).
Okay, I was able to create a simple Windows Forms project that reproduces some strange behavior I found. In the designer, make a form with a ListBox (named lbx) anchored Top, Left, Right, and Bottom, and a button (button1). Now, the Form's code is here:
using System;
using System.Windows.Forms;
namespace ListBoxKaboom
{
public partial class Form1 : Form
{
private bool _initFinished = false;
public Form1()
{
InitializeComponent();
this._initFinished = true;
this.Height += 100;
this.Height -= 50;
this.Height += 50;
}
private void lbx_SelectedIndexChanged(object sender, EventArgs e)
{
this.button1.Enabled = (this.lbx.SelectedItem != null);
}
protected override void OnLayout(LayoutEventArgs e)
{
if (_initFinished)
{
int lines = (this.lbx.Height - 4) / this.lbx.ItemHeight;
this.SuspendLayout();
while (lines < this.lbx.Items.Count)
{
this.lbx.Items.RemoveAt(this.lbx.Items.Count - 1);
}
while (lines > this.lbx.Items.Count)
{
this.lbx.Items.Add("Item " + (this.lbx.Items.Count + 1).ToString());
}
this.ResumeLayout();
}
base.OnLayout(e);
}
}
}
PLEASE NOTE THE FOLLOWING INSTRUCTIONS:
Run this, click any of the items in the list box, and use the arrow keys to move down far enough to cause the list box to scroll. Kaboom.
Exception (sometimes NullReferenceException and sometimes IndexOutOfBoundsException). Any ideas why? Also, I would think that the items would be in order, but they're not. Is this just a goofy corner case that didn't get handled properly by Windows Forms, or am I doing something wrong?
Stack trace:
at System.Windows.Forms.ListBox.NativeUpdateSelection()
at System.Windows.Forms.ListBox.SelectedObjectCollection.EnsureUpToDate()
at System.Windows.Forms.ListBox.SelectedObjectCollection.get_InnerArray()
at System.Windows.Forms.ListBox.SelectedObjectCollection.get_Item(Int32 index)
at System.Windows.Forms.ListBox.get_SelectedItem()
I Copy/Pasted it to an empty Form and get a StackOverflow exception. Looking at it, with manipulation of the Items inside a Layout event I would say you deserve little better.
I realize this may be a simplification of something else, but there simply are limits to what you can do in a EDS.
My best guess: The ResumeLayout triggers a recursive layout operation. You could try to stem it with a sibling to _initFinished but I would suggest rethinking tour design here.
I copy/pasted wrong, my bad (used layout event).
Second try:
based on the two while-loops I would expect the Item strings to be in order, and no vertical scrollbar. It is clear that the listbox is confused, showing vertical scroll range and with the items out-of-order. So some 'error' is already present in the internals of the Listbox, waiting for a Scroll. I can also reproduce it with the mouse.
A workaround: You should be able to get the desired effect using the Resize event.
Attempt to an explanation: The (unmanaged part of the) Listbox gets confused by (multiple) Add/RemoveAt operations with suspended Layout. The last items are drawn at the wrong place, and the Listbox can't compute pixel-to-item.
You should not manipulate any GUI elements in the
constructor, e.g. this.Height += 100; in your example.
Strange things can happen. I have been bitten by this
several times in legacy code.
Wait until form load time - handle the base.Load event and do the
height manipulation there.
From "When does Form.Load event get raised?":
Q: "... I need to find out basically
what the difference is between
putting code in Load event's handler,
versus putting code in the Form's
constructor after the
InitializeComponents() line ..."
A: "The Load event is fired once the
control/form has been fully
initialized and has a window handle
created. Therefore once this event
has fired it is a fully usable user
interface control. Remember that
inside the constructor the actual
window handle for the control/form has
not yet been created, you are only
creating C# objects here and inside
the InitializeComponent call."