White window flashes when using UsesPerPixelOpacity to make a wpf window transparent - c#

I create a transparent window with the following code here is the link
HwndSourceParameters p = new HwndSourceParameters("TestWindow", 100, 100);
p.UsesPerPixelOpacity = true;
p.WindowStyle |= 0x10000000; // WS_VISIBLE
HwndSource hwndSource = new HwndSource(p);
hwndSource.RootVisual = ellipse;
It works ok. But a white window always flashes before the ellipse appears and the size of white window is same with the parameters of HwndSourceParameters. It seems the code will render a white background window and then refresh it into transparent background and rootvisual. Anything wrong?

Related

Prevent new window from going offscreen

If the MainWindow is too close to the edge of the screen, opening a New Window with relative positioning can go off screen.
I'd like to have it detect that it's off screen and reposition itself close to the edge, even overlapping the MainWindow. For Top, Bottom, Left and Right.
Example Project Source
https://www.dropbox.com/s/3r2guvssiakcz6f/WindowReposition.zip?dl=0
private Boolean IsWindowOpened = false;
// Info Button
//
private void buttonInfo_Click(object sender, RoutedEventArgs e)
{
MainWindow mainwindow = this;
// Start Info Window
InfoWindow info = new InfoWindow(mainwindow);
// Only Allow 1 Window Instance
if (IsWindowOpened) return;
info.ContentRendered += delegate { IsWindowOpened = true; };
info.Closed += delegate { IsWindowOpened = false; };
// Position Relative to MainWindow
info.Left = mainwindow.Left - 270;
info.Top = mainwindow.Top + 0;
// Open Info Window
info.Show();
}
Example of 1280x720 screen
MainWindow Center Screen
InfoWindow -270px Left, 0px Top
Off Screen
MainWindow Top Left of Screen
InfoWindow -270px Left, 0px Top
Reposition In Screen
MainWindow Top Left of Screen
InfoWindow -160px Left, 0px Top
To Place Dialog to Left
The quick-n-dirty way of doing this is to simply use Math.Max (i.e. the right-most value) to use the offset or 0, whichever is larger. Using System.Windows.Forms.Screen allows us to accommodate for multiple monitors.
private void btnInfoToLeft_Click(object sender, RoutedEventArgs e)
{
// Figure out which screen we're on
var allScreens = Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
// Place dialog to left of window, but not past screen border
InfoWindow info= new InfoWindow();
info.Left = Math.Max(this.Left - info.Width - 10, thisScreen.WorkingArea.Left);
info.Top = Math.Max(this.Top - info.Height - 10, thisScreen.WorkingArea.Top);
info.Show();
}
Note that we use the Width of the dialog - it's ActualWidth will be 0 before it is shown on the screen.
To Place Dialog to Right
Similarly, we need to figure out the right-most boundaries of the screen, and account for the width of the main window and dialog, and take the Math.Min value (i.e. the left-most value).
private void btnInfoToRight_Click(object sender, RoutedEventArgs e)
{
// Figure out which screen we're on
var allScreens = Screen.AllScreens.ToList();
var thisScreen = allScreens.SingleOrDefault(s => this.Left >= s.WorkingArea.Left && this.Left < s.WorkingArea.Right);
// Place dialog to right of window, but not past screen border
InfoWindow info = new InfoWindow();
info.Left = Math.Min(this.Left + this.ActualWidth + 10, thisScreen.WorkingArea.Right - info.Width);
info.Top = Math.Min(this.Top + this.ActualHeight + 10, thisScreen.WorkingArea.Bottom - info.Height);
info.Show();
}
This time, we still use the Width of the dialog, but the ActualWidth of the main window, which will be the width after it has been drawn (and possibly resized).
In these examples, I've also placed the dialog above/below the main window. You can set the top of the dialog to be equal to the top of the main window, or play around to get it to line up with the bottom, etc., using this example as a guide.
There are no shortcuts for this kind of problem. You'll have to figure out the dimensions of the screen you're using, then adjust the position of the info window manually.
Take a look at this StackOverflow post: How to get the size of the current screen in WPF?

How to create transparent controls in Windows Forms when controls are layered

