How to use Matrix and scrollbar translate offsets for drawing - c#

I'm using a Matrix to set a transformation on my Graphics object during OnPaint. I would like the scrollbars to translate the matrix when scrolled; something like this in OnScroll:
float offset = se.NewValue - se.OldValue;
if (offset == 0)
{
return;
}
if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
this.TransformMatrix.Translate(-offset, 0);
}
else
{
this.TransformMatrix.Translate(0, -offset);
}
I'm pretty sure that both WM_VSCROLL and WM_HSCROLL send a WM_PAINT because the control is redrawn without me actually calling Refresh(). I find that this painting isn't very fluid and is jittery and doesn't draw with the right transformation it seems. Should I intercept the WM_V/HSCROLL messages and manually set the scrollbar's properties (like position etc) with SetScrollInfo? If not, what should I do?

I ended up catching WM_V/HSROLL and doing the painting on my own. It works great!
Here's what I'm using if anyone wants it:
protected override CreateParams CreateParams
{
get
{
var p = base.CreateParams;
p.Style |= NativeMethods.WindowStyles.WS_BORDER;
p.Style |= NativeMethods.WindowStyles.WS_HSCROLL;
p.Style |= NativeMethods.WindowStyles.WS_VSCROLL;
return p;
}
}
private Point ScrollPosition
{
get
{
var info = new NativeMethods.ScrollInfo();
info.cbSize = (uint)Marshal.SizeOf(info);
info.fMask = NativeMethods.ScrollInfoMask.SIF_POS;
NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_HORZ, out info);
int x = info.nPos;
NativeMethods.GetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_VERT, out info);
int y = info.nPos;
return new Point(x, y);
}
set
{
var info = new NativeMethods.ScrollInfo();
info.cbSize = (uint)Marshal.SizeOf(info);
info.fMask = NativeMethods.ScrollInfoMask.SIF_POS;
info.nPos = value.X;
NativeMethods.SetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_HORZ, ref info, true);
info.nPos = value.Y;
NativeMethods.SetScrollInfo(this.Handle, NativeMethods.SBFlags.SB_VERT, ref info, true);
}
}
protected override void OnPaint(PaintEventArgs e)
{
// Clear the background color.
e.Graphics.Clear(Color.White);
e.Graphics.Transform = this.TransformMatrix.Clone();
// Do drawing here.
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing.
}
private void OnScroll(ScrollEventArgs se)
{
float offset = se.NewValue - se.OldValue;
if (se.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
this.TransformMatrix.Translate(-offset, 0);
}
else
{
this.TransformMatrix.Translate(0, -offset);
}
this.Refresh();
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case NativeMethods.WindowMessages.WM_HSCROLL:
case NativeMethods.WindowMessages.WM_VSCROLL:
this.WmScroll(ref m);
m.Result = IntPtr.Zero;
break;
default:
base.WndProc(ref m);
break;
}
}
private void WmScroll(ref Message m)
{
int smallChange = 20;
uint sb = NativeMethods.SBFlags.SB_HORZ;
var orientation = ScrollOrientation.HorizontalScroll;
if(m.Msg == NativeMethods.WindowMessages.WM_VSCROLL)
{
sb = NativeMethods.SBFlags.SB_VERT;
orientation = ScrollOrientation.VerticalScroll;
}
// Get current scroll Page and Range.
var info = new NativeMethods.ScrollInfo();
info.cbSize = (uint)Marshal.SizeOf(info);
info.fMask = NativeMethods.ScrollInfoMask.SIF_PAGE | NativeMethods.ScrollInfoMask.SIF_POS;
NativeMethods.GetScrollInfo(this.Handle, sb, out info);
int newValue = info.nPos;
var type = ScrollEventType.SmallDecrement;
switch (Unmanaged.LoWord(m.WParam))
{
case NativeMethods.SBCommands.SB_BOTTOM:
type = ScrollEventType.Last;
break;
case NativeMethods.SBCommands.SB_ENDSCROLL:
type = ScrollEventType.EndScroll;
break;
case NativeMethods.SBCommands.SB_LINEDOWN:
newValue += smallChange;
type = ScrollEventType.SmallIncrement;
break;
case NativeMethods.SBCommands.SB_LINEUP:
newValue -= smallChange;
type = ScrollEventType.SmallDecrement;
break;
case NativeMethods.SBCommands.SB_PAGEDOWN:
newValue += (int)info.nPage;
type = ScrollEventType.LargeIncrement;
break;
case NativeMethods.SBCommands.SB_PAGEUP:
newValue -= (int)info.nPage;
type = ScrollEventType.LargeDecrement;
break;
case NativeMethods.SBCommands.SB_THUMBPOSITION:
type = ScrollEventType.ThumbPosition;
break;
case NativeMethods.SBCommands.SB_THUMBTRACK:
newValue = Unmanaged.HiWord(m.WParam);
type = ScrollEventType.ThumbTrack;
break;
case NativeMethods.SBCommands.SB_TOP:
type = ScrollEventType.First;
break;
}
var newInfo = new NativeMethods.ScrollInfo();
newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
newInfo.fMask = NativeMethods.ScrollInfoMask.SIF_POS;
newInfo.nPos = newValue;
NativeMethods.SetScrollInfo(this.Handle, sb, ref newInfo, false);
int realNewValue = (orientation == ScrollOrientation.HorizontalScroll) ? this.ScrollPosition.X : this.ScrollPosition.Y;
// Fire the scroll event.
// TODO - Create a Scroll event.
this.OnScroll(new ScrollEventArgs(type, info.nPos, realNewValue, orientation));
}