I am trying to implement a "Fillable Form" in which editable text fields appear over top of an image of a pre-preprinted form for a dot matrix printer. (using c# and Windows Forms and targeting .Net 2.0) My first idea was to use the image as the Windows Form background, but it looked horrible when scrolling and also did not scroll properly with the content.
My next attempt was to create a fixed-size window with a panel that overflows the bounds of the window (for scrolling purposes.) I added a PictureBox to the panel, and added my textboxes on top of it. This works fine, except that TextBoxes do not support transparency, so I tried several methods to make the TextBoxes transparent. One approach was to use an odd background color and a transparency key. Another, described in the following links, was to create a derived class that allows transparency:
Transparency for windows forms textbox
TextBox with a Transparent Background
Neither method works, because as I have come to find out, "transparency" in Windows Forms just means that the background of the window is painted onto the control background. Since the PictureBox is positioned between the Window background and the TextBox, it gives the appearance that the TextBox is not transparent, but simply has a background color equal to the background color of the Window. With the transparency key approach, the entire application becomes transparent so that you can see Visual Studio in the background, which is not what I want. So now I am trying to implement a class that derives from TextBox and overrides either OnPaint or OnPaintBackground to paint the appropriate part of the PictureBox image onto the control background to give the illusion of transparency as described in the following link:
How to create a transparent control which works when on top of other controls?
First of all, I can't get it working (I have tried various things, and either get a completely black control, or just a standard label background), and second of all, I get intermittent ArgumentExceptions from the DrawToBitmap method that have the cryptic message "Additional information: targetBounds." Based on the following link from MSDN, I believe that this is because the bitmap is too large - in either event it seems inefficient to capture the whole form image here because I really just want a tiny piece of it.
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.drawtobitmap(v=vs.100).aspx
Here is my latest attempt. Can somebody please help me with the OnPaintBackground implementation or suggest a different approach? Thanks in advance!
public partial class TransparentTextbox : TextBox
{
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e); // not sure whether I need this
if (Parent != null)
{
foreach (Control c in Parent.Controls)
{
if (c.GetType() == typeof(PictureBox))
{
PictureBox formImg = (PictureBox)c;
Bitmap bitmap = new Bitmap(formImg.Width, formImg.Height);
formImg.DrawToBitmap(bitmap, formImg.Bounds);
e.Graphics.DrawImage(bitmap, -Left, -Top);
break;
}
}
Debug.WriteLine(Name + " didn't find the PictureBox.");
}
}
}
NOTE: This has been tagged as a duplicate, but I referenced the "duplicate question" in my original post, and explained why it was not working. That solution only works if the TextBox sits directly over the Window - if another control (such as my Panel and PictureBox) sit between the window and the TextBox, then .Net draws the Window background onto the TextBox background, effectively making its background look gray, not transparent.
I think I have finally gotten to the bottom of this. I added a Bitmap variable to my class, and when I instantiate the textboxes, I am setting it to contain just the portion of the form image that sits behind the control. Then I overload OnPaintBackground to display the Bitmap, and I overload OnPaint to manually draw the text string. Here is the updated version of my TransparentTextbox class:
public partial class TransparentTextbox : TextBox
{
public Bitmap BgBitmap { get; set; }
public TransparentTextbox()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(this.Text, this.Font, Brushes.Black, new PointF(0.0F, 0.0F));
}
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.DrawImage(BgBitmap, 0, 0);
}
}
... and here is the relevant part of how I instantiate:
Bitmap bgImage = (Bitmap)Bitmap.FromStream(Document.FormImage);
PictureBox pb = new PictureBox();
pb.Image = bgImage;
pb.Size = pb.Image.Size;
pb.Top = 0;
pb.Left = 0;
panel1.Controls.Add(pb);
foreach (FormField field in Document.FormFields)
{
TransparentTextbox tb = new TransparentTextbox();
tb.Width = (int)Math.Ceiling(field.MaxLineWidth * 96.0);
tb.Height = 22;
tb.Font = new Font("Courier", 12);
tb.BorderStyle = BorderStyle.None;
tb.Text = "Super Neat!";
tb.TextChanged += tb_TextChanged;
tb.Left = (int)Math.Ceiling(field.XValue * 96.0);
tb.Top = (int)Math.Ceiling(field.YValue * 96.0);
tb.Visible = true;
Bitmap b = new Bitmap(tb.Width, tb.Height);
using (Graphics g = Graphics.FromImage(b))
{
g.DrawImage(bgImage, new Rectangle(0, 0, b.Width, b.Height), tb.Bounds, GraphicsUnit.Pixel);
tb.BgBitmap = b;
}
panel1.Controls.Add(tb);
}
I still need to work on how the text looks when I highlight it, and other things like that, but I feel like I am on the right track. +1 to Reza Aghaei and Mangist for commenting with other viable solutions!