Related

Custom WndProc doesn't stop resizing

I made a form that handles WM_CREATE, WM_ACTIVATE, WM_NCCALCSIZE and WM_NCHITTEST. It also overrides the paint method.
The problem is when I resize the form it doesn't stop resizing. I tried to compare the messages with a working window but spy++ keeps crashing.
Here is my WndProc code:
protected override void WndProc(ref Message m)
{
IntPtr result = IntPtr.Zero;
bool callDWP = !Win32Interop.DwmDefWindowProc(m.HWnd, m.Msg, m.WParam, m.LParam, out result);
switch (m.Msg)
{
case Win32Messages.WM_CREATE:
{
int style = Win32Interop.GetWindowLong(m.HWnd, Win32Constants.GWL_STYLE);
int styleEx = Win32Interop.GetWindowLong(m.HWnd, Win32Constants.GWL_EXSTYLE);
Win32Interop.AdjustWindowRectEx(out RECT rc, style, false, styleEx);
}
break;
case Win32Messages.WM_ACTIVATE:
{
MARGINS margins = new MARGINS
{
cxLeftWidth = Math.Abs(BorderLeft),
cxRightWidth = Math.Abs(BorderRight),
cyBottomHeight = Math.Abs(BorderBottom),
cyTopHeight = Math.Abs(BorderTop)
};
int hr = Win32Interop.DwmExtendFrameIntoClientArea(m.HWnd, ref margins);
result = IntPtr.Zero;
}
break;
case Win32Messages.WM_NCCALCSIZE:
{
if (m.WParam != IntPtr.Zero)
{
result = IntPtr.Zero;
callDWP = false;
}
}
break;
case Win32Messages.WM_NCHITTEST:
{
{
int ht = DoHitTest(m);
Console.WriteLine(ht);
if (callDWP)
{
callDWP = (ht == Win32Constants.HTNOWHERE);
result = new IntPtr(ht);
}
}
break;
}
default:
{
base.WndProc(ref m);
break;
}
}
m.Result = result;
if (callDWP)
{
base.WndProc(ref m);
}
}
Don't call base.WndProc(ref m); in default

How to resize label control

I'm trying to allow user to resize a label (the control, not the font) before printing it.
For this I'm using a LabelPrint class and I've set AutoSize property to false. I already made a code that would adjust font size to my label size. But I can't change the label size… Thanks for help, here's the code.
class LabelPrint : Label
{
public LabelPrint()
{
this.ResizeRedraw = true;
this.AutoSize = false;
this.TextAlign = ContentAlignment.MiddleCenter;
this.Font = new Font(this.Font.FontFamily, 12);
this.SizeChanged += new EventHandler(this.SizeLabelFont);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x84)
{ // Trap WM_NCHITTEST
var pos = this.PointToClient(new Point(m.LParam.ToInt32()));
if (pos.X >= this.Size.Width - grab && pos.Y >= this.Size.Height - grab)
m.Result = new IntPtr(17); // HT_BOTTOMRIGHT
}
}
private const int grab = 16;
public void SizeLabelFont(object sender, EventArgs e)
{
// Only bother if there's text.
string txt = this.Text;
if (txt.Length > 0)
{
int best_size = 100;
// See how much room we have, allowing a bit
// for the Label's internal margin.
int wid = this.DisplayRectangle.Width - 3;
int hgt = this.DisplayRectangle.Height - 3;
// Make a Graphics object to measure the text.
using (Graphics gr = this.CreateGraphics())
{
for (int i = 1; i <= 100; i++)
{
using (Font test_font =
new Font(this.Font.FontFamily, i))
{
// See how much space the text would
// need, specifying a maximum width.
SizeF text_size =
gr.MeasureString(txt, test_font);
if ((text_size.Width > wid) ||
(text_size.Height > hgt))
{
best_size = i - 1;
break;
}
}
}
}
// Use that font size.
this.Font = new Font(this.Font.FontFamily, best_size);
}
}
}
I tried to add this in LabelPrint class:
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
base.AutoSize = false;
}
But I still get the same problem: AutoSize is set to false so when i increase the fontsize, i can't see all my label.Text but I can't resize the label itself… I mean with the mouse and ResizeRedraw().

Label leave trail on font change in Winform