Make GDI drawing not clickable

I have a transparent WinForms app with GDI drawings (I use it as an overlay). The problem is that whenever I click on the GDI drawing the focus goes to the app window. How do I turn that of?
You need to use the right Color as the TransparencyKey!
Everything makes the Form clickable except Color.Fuchsia.
A not really explicable, let alone documented, 'feature', that may have started as a bug but now, hopefully forever and ever, let's us switch from clickable transparent forms, onto which we can draw and non-clickable ones through which we can interact with the background items..
// click-through:
this.BackColor = Color.Fuchsia;
this.TransparencyKey = this.BackColor;
// clickable:
this.BackColor = Color.FromArgb(255, 147, 151, 162); // any non-fuchsia color
this.TransparencyKey = this.BackColor;
As long as you implemented the overlay correctly (an owned window displayed with the Shown(owner) overload, example) then it just takes a little scrap of copy/paste code. Windows asks you what part of the window was clicked, you can respond with "it is transparent". So it will keep looking for anybody that is interested, its parent window is next.
Like this:
protected override void WndProc(ref Message m) {
const int WM_NCHITTEST = 0x84;
const int HTTRANSPARENT = -1;
if (m.Msg == WM_NCHITTEST) m.Result = new IntPtr(HTTRANSPARENT);
else base.WndProc(ref m);
}

C# moving window over taskbar

const int SWP_SHOWWINDOW = 0x0040;
int Left = Convert.ToInt32(LeftSizeTextBox.Text);
int Top = Convert.ToInt32(TopSizeTextBox.Text);
int Width = Convert.ToInt32(WidthSizeTextBox.Text);
int Height = Convert.ToInt32(HeightSizeTextBox.Text);
IntPtr handle = FindWindow(null, WindowTextBox.Text);
SetWindowPos(handle, -2, Top - 8, Left - 30, Width + 32, Height + 38, SWP_SHOWWINDOW);
SetForegroundWindow(handle);
I want TRUE fullscreen. Currently mine only puts it mostly fullscreen but it stops slightly below the bottom, and doesn't cover the taskbar. How do I made it completely (Windowed borderless) while still being able to alt tab? If you've ever used shiftwindow, that is my desired effect.
To make a (border less) window go true fullscreen, go to your Form's properties (CTRL + W, P) then set ControlBox to false and WindowState to Maximized. This should display your form over the taskbar. Here is the code of you want to do it dynamically:
//ControlBox
this.ControlBox = false;
//WindowState
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
Edit: This will only make your form go true fullscreen. Looking at your code I think you are trying to make other forms go fullscreen.

Windows Vista Sidebar Equivalent

I'm trying to create an application that looks similar to the Windows Vista sidebar. There's an API that allows docking toolbars on the screen (AppBar), but it's not exactly what I'm looking for.
How can I attach a Form to the desktop and dock it to the side of the screen, but without preventing other windows from overlapping it?
With all the following options you get a Sidebar look-a-like (the code below is for a WPF Window):
//width of the sidebar
Width = 300;
//height (remember to add a reference to the System.Windows.Forms dll)
Height = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
//no window style means no border
WindowStyle = WindowStyle.None;
//not resizable
ResizeMode = ResizeMode.NoResize;
//allow a transparent sidebar
AllowsTransparency = true;
//change the color
Background = new SolidColorBrush(Colors.CadetBlue);
//set the opacity (how much transparent)
Opacity = 0.5d;
//offset from the top
Top = 0;
//offset from the left (calculated so it shows on the right side)
Left = SystemParameters.PrimaryScreenWidth - (double)GetValue(WidthProperty);
//set it the topmost window
Topmost = true;
//hide the icon from the taskbar
ShowInTaskbar = false;
Hope this helps!
Update:
Here's a similar solution for when you're using WindowsForms, altough with WPF you have much more possibilities! The differences are minor, everything explains itself. The last line I added hides the window taskbar-icon. Do not place the code in the constructor of the Form but in the Load-event, otherwise the Location will be wrong. In WPF this doesn't matter.
Width = 300;
Height = Screen.PrimaryScreen.Bounds.Height;
FormBorderStyle = FormBorderStyle.None;
BackColor = Color.CadetBlue;
Opacity = 0.5d;
Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - Width, 0);
TopMost = true;
ShowInTaskbar = false;

Categories