Video link: https://drive.google.com/open?id=1pErIL2TcE_wMNYaH0FlWxuijqSwbkYY3
There is something that I cannot translate the problem/bug as I don't know how to address this kind of problem. When the font family selection change, all the label inside the groupbox will be changed too. The code for it as per below:-
private void ScreenPage_FontFamilyCombobox_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if (ScreenPage_FontFamilyCombobox.Text != "")
{
ScreenPage_FontFamilyNotInstalled.Visible = false;
UpdateScreenSample();
}
}
catch (Exception ex)
{
UpdateEvents("Exception ScreenPage_FontFamilyCombobox_SelectedIndexChanged. " + ex.Message);
}
}
private void UpdateScreenSample()
{
try
{
//foreach (var TransLabels in ScreenPage_SampleGroupbox.Controls.OfType<GroupBox>().SelectMany(groupBox => groupBox.Controls.OfType<CodeMess.TransparentLabel>()))
//{
// float fntSize = TransLabels.Font.Size;
// FontStyle fntStyle = TransLabels.Font.Style;
// TransLabels.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, fntStyle);
//}
var TransLabels = ScreenPage_SampleGroupbox.Controls.OfType<CodeMess.TransparentLabel>();
foreach (CodeMess.TransparentLabel tL in TransLabels)
{
float fntSize = tL.Font.Size;
FontStyle fntStyle = tL.Font.Style;
tL.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, fntStyle);
}
//foreach (Control control in ScreenPage_SampleGroupbox.Controls)
//{
// if (control is ApplizoneConfiguration.CodeMess.TransparentLabel transLabel)
// {
// float fntSize = control.Font.Size;
// control.Font = new Font(ScreenPage_FontFamilyCombobox.Text, fntSize, FontStyle.Regular);
// }
//}
}
catch (Exception ex)
{
UpdateEvents("Exception UpdateScreenSample. " + ex.Message);
}
}
There is 3 code that I try to see if there any change but seems not. The font shadow trail will be disappear if I change to another tab and go back to the ScreenPage tab again.
The code for Transparent Label class is below which I use to transparent the background as it has dock fill image inside the groupbox on the bottom of all label:-
using System;
using System.Windows.Forms;
namespace ApplizoneConfiguration.CodeMess
{
public class TransparentLabel : Label
{
public TransparentLabel()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
}
There is a way to overcome this?
I have go through code by #Jimi answer linked from comment. And modified the code combined with solution from here and it a bit OK. Here is the full code with commenting
Sample Screen2Gif from solutions by Reza Aghaei here result with delay effect:-
Combine with solutions from Jimi + Reza result with these:-
Code:-
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Forms;
using System.Linq;
namespace Configuration.CodeMess
{
public class TransparentLabel : Label, INotifyPropertyChanged
{
internal int WS_EX_TRANSPARENT = 0x00000020;
//internal Font m_CustomFont = new Font("Segoe UI", 9, FontStyle.Regular, GraphicsUnit.Pixel);
//internal Color m_BackGroundColor;
//internal int m_InnerPadding = 20;
//internal int m_FontPadding = 5;
//internal int m_Opacity = 50;
public event PropertyChangedEventHandler PropertyChanged;
public TransparentLabel() => InitializeComponent();
public void InitializeComponent()
{
this.SetStyle(ControlStyles.Opaque |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.Refresh();
}
//protected override void OnPaint(PaintEventArgs e)
//{
// if (Parent != null)
// {
// using (var bmp = new Bitmap(Parent.Width, Parent.Height))
// {
// Parent.Controls.Cast<Control>()
// .Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
// .Where(c => c.Bounds.IntersectsWith(this.Bounds))
// .OrderByDescending(c => Parent.Controls.GetChildIndex(c))
// .ToList()
// .ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
// e.Graphics.DrawImage(bmp, -Left, -Top);
// using (var b = new SolidBrush(Color.FromArgb(this.Opacity, this.TransparentBackColor)))
// {
// e.Graphics.FillRectangle(b, this.ClientRectangle);
// }
// e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
// }
// }
//}
private int opacity;
public int Opacity
{
get { return opacity; }
set
{
if (value >= 0 && value <= 255)
opacity = value;
this.Invalidate();
}
}
public Color transparentBackColor;
public Color TransparentBackColor
{
get { return transparentBackColor; }
set
{
transparentBackColor = value;
this.Invalidate();
}
}
[Browsable(false)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
base.BackColor = Color.Transparent;
}
}
//public new Font Font
//{
// get => this.m_CustomFont;
// set
// {
// this.FontAdapter(value);
// NotifyPropertyChanged(nameof(this.Font));
// }
//}
//public int InnerPadding
//{
// get => this.m_InnerPadding;
// set
// {
// this.m_InnerPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
// NotifyPropertyChanged(nameof(this.InnerPadding));
// }
//}
//public int FontPadding
//{
// get => this.m_FontPadding;
// set
// {
// this.m_FontPadding = CheckValue(value, 0, this.ClientRectangle.Height - 10);
// NotifyPropertyChanged(nameof(this.FontPadding));
// }
//}
//public int Opacity
//{
// get => this.m_Opacity;
// set
// {
// this.m_Opacity = CheckValue(value, 0, 255);
// this.BackColor = Color.FromArgb(this.m_Opacity, this.BackColor);
// NotifyPropertyChanged(nameof(this.Opacity));
// }
//}
//public override Color BackColor
//{
// get => this.m_BackGroundColor;
// set
// {
// this.m_BackGroundColor = Color.FromArgb(this.m_Opacity, value);
// base.BackColor = this.m_BackGroundColor;
// NotifyPropertyChanged(nameof(this.BackColor));
// }
//}
//private void NotifyPropertyChanged(string PropertyName)
//{
// this.Refresh();
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
//}
//protected override void OnLayout(LayoutEventArgs evt)
//{
// base.OnLayout(evt);
// base.AutoSize = false;
// this.Opacity = this.m_Opacity;
//}
protected override void OnPaint(PaintEventArgs e)
{
StringFormat format = new StringFormat(StringFormatFlags.LineLimit, CultureInfo.CurrentUICulture.LCID)
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
TextRenderer.DrawText(e.Graphics, this.Text, this.Font, this.ClientRectangle, this.ForeColor, Color.Transparent);
//using (SolidBrush CircleBrush = new SolidBrush(this.BackColor))
//using (SolidBrush ForeBrush = new SolidBrush(this.ForeColor))
//{
// this.FontAdapter(this.m_CustomFont);
// RectangleF rect = InnerRectangle();
// e.Graphics.FillEllipse(CircleBrush, rect);
// e.Graphics.DrawString(this.Text, this.m_CustomFont, format);
//}
}
//private RectangleF InnerRectangle()
//{
// Tuple<decimal, decimal> refSize = GetMinMax(this.ClientRectangle.Height, this.ClientRectangle.Width);
// SizeF size = new SizeF((float)refSize.Item1 - (this.m_InnerPadding / 2),
// (float)refSize.Item1 - (this.m_InnerPadding / 2));
// PointF position = new PointF((this.ClientRectangle.Width - size.Width) / 2,
// (this.ClientRectangle.Height - size.Height) / 2);
// return new RectangleF(position, size);
//}
//private void FontAdapter(Font font)
//{
// RectangleF rect = InnerRectangle();
// float FontSize = (CheckValue((int)(rect.Height - this.m_FontPadding), 6,
// (int)(rect.Height - this.m_FontPadding)) / 1.4F);
// using (Font customfont = new Font(font.FontFamily, FontSize, font.Style, GraphicsUnit.Pixel))
// this.m_CustomFont = (Font)customfont.Clone();
//}
//private int CheckValue(int Value, int Min, int Max)
//{
// return (Value < Min) ? Min : ((Value > Max) ? Max : Value);
//}
//private Tuple<decimal, decimal> GetMinMax(ValueType Value1, ValueType Value2)
//{
// if ((Value1 is Enum) || (Value1.GetType().IsNested)) return null;
// if ((Value2 is Enum) || (Value2.GetType().IsNested)) return null;
// return new Tuple<decimal, decimal>(Math.Min(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)),
// Math.Max(Convert.ToDecimal(Value1), Convert.ToDecimal(Value2)));
//}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
}
Open to any amendment for improvement.

Missing something in Behavior of ListView in Xamarin Forms

I don't know what I am missing in 4th line. I have ListView in page and I want to set behavior whenever size change.
public class ListViewAutoSizeBehavior : Behavior<ListView>
{
ListView _ListView;
//Error here. ITemplatedItemsView<TItem>
ITemplatedItemsView Cells => _ListView;
private readonly int _extraPaddingPerRow;
public ListViewAutoSizeBehavior()
{
switch (Device.RuntimePlatform)
{
default:
_extraPaddingPerRow = 2;
break;
case Device.Android:
_extraPaddingPerRow = 6;
break;
case Device.iOS:
_extraPaddingPerRow = 4;
break;
case Device.WinPhone:
_extraPaddingPerRow = 2;
break;
}
}
protected override void OnAttachedTo(ListView bindable)
{
bindable.ItemAppearing += AppearanceChanged;
bindable.ItemDisappearing += AppearanceChanged;
_ListView = bindable;
}
protected override void OnDetachingFrom(ListView bindable)
{
bindable.ItemAppearing -= AppearanceChanged;
bindable.ItemDisappearing -= AppearanceChanged;
_ListView = null;
}
private void AppearanceChanged(object sender, ItemVisibilityEventArgs e)
{
UpdateHeight(e.Item);
}
private void UpdateHeight(object item)
{
if (_ListView == null || _ListView.ItemsSource == null) return;
int itemsCount = _ListView.ItemsSource.Cast<object>().Count();
if (_ListView.HasUnevenRows)
{
double height;
if ((height = _ListView.HeightRequest) == (double)VisualElement.HeightRequestProperty.DefaultValue)
{
height = 0;
}
height += MeasureRowHeight(item);
SetHeight((height + _extraPaddingPerRow) * itemsCount + _extraPaddingPerRow);
}
else if (_ListView.RowHeight == (int)ListView.RowHeightProperty.DefaultValue)
{
var height = MeasureRowHeight(item);
_ListView.RowHeight = (height + _extraPaddingPerRow);
SetHeight((height + _extraPaddingPerRow) * itemsCount + _extraPaddingPerRow);
}
}
private int MeasureRowHeight(object item)
{
var template = _ListView.ItemTemplate;
var cell = (Cell)template.CreateContent();
cell.BindingContext = item;
var height = cell.RenderHeight;
var mod = height % 1;
if (mod > 0)
{
height = height - mod + 1;
}
return (int)height;
}
private void SetHeight(double height)
{
//TODO if header or footer is string etc.
if (_ListView.Header is VisualElement header)
{
height += header.Height;
}
if (_ListView.Footer is VisualElement footer)
{
height += footer.Height;
}
_ListView.HeightRequest = height;
}
}
Can anybody please suggest me what I am missing here? It looks like I need to set some class name? or name of some control.
Please suggest.
Your problem is most likely due to naming conventions.
Listview is a reserved keyword, and _Listview must be confusing the compiler.
Try to rename that variable, and refactor your code, so there won't be any issue.
ListView _myListView;
ITemplatedItemsView Cells => _myListView;
You didn't put any error, so I'm assuming this is the main cause. If not, please submit the error message for further analysis.

AppBar Multi-Monitor

I've made a simple appBar with just a label on the top of the screen that shrinks the desktop but I'm having trouble making it appear my second monitor. I've been searching around but everything I've found has been for WPF. These are most likely the areas where I've made a mistake but if there is any other code you need to see, just let me know.
private void InitializeComponent()
{
this.ClientSize = new System.Drawing.Size(SystemInformation.WorkingArea.Width, -1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "MainForm";
this.Text = "AppBar";
this.Closing += new System.ComponentModel.CancelEventHandler(this.OnClosing);
this.Load += new System.EventHandler(this.OnLoad);
this.BackColor = Color.Green;
this.Padding = new Padding(0, 0, 0, 0);
Label label1 = new Label();
label1.Text = "TEXT";
label1.Width = 270;
label1.Margin = new Padding(0,0,0,0);
label1.Padding = new Padding(0,0,0,0);
label1.TextAlign = ContentAlignment.MiddleCenter;
label1.ForeColor = Color.White;
label1.Font = new Font(FontFamily.GenericSansSerif, 12,FontStyle.Regular);
label1.Location = new Point((SystemInformation.WorkingArea.Width - 270) / 2, 0);
this.Controls.Add(label1);
}
private void ABSetPos()
{
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = this.Handle;
abd.uEdge = (int)ABEdge.ABE_TOP;
if (abd.uEdge == (int)ABEdge.ABE_LEFT || abd.uEdge == (int)ABEdge.ABE_RIGHT)
{
abd.rc.top = 0;
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height;
if (abd.uEdge == (int)ABEdge.ABE_LEFT)
{
abd.rc.left = 0;
abd.rc.right = Size.Width;
}
else
{
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width;
abd.rc.left = abd.rc.right - Size.Width;
}
}
else
{
abd.rc.left = 0;
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width;
if (abd.uEdge == (int)ABEdge.ABE_TOP)
{
abd.rc.top = 0;
abd.rc.bottom = Size.Height;
}
else
{
abd.rc.bottom = SystemInformation.PrimaryMonitorSize.Height;
abd.rc.top = abd.rc.bottom - Size.Height;
}
}
You can use a different screen by iterating over the Screen.AllScreens array. For example, here is how you would get the first non-primary monitor:
Screen nonPrimaryScreen = Screen.AllScreens.FirstOrDefault(x => !x.Primary);
Then everywhere you are using SystemInformation.WorkingArea (which always uses the primary screen), you can use:
nonPrimaryScreen.WorkingArea
Assuming nonPrimaryScreen != null ... of course.
EDIT:
Instead of duplicating code, make it all more generic:
public static Rectangle GetWorkingArea() {
if (UseWantsItOnPrimaryScreen) {
return SystemInformation.WorkingArea;
}
else {
return Screen.AllScreens.FirstOrDefault(x => !x.Primary).WorkingArea;
}
}
private Screen GetScreenObject(String Name)
{
logger.Info(GlobalModulename + "# ScreenList::looking for screen:"+Name);
if ((Name == "Primary"))
{
bool ExpectedParameter = true;
foreach (var screen in Screen.AllScreens)
{
// For each screen, add the screen properties to a list box.
logger.Info(GlobalModulename + "# ScreenList::("+screen.DeviceName.ToString()+")Primary Screen: " + screen.Primary.ToString());
if (screen.Primary==ExpectedParameter)
{
return screen;
}
}
}
if ((Name == "Secondary"))
{
bool ExpectedParameter = false;
foreach (var screen in Screen.AllScreens)
{
// For each screen, add the screen properties to a list box.
logger.Info(GlobalModulename + "# ScreenList::(" + screen.DeviceName.ToString() + ")Primary Screen: " + screen.Primary.ToString());
if (screen.Primary == ExpectedParameter)
{
return screen;
}
}
}
// konkretni jmeno obrazovky tak jak je to v systemu
try
{
foreach (var screen in Screen.AllScreens)
{
// For each screen, add the screen properties to a list box.
logger.Info("UEFA_Core # ScreenList::Device Name: " + screen.DeviceName);
logger.Info("UEFA_Core # ScreenList::Bounds: " + screen.Bounds.ToString());
logger.Info("UEFA_Core # ScreenList::Type: " + screen.GetType().ToString());
logger.Info("UEFA_Core # ScreenList::Working Area: " + screen.WorkingArea.ToString());
logger.Info("UEFA_Core # ScreenList::Primary Screen: " + screen.Primary.ToString());
if (screen.DeviceName == Name) return screen;
}
}
catch { }
// podobne jmeno obrazovky tak jak je to v systemu
try
{
foreach (var screen in Screen.AllScreens)
{
// For each screen, add the screen properties to a list box.
logger.Info("UEFA_Core # ScreenList::Device Name: " + screen.DeviceName);
logger.Info("UEFA_Core # ScreenList::Bounds: " + screen.Bounds.ToString());
logger.Info("UEFA_Core # ScreenList::Type: " + screen.GetType().ToString());
logger.Info("UEFA_Core # ScreenList::Working Area: " + screen.WorkingArea.ToString());
logger.Info("UEFA_Core # ScreenList::Primary Screen: " + screen.Primary.ToString());
if (screen.DeviceName.Contains(Name)) return screen;
}
}
catch { }
logger.Info("UEFA_Core # ScreenList::No screen found by name");
return Screen.PrimaryScreen;
}
#region APPBAR
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE = 1,
ABM_QUERYPOS = 2,
ABM_SETPOS = 3,
ABM_GETSTATE = 4,
ABM_GETTASKBARPOS = 5,
ABM_ACTIVATE = 6,
ABM_GETAUTOHIDEBAR = 7,
ABM_SETAUTOHIDEBAR = 8,
ABM_WINDOWPOSCHANGED = 9,
ABM_SETSTATE = 10
}
enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
enum ABEdge : int
{
ABE_LEFT = 0,
ABE_TOP,
ABE_RIGHT,
ABE_BOTTOM
}
private bool fBarRegistered = false;
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("USER32")]
static extern int GetSystemMetrics(int Index);
[DllImport("User32.dll", ExactSpelling = true,
CharSet = System.Runtime.InteropServices.CharSet.Auto)]
private static extern bool MoveWindow
(IntPtr hWnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private int uCallBack;
private void RegisterBar()
{
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = this.Handle;
if (!fBarRegistered)
{
uCallBack = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = uCallBack;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
fBarRegistered = true;
ABSetPos();
}
else
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
fBarRegistered = false;
}
}
private void ABSetPos()
{
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = this.Handle;
abd.uEdge = (int)ABEdge.ABE_TOP;
if (abd.uEdge == (int)ABEdge.ABE_LEFT || abd.uEdge == (int)ABEdge.ABE_RIGHT)
{
abd.rc.top = this.GetScreenObject(ScreenName).Bounds.Top; //0;
abd.rc.bottom = this.GetScreenObject(ScreenName).Bounds.Top + this.GetScreenObject(ScreenName).Bounds.Height; //SystemInformation.PrimaryMonitorSize.Height;
if (abd.uEdge == (int)ABEdge.ABE_LEFT)
{
abd.rc.left = this.GetScreenObject(ScreenName).Bounds.Left;//0;
abd.rc.right = Size.Width;
}
else
{
abd.rc.right = this.GetScreenObject(ScreenName).Bounds.Left + this.GetScreenObject(ScreenName).Bounds.Width; // SystemInformation.PrimaryMonitorSize.Width;
abd.rc.left = abd.rc.right - Size.Width;
}
}
else
{
abd.rc.left = this.GetScreenObject(ScreenName).Bounds.Left; //0;
abd.rc.right = this.GetScreenObject(ScreenName).Bounds.Left+this.GetScreenObject(ScreenName).Bounds.Width; //SystemInformation.PrimaryMonitorSize.Width;
if (abd.uEdge == (int)ABEdge.ABE_TOP)
{
abd.rc.top = this.GetScreenObject(ScreenName).Bounds.Top; //0 nebo -1080
abd.rc.bottom = Size.Height;
}
else
{
abd.rc.bottom = this.GetScreenObject(ScreenName).Bounds.Top + this.GetScreenObject(ScreenName).Bounds.Height; //SystemInformation.PrimaryMonitorSize.Height;
abd.rc.top = abd.rc.bottom - Size.Height;
}
}
// Query the system for an approved size and position.
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref abd);
// Adjust the rectangle, depending on the edge to which the
// appbar is anchored.
switch (abd.uEdge)
{
case (int)ABEdge.ABE_LEFT:
abd.rc.right = abd.rc.left + Size.Width;
break;
case (int)ABEdge.ABE_RIGHT:
abd.rc.left = abd.rc.right - Size.Width;
break;
case (int)ABEdge.ABE_TOP:
abd.rc.bottom = abd.rc.top + Size.Height;
break;
case (int)ABEdge.ABE_BOTTOM:
abd.rc.top = abd.rc.bottom - Size.Height;
break;
}
// Pass the final bounding rectangle to the system.
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref abd);
// Move and size the appbar so that it conforms to the
// bounding rectangle passed to the system.
MoveWindow(abd.hWnd, abd.rc.left, abd.rc.top,
abd.rc.right - abd.rc.left, abd.rc.bottom - abd.rc.top, true);
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == uCallBack)
{
switch (m.WParam.ToInt32())
{
case (int)ABNotify.ABN_POSCHANGED:
ABSetPos();
break;
}
}
try
{
base.WndProc(ref m);
}
catch (Exception E) { }
}
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style &= (~0x00C00000); // WS_CAPTION
cp.Style &= (~0x00800000); // WS_BORDER
//cp.ExStyle = 0x00000080 | 0x00000008 | 0x20; // WS_EX_TOOLWINDOW | WS_EX_TOPMOST
//cp.ExStyle &= 0x20;
cp.ExStyle |= 0x00000008 | 0x00000080;
//cp.ExStyle &= 0x00000080 ; // WS_EX_TOOLWINDOW | WS_EX_TOPMOST
return cp;
}
}
private void OnLoad(object sender, System.EventArgs e)
{
RegisterBar();
}
private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
RegisterBar();
}
#endregion
You can compute correct formula for moving your AppBar to second monitor without using anything other than PrimaryMonitorSize. For example for left side AppBar on second monitor you can use this:
if (abd.uEdge == (int)ABEdge.ABE_LEFT)
{
abd.rc.left = SystemInformation.PrimaryMonitorSize.Width;
abd.rc.right = SystemInformation.PrimaryMonitorSize.Width + Size.Width;
}

Categories