Fatal Signal 11 error when drawing on canvas - c#

I am running into an issue where swiping cells in my recycler view results in the application throwing a signal 11 error.
After looking into this issue on google/stackoverflow i found out that some people seemed to be able to fix it by setting the layer of a view to software rendering rather than hardware accelerated rendering - unfortunately in my case that did not fix the issue for me, nor was i able to find any suggestions which were really different from fixing it like that.
I attempted to do fix this by setting the "container" with that method as well as the ItemView of the viewholder - neither one prevented the error.
Is someone able to tell me what's so special about my scenario that i can't get it to work?
The error happens with line 242 of LeftRightSwipeController.cs
The full reproduction sample is available on github at:
https://github.com/devsolvum/Signal-11-CanvasDraw
LeftRightSwipeController.cs
using System;
using System.Collections.Generic;
using System.Threading;
using Android.Graphics;
using Android.Support.V7.Widget;
using Android.Support.V7.Widget.Helper;
using Android.Views;
using Math = Java.Lang.Math;
using Object = Java.Lang.Object;
namespace App1.Domain
{
// https://medium.com/#fanfatal/android-swipe-menu-with-recyclerview-8f28a235ff28
// https://github.com/FanFataL/swipe-controller-demo/blob/master/app/src/main/java/pl/fanfatal/swipecontrollerdemo/SwipeController.java
public enum SwipeState
{
Default,
LeftOpen,
RightOpen
}
public class LeftRightSwipeController : ItemTouchHelper.Callback
{
private bool _swipeBack = false;
private SwipeState _currentSwipeState = SwipeState.Default;
private RectF _buttonInstance = null;
public override int ConvertToAbsoluteDirection(int flags, int layoutDirection)
{
if (_swipeBack)
{
_swipeBack = _currentSwipeState != SwipeState.Default;
return 0;
}
return base.ConvertToAbsoluteDirection(flags, layoutDirection);
}
public override void OnChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, bool isCurrentlyActive)
{
var measuredsize = GetMeasuredSize(GetContainerArea(dX), viewHolder);
if (measuredsize.width <= 0 || measuredsize.height <= 0)
return;
var clippedX = dX >= 0 ? Math.Min(dX, measuredsize.width) : Math.Max(dX, -measuredsize.width);
if (actionState == ItemTouchHelper.ActionStateSwipe)
{
if (_currentSwipeState != SwipeState.Default)
{
if (_currentSwipeState == SwipeState.LeftOpen)
clippedX = Math.Max(clippedX, measuredsize.width);
if (_currentSwipeState == SwipeState.RightOpen)
clippedX = Math.Min(clippedX, -measuredsize.width);
base.OnChildDraw(c, recyclerView, viewHolder, clippedX, dY, actionState, isCurrentlyActive);
}
else
{
SetTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
if (_currentSwipeState == SwipeState.Default)
{
base.OnChildDraw(c, recyclerView, viewHolder, clippedX, dY, actionState, isCurrentlyActive);
}
DrawButtons(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive, _currentSwipeState, false);
}
private (int width, int height) GetMeasuredSize(int containerArea, RecyclerView.ViewHolder viewHolder)
{
if (viewHolder is IViewHolderButtonContainer buttonContainer)
{
int widthSpec = View.MeasureSpec.MakeMeasureSpec(viewHolder.ItemView.Width, MeasureSpecMode.AtMost);
int heightSpec = View.MeasureSpec.MakeMeasureSpec(viewHolder.ItemView.Height, MeasureSpecMode.AtMost);
var container = buttonContainer.CreateButtonContainer(containerArea);
container.Measure(widthSpec, heightSpec);
return (container.MeasuredWidth, container.MeasuredHeight);
}
return (0, 0);
}
private void SetItemsClickable(RecyclerView recyclerView, bool isClickable)
{
for (int i = 0; i < recyclerView.ChildCount; ++i)
{
recyclerView.GetChildAt(i).Clickable = isClickable;
}
}
private void SetTouchListener(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, bool isCurrentlyActive)
{
recyclerView.SetOnTouchListener(
new TouchListenerDelegate(
(v, #event) =>
{
_swipeBack = #event.Action == MotionEventActions.Cancel || #event.Action == MotionEventActions.Up;
if (_swipeBack)
{
float containerWidth = GetMeasuredSize(GetContainerArea(dX), viewHolder).width;
if (dX <= -containerWidth)
{
_currentSwipeState = SwipeState.RightOpen;
}
else if (dX >= containerWidth)
{
_currentSwipeState = SwipeState.LeftOpen;
}
if (_currentSwipeState != SwipeState.Default)
{
DrawButtons(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive, _currentSwipeState, true);
SetTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
SetItemsClickable(recyclerView, false);
}
}
return false;
}));
}
private void SetTouchDownListener(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, bool isCurrentlyActive)
{
recyclerView.SetOnTouchListener(
new TouchListenerDelegate(
(view, args) =>
{
if (args.Action == MotionEventActions.Down)
{
SetTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
return false;
}));
}
private void SetTouchUpListener(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, bool isCurrentlyActive)
{
recyclerView.SetOnTouchListener(
new TouchListenerDelegate(
(recyclerViewInDelegate, outerArgs) =>
{
if (outerArgs.Action == MotionEventActions.Up)
{
recyclerView.SetOnTouchListener(
new TouchListenerDelegate(
(innerView, innerArgs) => { return false; }));
SetItemsClickable(recyclerView, true);
_swipeBack = false;
// if (_buttonsActions != null && _buttonInstance != null &&
// _buttonInstance.Contains(outerArgs.GetX(), outerArgs.GetY()))
// {
// if (_currentSwipeState == SwipeState.LeftOpen)
// {
// _buttonsActions.OnLeftClicked(viewHolder.AdapterPosition);
// }
// else if (_currentSwipeState == SwipeState.RightOpen)
// {
// _buttonsActions.OnRightClicked(viewHolder.AdapterPosition);
// }
// }
_currentSwipeState = SwipeState.Default;
// folgende codezeile behebt einen darstellungsbug, wenn man eine schaltfläche klickt
Thread.Sleep(100);
base.OnChildDraw(c, recyclerView, viewHolder, 0, dY, actionState, isCurrentlyActive);
}
return false;
}));
}
private void DrawButtons(Canvas canvas, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
float dY, int actionState, bool isCurrentlyActive, SwipeState swipeState, bool swipeEnd)
{
var validState = isCurrentlyActive || swipeState != SwipeState.Default;
if (!validState)
return;
if (viewHolder is IViewHolderButtonContainer buttonContainer)
{
// int widthSpec = View.MeasureSpec.MakeMeasureSpec(ViewGroup.LayoutParams.WrapContent, MeasureSpecMode.Unspecified);
int widthSpec = View.MeasureSpec.MakeMeasureSpec(viewHolder.ItemView.Width, MeasureSpecMode.AtMost);
int heightSpec = View.MeasureSpec.MakeMeasureSpec(viewHolder.ItemView.Height, MeasureSpecMode.AtMost);
var containerArea = GetContainerArea(dX);
var container = buttonContainer.CreateButtonContainer(containerArea);
container.Measure(widthSpec, heightSpec);
var clippedWidth = GetClippedWidth(viewHolder.ItemView.Width, dX, container.MeasuredWidth);
if (clippedWidth <= 0)
return;
var clippedHeight = GetClippedHeight(viewHolder.ItemView.Height, dY, container.MeasuredHeight);
if (clippedHeight <= 0)
return;
var viewLeft = dX >= 0 ? viewHolder.ItemView.Left : viewHolder.ItemView.Right - clippedWidth;
var viewTop = viewHolder.ItemView.Top;
var viewRight = viewLeft + clippedWidth;
var viewBottom = viewTop + clippedHeight;
container.Layout(viewLeft, viewTop, viewRight, viewBottom);
container.SetBackgroundColor(Color.Orange);
// var canvasX = dX >= 0 ? viewHolder.ItemView.Left : viewHolder.ItemView.Left + viewHolder.ItemView.Width;
// var paint = new Paint();
// paint.Color = Color.Aqua;
// canvas.DrawRect(viewLeft, viewTop, viewRight, viewBottom, paint);
// Translate the canvas so the view is drawn at the proper coordinates
canvas.Save();
canvas.Translate(viewLeft, viewHolder.ItemView.Top);
if (swipeEnd)
{
viewHolder.ItemView.Invalidate();
container.Invalidate();
}
//Draw the View and clear the translation
container.Draw(canvas);
canvas.Restore();
}
}
private static int GetContainerArea(float dX)
{
return dX >= 0 ? 0 : 1;
}
private int GetClippedHeight(int viewHolderHeight, float dY, int measured)
{
return viewHolderHeight;
}
private int GetClippedWidth(int viewHolderWidth, float dX, int measured)
{
if (viewHolderWidth <= 0)
return 0;
return Math.Min(measured, (int) Math.Ceil(Math.Abs(dX)));
}
public override int GetMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
{
return MakeMovementFlags(0, ItemTouchHelper.Left | ItemTouchHelper.Right);
}
public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target)
{
return false;
}
public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction)
{
}
private class TouchListenerDelegate : Object, View.IOnTouchListener
{
public void Dispose()
{
Callback = null;
}
public TouchListenerDelegate(Func<View, MotionEvent, bool> callback)
{
Callback = callback;
}
public Func<View, MotionEvent, bool> Callback { get; set; }
public bool OnTouch(View v, MotionEvent e)
{
return Callback(v, e);
}
}
}
public interface IViewHolderButtonContainer
{
/// <summary>
/// Erstellt container für die Buttons
/// </summary>
/// <param name="containerArea">0 = links, 1 = rechts</param>
/// <returns></returns>
View CreateButtonContainer(int containerArea);
}
}
MainActivity.cs
using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Widget;
using Android.OS;
using Android.Runtime;
using Android.Support.V7.Widget;
using Android.Support.V7.Widget.Helper;
using Android.Views;
using App1.Domain;
namespace App1
{
[Activity(Label = "App1", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
var mainView = LayoutInflater.Inflate(Resource.Layout.Main, null, true) as LinearLayout;
SetContentView(mainView);
mainView.SetBackgroundColor(Color.Red);
var recyclerView = new RecyclerView(this);
var recyclerAdapter = new RecyclerAdapter();
for (int i = 0; i < 30; i++)
{
recyclerAdapter.Items.Add($"Entry {i}.");
}
recyclerView.SetAdapter(recyclerAdapter);
var linearLayoutManager = new LinearLayoutManager(this);
var swipeController = new LeftRightSwipeController();
var ith = new ItemTouchHelper(swipeController);
ith.AttachToRecyclerView(recyclerView);
recyclerView.SetLayoutManager(linearLayoutManager);
recyclerView.SetBackgroundColor(Color.Orange);
mainView.AddView(recyclerView);
}
public class RecyclerAdapter : RecyclerView.Adapter
{
public List<string> Items { get; set; } = new List<string>();
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
var data = Items[position];
var textView = holder.ItemView.FindViewById<TextView>(Android.Resource.Id.Text1);
if (textView != null)
{
textView.Text = data;
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var inflater = Android.Views.LayoutInflater.FromContext(parent.Context);
var view = inflater.Inflate(Android.Resource.Layout.SimpleListItem1, parent, false);
return new CustomViewHolder(view);
}
public override int ItemCount => Items.Count;
}
public class CustomViewHolder : RecyclerView.ViewHolder, IViewHolderButtonContainer
{
public CustomViewHolder(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public CustomViewHolder(View itemView) : base(itemView)
{
}
private View _leftContainer;
private View _rightContainer;
public View CreateButtonContainer(int containerArea)
{
if (containerArea == 0)
{
return _leftContainer ?? (_leftContainer = CreateContainer(containerArea));
}
else
{
return _rightContainer ?? (_rightContainer = CreateContainer(containerArea));
}
}
private string GetButtonText(int containerArea)
{
return $"Button area {containerArea}";
}
private View CreateContainer(int containerArea)
{
var textView = new TextView(Android.App.Application.Context);
textView.SetBackgroundColor(Color.Blue);
textView.Text = GetButtonText(containerArea);
return textView;
}
}
}
}
Any suggestions to try and fix this are very welcome.

Setting the layer type on the recyclerview to software fixed the issue. Turns out i picked the wrong view to set it.

Related

WindowsFormsHost control is being overclipped when embedded inside a WPF ScrollViewer

I have a WindowsFormsHost nested inside a WPF ScrollViewer, for some reason, after applying clipping it seems that the WIndowsFormsHost is not filling up all of the available spaces, ie: the control has been overclipped.
Here's how it looks like-- note that there are a lot of white space, which really should be filled with blue color.
Here's my code in totality:
public class DummyWinformControl : WindowsFormsHostEx /* WindowsFormsHost */
{
public DummyWinformControl()
{
var panel = new System.Windows.Forms.Panel();
panel.Dock = DockStyle.Fill;
panel.BackColor = System.Drawing.Color.Blue;
Child = panel;
}
}
/// <summary>
/// https://stackoverflow.com/a/18084481
/// </summary>
public class WindowsFormsHostEx : WindowsFormsHost
{
private PresentationSource _presentationSource;
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
if (ParentScrollViewer == null)
return;
GeneralTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer);
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var intersect = Rect.Intersect(scrollRect, tr.TransformBounds(rcBoundingBox));
if (!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this);
intersect = tr.TransformBounds(intersect);
}
else
intersect = new Rect();
int x1 = (int)Math.Round(intersect.Left);
int y1 = (int)Math.Round(intersect.Top);
int x2 = (int)Math.Round(intersect.Right);
int y2 = (int)Math.Round(intersect.Bottom);
SetRegion(x1, y1, x2, y2);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
}
private void SourceChangedEventHandler(Object sender, SourceChangedEventArgs e)
{
ParentScrollViewer = FindParentScrollViewer();
}
private ScrollViewer FindParentScrollViewer()
{
DependencyObject vParent = this;
ScrollViewer parentScroll = null;
while (vParent != null)
{
parentScroll = vParent as ScrollViewer;
if (parentScroll != null)
break;
vParent = LogicalTreeHelper.GetParent(vParent);
}
return parentScroll;
}
private void SetRegion(int x1, int y1, int x2, int y2)
{
SetWindowRgn(Handle, CreateRectRgn(x1, y1, x2, y2), true);
}
private Visual RootVisual
{
get
{
if (_presentationSource == null)
_presentationSource = PresentationSource.FromVisual(this);
return _presentationSource.RootVisual;
}
}
private ScrollViewer ParentScrollViewer { get; set; }
[DllImport("User32.dll", SetLastError = true)]
static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
}
And here's the MainWindow.XAML:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid ScrollViewer.CanContentScroll="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox Header="abc" Grid.Row="0" BorderThickness="1" Width="400" Height="600">
<local:DummyWinformControl />
</GroupBox>
<Label Content="Hello world" Grid.Row="1"/>
</Grid>
</ScrollViewer>
Note that in my code, I am inheriting from WindowsFormsHostEx and not WindowsFormsHost, because doing so will apply clipping on the Winformcontrols when I am resizing the Windows, so that the label content will always remain visible.
If I use WindowsFormsHost then all the spaces will be filled up, but the label content below will be overlaid. Also not what I want.
The code for WindowsFormsHostEx is obtained from here.
I'm not too sure what I do wrong with the above code; how can I fix it?
I found the solution-- the idea is that you need to scale properly for DPI, as per below.
#region Using Declarations
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Media;
#endregion
public class WindowsFormsHostEx : WindowsFormsHost
{
#region DllImports
[DllImport("User32.dll", SetLastError = true)]
static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
#endregion
#region Events
public event EventHandler LocationChanged;
#endregion
#region Members
private PresentationSource _presentationSource;
#endregion
#region Properties
private ScrollViewer ParentScrollViewer { get; set; }
private bool Scrolling { get; set; }
public bool Resizing { get; set; }
private Visual RootVisual
{
get
{
_presentationSource = PresentationSource.FromVisual(this);
return _presentationSource.RootVisual;
}
}
#endregion
#region Constructors
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
#endregion
#region Methods
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
DpiScale dpiScale = VisualTreeHelper.GetDpi(this);
base.OnWindowPositionChanged(rcBoundingBox);
Rect newRect = ScaleRectDownFromDPI(rcBoundingBox, dpiScale);
Rect finalRect;
if (ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged += ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged += ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded += ParentScrollViewer_Loaded;
}
if (Scrolling || Resizing)
{
if (ParentScrollViewer == null)
return;
MatrixTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer) as MatrixTransform;
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var c = tr.TransformBounds(newRect);
var intersect = Rect.Intersect(scrollRect, c);
if (!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this) as MatrixTransform;
intersect = tr.TransformBounds(intersect);
finalRect = ScaleRectUpToDPI(intersect, dpiScale);
}
else
finalRect = intersect = new Rect();
int x1 = (int)Math.Round(finalRect.X);
int y1 = (int)Math.Round(finalRect.Y);
int x2 = (int)Math.Round(finalRect.Right);
int y2 = (int)Math.Round(finalRect.Bottom);
SetRegion(x1, y1, x2, y2);
this.Scrolling = false;
this.Resizing = false;
}
LocationChanged?.Invoke(this, new EventArgs());
}
private void ParentScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
this.Resizing = true;
}
private void ParentScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.Resizing = true;
}
private void ParentScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.VerticalChange != 0 || e.HorizontalChange != 0 || e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
Scrolling = true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
_presentationSource = null;
}
}
private void SourceChangedEventHandler(Object sender, SourceChangedEventArgs e)
{
if (ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged -= ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged -= ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded -= ParentScrollViewer_Loaded;
}
ParentScrollViewer = FindParentScrollViewer();
}
private ScrollViewer FindParentScrollViewer()
{
DependencyObject vParent = this;
ScrollViewer parentScroll = null;
while (vParent != null)
{
parentScroll = vParent as ScrollViewer;
if (parentScroll != null)
break;
vParent = LogicalTreeHelper.GetParent(vParent);
}
return parentScroll;
}
private void SetRegion(int x1, int y1, int x2, int y2)
{
SetWindowRgn(Handle, CreateRectRgn(x1, y1, x2, y2), true);
}
public static Rect ScaleRectDownFromDPI(Rect _sourceRect, DpiScale dpiScale)
{
double dpiX = dpiScale.DpiScaleX;
double dpiY = dpiScale.DpiScaleY;
return new Rect(new Point(_sourceRect.X / dpiX, _sourceRect.Y / dpiY), new System.Windows.Size(_sourceRect.Width / dpiX, _sourceRect.Height / dpiY));
}
public static Rect ScaleRectUpToDPI(Rect _toScaleUp, DpiScale dpiScale)
{
double dpiX = dpiScale.DpiScaleX;
double dpiY = dpiScale.DpiScaleY;
return new Rect(new Point(_toScaleUp.X * dpiX, _toScaleUp.Y * dpiY), new System.Windows.Size(_toScaleUp.Width * dpiX, _toScaleUp.Height * dpiY));
}
#endregion
}
The above code requires .Net framework 4.7 to compile. It is an answer that I found at the same question.

C# drag mouse down

I'm trying to make a program where when I click left click it drags the mouse down without me moving my mouse down for a game but it does not move the game at all. It moves my cursor outside of the game but does not move inside the game.
public static Point Position { get; set; }
public Anti_Recoil()
{
InitializeComponent();
}
private void Anti_Recoil_Load(object sender, EventArgs e)
{
this.BackColor = Color.Wheat;
this.TransparencyKey = Color.Wheat;
this.TopMost = true;
int initialStyle = GetWindowLong(this.Handle, -20);
SetWindowLong(this.Handle, -20, initialStyle | 0x80000 | 0x20);
PointConverter pc = new PointConverter();
Point pt = new Point();
pt = (Point)pc.ConvertFromString("765, 500");
Cursor.Position = pt;
}
private void Anti_Recoil_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Z)
Hide();
}
Use my class
First, add my class to your project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace Hector.Framework.Utils
{
public class Peripherals
{
public class Mouse
{
public static int X
{
get => Cursor.Position.X;
}
public static int Y
{
get => Cursor.Position.Y;
}
public static void MoveToPoint(int X, int Y)
{
Win32.SetCursorPos(X, Y);
}
public static void Hide()
{
Cursor.Hide();
}
public static void Show()
{
Cursor.Show();
}
public static bool IsHidden
{
get => Cursor.Current == null;
}
/// <summary>
/// ButtonNumber: 0-None 1-Left 2-Middle 3-Right 4-XButton1 5-XButton2
/// </summary>
public static bool MouseButtonIsDown(int ButtonNumber)
{
bool outValue = false;
bool isLeft = Control.MouseButtons == MouseButtons.Left;
bool isRight = Control.MouseButtons == MouseButtons.Right;
bool isMiddle = Control.MouseButtons == MouseButtons.Middle;
bool isXButton1 = Control.MouseButtons == MouseButtons.XButton1;
bool isXButton2 = Control.MouseButtons == MouseButtons.XButton2;
switch (ButtonNumber)
{
case 0:
outValue = false;
break;
case 1:
outValue = isLeft;
break;
case 2:
outValue = isMiddle;
break;
case 3:
outValue = isRight;
break;
case 4:
outValue = isXButton1;
break;
case 5:
outValue = isXButton2;
break;
}
return outValue;
}
/// <summary>
/// This function is in Alpha Phase
/// </summary>
/// <param name="FocusedControl">The control that is scrolled; If the control has no focus, it will be applied automatically</param>
/// <param name="FontSize">This is used to calculate the number of pixels to move, its default value is 20</param>
static int delta = 0;
static int numberOfTextLinesToMove = 0;
static int numberOfPixelsToMove = 0;
public static bool GetWheelValues(Control FocusedControl, out int Delta, out int NumberOfTextLinesToMove, out int NumberOfPixelsToMove, int FontSize = 20)
{
try
{
if (FocusedControl == null) throw new NullReferenceException("The FocusedControl can not bel null");
if (!FocusedControl.Focused) FocusedControl.Focus();
FocusedControl.MouseWheel += (object sender, MouseEventArgs e) =>
{
delta = e.Delta;
numberOfTextLinesToMove = e.Delta * SystemInformation.MouseWheelScrollLines / 120;
numberOfPixelsToMove = numberOfTextLinesToMove * FontSize;
};
Delta = delta;
NumberOfTextLinesToMove = numberOfTextLinesToMove;
NumberOfPixelsToMove = numberOfPixelsToMove;
return true;
}
catch
{
Delta = 0;
NumberOfTextLinesToMove = 0;
NumberOfPixelsToMove = numberOfPixelsToMove;
return false;
}
}
private class Win32
{
[DllImport("User32.Dll")]
public static extern long SetCursorPos(int x, int y);
[DllImport("User32.Dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref POINT point);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
}
}
}
}
Then where you need to check the mouse's status,
bool MouseLeftButton = Hector.Framework.Utils.Peripherals.Mouse.MouseButtonIsDown(1);
And try this:
private void Form2_Load(object sender, EventArgs e)
{
Hector.Framework.Utils.Peripherals.Mouse.MoveToPoint(300, 0); //Move the cursor to Top
}
//Then use timer to move the cursor
int a = 0;
private void timer1_Tick(object sender, EventArgs e)
{
Hector.Framework.Utils.Peripherals.Mouse.MoveToPoint(300, a += 1);
}
if(MouseLeftButton)
{
timer1.Start(); //timer is initialized
}

How to get a constant offset of Adorner in WPF?

I am trying to add drag and drop as a feature to my program. However I am really close at getting it right, but I can't figure out how to set a constant offset of the Adorner I built. It has a way to big offset and as I move the mouse it even changes.
I create the adorner within the following method:
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Point mousePos = e.GetPosition(null);
Vector diff = _startPoint.Value - mousePos;
// Initialize the drag & drop operation
DataObject dragData = new DataObject("foo", foo);
_adorner = new DragAdorner(button, mousePos);
AdornerLayer.GetAdornerLayer(this).Add(_adorner);
DragDrop.DoDragDrop(this, dragData, DragDropEffects.Move);
if (_adorner != null && AdornerLayer.GetAdornerLayer(button) != null)
AdornerLayer.GetAdornerLayer(this).Remove(_adorner);
}
I update the Position with another method:
private void OnGiveFeedback(object sender, GiveFeedbackEventArgs e)
{
if (_adorner == null) return;
UIElement lbl = sender as UIElement;
if (lbl == null) return;
var pos = lbl.PointToScreen(Utils.GetCursorPos());
_adorner.UpdatePosition(pos);
}
And the DragAdorner.cs looks like this:
public class DragAdorner : Adorner
{
private Brush _vbrush;
private Point _location;
private Point _offset;
public DragAdorner(UIElement adornedElement, Point offset)
: base(adornedElement)
{
_offset = offset;
IsHitTestVisible = false;
_vbrush = new VisualBrush(AdornedElement) {Opacity = .7};
}
public void UpdatePosition(Point location)
{
_location = location;
InvalidateVisual();
}
public void SetDropable(bool dropable)
{
ReservationButton button = AdornedElement as ReservationButton;
if (button == null) return;
if (dropable)
{
button.Background = Utils.GetReservationColor(button.Reservation.Status);
}
else
{
button.Background = NaramisColors.Yellow;
}
InvalidateVisual();
}
protected override void OnRender(DrawingContext dc)
{
var p = _location;
p.Offset(-_offset.X, -_offset.Y);
dc.DrawRectangle(_vbrush, null, new Rect(p, RenderSize));
}
}
Inside of Utils.cs I wrote the methods to get a Win32Point
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetCursorPos(out POINT lpPoint);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
}
[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
private static Point ConvertPixelsToUnits(int x, int y)
{
// get the system DPI
IntPtr dDc = GetDC(IntPtr.Zero); // Get desktop DC
int dpi = GetDeviceCaps(dDc, 88);
bool rv = ReleaseDC(IntPtr.Zero, dDc);
// WPF's physical unit size is calculated by taking the
// "Device-Independant Unit Size" (always 1/96)
// and scaling it by the system DPI
double physicalUnitSize = 1d / 96d * dpi;
Point wpfUnits = new Point(x / physicalUnitSize , y / physicalUnitSize);
return wpfUnits;
}
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap
{
VERTRES = 10,
DESKTOPVERTRES = 117
}
public static float GetScalingFactor()
{
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
int logicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
int physicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
float screenScalingFactor = physicalScreenHeight / (float)logicalScreenHeight;
return screenScalingFactor; // 1.25 = 125%
}
public static Point GetCursorPos()
{
POINT p;
if (GetCursorPos(out p))
{
Point wpfPoint = ConvertPixelsToUnits(p.X, p.Y);
return wpfPoint;
}
return new Point();
}
Also I made a gif to visualize my problem, you can find it here
I'd be really happy if you could help me out with this one, I am working on it for hours now and I just can't find the right solution.

xamarin ViewPager Android

I was trying to create a Sliding-Tab-Layout. I followed this tutorial Sliding Tab Layout
and it was great, but I wanted to Load Specific Layout in each TabView
I also want to make each one loaded, when I select it or scroll to it just like facebook application.
The reason is not to take so much time to load for each View in ViewPager
and there is the classes:
SlidingTabStrip
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Util;
namespace SlidingTabLayoutTutorial
{
public class SlidingTabStrip : LinearLayout
{
//Copy and paste from here................................................................
private const int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
private const byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0X26;
private const int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
private int[] INDICATOR_COLORS = { 0xffffff };
private int[] DIVIDER_COLORS = { 0xffffff };
private const int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
private const float DEFAULT_DIVIDER_HEIGHT = 0.5f;
//Bottom border
private int mBottomBorderThickness;
private Paint mBottomBorderPaint;
private int mDefaultBottomBorderColor;
//Indicator
private int mSelectedIndicatorThickness;
private Paint mSelectedIndicatorPaint;
//Divider
private Paint mDividerPaint;
private float mDividerHeight;
//Selected position and offset
private int mSelectedPosition;
private float mSelectionOffset;
//Tab colorizer
private SlidingTabScrollView.TabColorizer mCustomTabColorizer;
private SimpleTabColorizer mDefaultTabColorizer;
//Stop copy and paste here........................................................................
//Constructors
public SlidingTabStrip (Context context) : this(context, null)
{ }
public SlidingTabStrip (Context context, IAttributeSet attrs) : base(context, attrs)
{
SetWillNotDraw(false);
float density = Resources.DisplayMetrics.Density;
TypedValue outValue = new TypedValue();
context.Theme.ResolveAttribute(Android.Resource.Attribute.ColorForeground, outValue, true);
int themeForeGround = outValue.Data;
mDefaultBottomBorderColor = SetColorAlpha(themeForeGround, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
mDefaultTabColorizer = new SimpleTabColorizer();
mDefaultTabColorizer.IndicatorColors = INDICATOR_COLORS;
mDefaultTabColorizer.DividerColors = DIVIDER_COLORS;
mBottomBorderThickness = (int)(DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
mBottomBorderPaint = new Paint();
mBottomBorderPaint.Color = GetColorFromInteger(0x1B729E); //Gray
mSelectedIndicatorThickness = (int)(SELECTED_INDICATOR_THICKNESS_DIPS * density);
mSelectedIndicatorPaint = new Paint();
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
mDividerPaint = new Paint();
mDividerPaint.StrokeWidth = (int)(DEFAULT_DIVIDER_THICKNESS_DIPS * density);
}
public SlidingTabScrollView.TabColorizer CustomTabColorizer
{
set
{
mCustomTabColorizer = value;
this.Invalidate();
}
}
public int [] SelectedIndicatorColors
{
set
{
mCustomTabColorizer = null;
mDefaultTabColorizer.IndicatorColors = value;
this.Invalidate();
}
}
public int [] DividerColors
{
set
{
mDefaultTabColorizer = null;
mDefaultTabColorizer.DividerColors = value;
this.Invalidate();
}
}
private Color GetColorFromInteger(int color)
{
return Color.Rgb(Color.GetRedComponent(color), Color.GetGreenComponent(color), Color.GetBlueComponent(color));
}
private int SetColorAlpha(int color, byte alpha)
{
return Color.Argb(alpha, Color.GetRedComponent(color), Color.GetGreenComponent(color), Color.GetBlueComponent(color));
}
public void OnViewPagerPageChanged(int position, float positionOffset)
{
mSelectedPosition = position;
mSelectionOffset = positionOffset;
this.Invalidate();
}
protected override void OnDraw(Canvas canvas)
{
int height = Height;
int tabCount = ChildCount;
int dividerHeightPx = (int)(Math.Min(Math.Max(0f, mDividerHeight), 1f) * height);
SlidingTabScrollView.TabColorizer tabColorizer = mCustomTabColorizer != null ? mCustomTabColorizer : mDefaultTabColorizer;
//Thick colored underline below the current selection
if (tabCount > 0)
{
View selectedTitle = GetChildAt(mSelectedPosition);
int left = selectedTitle.Left;
int right = selectedTitle.Right;
int color = tabColorizer.GetIndicatorColor(mSelectedPosition);
if (mSelectionOffset > 0f && mSelectedPosition < (tabCount - 1))
{
int nextColor = tabColorizer.GetIndicatorColor(mSelectedPosition + 1);
if (color != nextColor)
{
color = blendColor(nextColor, color, mSelectionOffset);
}
View nextTitle = GetChildAt(mSelectedPosition + 1);
left = (int)(mSelectionOffset * nextTitle.Left + (1.0f - mSelectionOffset) * left);
right = (int)(mSelectionOffset * nextTitle.Right + (1.0f - mSelectionOffset) * right);
}
mSelectedIndicatorPaint.Color = GetColorFromInteger(color);
canvas.DrawRect(left, height - mSelectedIndicatorThickness, right, height, mSelectedIndicatorPaint);
//Creat vertical dividers between tabs
int separatorTop = (height - dividerHeightPx) / 2;
for (int i = 0; i < ChildCount -1; i++)
{
View child = GetChildAt(i);
mDividerPaint.Color = GetColorFromInteger(tabColorizer.GetDividerColor(i));
canvas.DrawLine(child.Right, separatorTop, child.Right, separatorTop + dividerHeightPx, mDividerPaint);
}
canvas.DrawRect(0, height - mBottomBorderThickness, Width, height, mBottomBorderPaint);
}
}
private int blendColor(int color1, int color2, float ratio)
{
float inverseRatio = 1f - ratio;
float r = (Color.GetRedComponent(color1) * ratio) + (Color.GetRedComponent(color2) * inverseRatio);
float g = (Color.GetGreenComponent(color1) * ratio) + (Color.GetGreenComponent(color2) * inverseRatio);
float b = (Color.GetBlueComponent(color1) * ratio) + (Color.GetBlueComponent(color2) * inverseRatio);
return Color.Rgb((int)r, (int)g, (int)b);
}
private class SimpleTabColorizer : SlidingTabScrollView.TabColorizer
{
private int[] mIndicatorColors;
private int[] mDividerColors;
public int GetIndicatorColor(int position)
{
return mIndicatorColors[position % mIndicatorColors.Length];
}
public int GetDividerColor (int position)
{
return mDividerColors[position % mDividerColors.Length];
}
public int[] IndicatorColors
{
set { mIndicatorColors = value; }
}
public int[] DividerColors
{
set { mDividerColors = value; }
}
}
}
}
SlidingTabScrollView
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V4.View;
using Android.Util;
using Android.Graphics;
namespace SlidingTabLayoutTutorial
{
public class SlidingTabScrollView : HorizontalScrollView
{
private const int TITLE_OFFSET_DIPS = 15;
private const int TAB_VIEW_PADDING_DIPS = 15;
private const int TAB_VIEW_TEXT_SIZE_SP = 20;
private int mTitleOffset;
//private int mTabViewLayoutID;
//private int mTabViewTextViewID;
private ViewPager mViewPager;
private ViewPager.IOnPageChangeListener mViewPagerPageChangeListener;
private static SlidingTabStrip mTabStrip;
private int mScrollState;
public interface TabColorizer
{
int GetIndicatorColor(int position);
int GetDividerColor(int position);
}
public SlidingTabScrollView(Context context) : this(context, null) { }
public SlidingTabScrollView(Context context, IAttributeSet attrs) : this(context, attrs, 0) { }
public SlidingTabScrollView (Context context, IAttributeSet attrs, int defaultStyle) : base(context, attrs, defaultStyle)
{
//Disable the scroll bar
HorizontalScrollBarEnabled = false;
//Make sure the tab strips fill the view
FillViewport = true;
this.SetBackgroundColor(Android.Graphics.Color.ParseColor("#0078FF")); //Gray color
mTitleOffset = (int)(TITLE_OFFSET_DIPS * Resources.DisplayMetrics.Density);
mTabStrip = new SlidingTabStrip(context);
mTabStrip.WeightSum = 3;
this.AddView(mTabStrip, LayoutParams.MatchParent, LayoutParams.MatchParent);
}
public TabColorizer CustomTabColorizer
{
set { mTabStrip.CustomTabColorizer = value; }
}
public int [] SelectedIndicatorColor
{
set { mTabStrip.SelectedIndicatorColors = value; }
}
public int [] DividerColors
{
set { mTabStrip.DividerColors = value; }
}
public ViewPager.IOnPageChangeListener OnPageListener
{
set { mViewPagerPageChangeListener = value; }
}
public ViewPager ViewPager
{
set
{
mTabStrip.RemoveAllViews();
mViewPager = value;
if (value != null)
{
value.PageSelected += value_PageSelected;
value.PageScrollStateChanged += value_PageScrollStateChanged;
value.PageScrolled += value_PageScrolled;
PopulateTabStrip();
}
}
}
void value_PageScrolled(object sender, ViewPager.PageScrolledEventArgs e)
{
int tabCount = mTabStrip.ChildCount;
if ((tabCount == 0) || (e.Position < 0) || (e.Position >= tabCount))
{
//if any of these conditions apply, return, no need to scroll
return;
}
mTabStrip.OnViewPagerPageChanged(e.Position, e.PositionOffset);
View selectedTitle = mTabStrip.GetChildAt(e.Position);
int extraOffset = (selectedTitle != null ? (int)(e.Position * selectedTitle.Width) : 0);
ScrollToTab(e.Position, extraOffset);
if (mViewPagerPageChangeListener != null)
{
mViewPagerPageChangeListener.OnPageScrolled(e.Position, e.PositionOffset, e.PositionOffsetPixels);
}
}
void value_PageScrollStateChanged(object sender, ViewPager.PageScrollStateChangedEventArgs e)
{
mScrollState = e.State;
if (mViewPagerPageChangeListener != null)
{
mViewPagerPageChangeListener.OnPageScrollStateChanged(e.State);
}
}
void value_PageSelected(object sender, ViewPager.PageSelectedEventArgs e)
{
if (mScrollState == ViewPager.ScrollStateIdle)
{
mTabStrip.OnViewPagerPageChanged(e.Position, 0f);
ScrollToTab(e.Position, 0);
}
if (mViewPagerPageChangeListener != null)
{
mViewPagerPageChangeListener.OnPageSelected(e.Position);
}
}
private void PopulateTabStrip()
{
PagerAdapter adapter = mViewPager.Adapter;
for (int i = 0; i < adapter.Count; i++)
{
TextView tabView = CreateDefaultTabView(Context);
tabView.Text = ((SlidingTabsFragment.SamplePagerAdapter)adapter).GetHeaderTitle(i);
tabView.SetTextColor(Android.Graphics.Color.White);
tabView.Tag = i;
tabView.Click += tabView_Click;
mTabStrip.AddView(tabView);
}
}
void tabView_Click(object sender, EventArgs e)
{
TextView clickTab = (TextView)sender;
int pageToScrollTo = (int)clickTab.Tag;
mViewPager.CurrentItem = pageToScrollTo;
}
private TextView CreateDefaultTabView(Android.Content.Context context )
{
TextView textView = new TextView(context);
textView.Gravity = GravityFlags.Center;
textView.SetTextSize(ComplexUnitType.Sp, TAB_VIEW_TEXT_SIZE_SP);
textView.Typeface = Android.Graphics.Typeface.Default;
textView.LayoutParameters = new LinearLayout.LayoutParams(0, LayoutParams.MatchParent, 1);
if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Honeycomb)
{
TypedValue outValue = new TypedValue();
Context.Theme.ResolveAttribute(Android.Resource.Attribute.SelectableItemBackground, outValue, false);
textView.SetBackgroundResource(outValue.ResourceId);
}
if (Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.IceCreamSandwich)
{
textView.SetAllCaps(true);
}
int padding = (int)(TAB_VIEW_PADDING_DIPS * Resources.DisplayMetrics.Density);
textView.SetPadding(padding, padding, padding, padding);
return textView;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
if (mViewPager != null)
{
ScrollToTab(mViewPager.CurrentItem, 0);
}
}
private void ScrollToTab(int tabIndex, int extraOffset)
{
int tabCount = mTabStrip.ChildCount;
if (tabCount == 0 || tabIndex < 0 || tabIndex >= tabCount)
{
//No need to go further, dont scroll
return;
}
View selectedChild = mTabStrip.GetChildAt(tabIndex);
if (selectedChild != null)
{
int scrollAmountX = selectedChild.Left + extraOffset;
if (tabIndex >0 || extraOffset > 0)
{
scrollAmountX -= mTitleOffset;
}
this.ScrollTo(scrollAmountX, 0);
}
}
}
}
SlidingTabsFragment
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Android.Support.V4.View;
namespace SlidingTabLayoutTutorial
{
public class SlidingTabsFragment : Fragment
{
private SlidingTabScrollView mSlidingTabScrollView;
private ViewPager mViewPager;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.fragment_sample, container, false);
}
public override void OnViewCreated(View view, Bundle savedInstanceState)
{
mSlidingTabScrollView = view.FindViewById<SlidingTabScrollView>(Resource.Id.sliding_tabs);
mViewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
mViewPager.Adapter = new SamplePagerAdapter();
mSlidingTabScrollView.ViewPager = mViewPager;
}
public class SamplePagerAdapter : PagerAdapter
{
List<string> items = new List<string>();
public SamplePagerAdapter() : base()
{
items.Add("Home");
items.Add("Sell");
items.Add("Rent");
}
public override int Count
{
get { return items.Count; }
}
public override bool IsViewFromObject(View view, Java.Lang.Object obj)
{
return view == obj;
}
public override Java.Lang.Object InstantiateItem(ViewGroup container, int position)
{
View view = LayoutInflater.From(container.Context).Inflate(Resource.Layout.pager_item, container, false);
container.AddView(view);
TextView txtTitle = view.FindViewById<TextView>(Resource.Id.item_title);
int pos = position + 1;
txtTitle.Text = pos.ToString();
return view;
}
public string GetHeaderTitle (int position)
{
return items[position];
}
public override void DestroyItem(ViewGroup container, int position, Java.Lang.Object obj)
{
container.RemoveView((View)obj);
}
}
}
}
MainActivity
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Support.V4.View;
namespace SlidingTabLayoutTutorial
{
[Activity(Label = "Sliding Tab Layout", MainLauncher = true, Icon = "#drawable/xs")]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
FragmentTransaction transaction = FragmentManager.BeginTransaction();
SlidingTabsFragment fragment = new SlidingTabsFragment();
transaction.Replace(Resource.Id.sample_content_fragment, fragment);
transaction.Commit();
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.actionbar_main, menu);
return base.OnCreateOptionsMenu(menu);
}
}
}
I think you need to add in your code something like this : (in OnCreateView() )
position => comes from the adapter..
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
switch (position)
{
case 0:
view = (ViewGroup)inflater.Inflate(Resource.Layout.Page1, container, false);
break;
case 1:
view = (ViewGroup)inflater.Inflate(Resource.Layout.Page2, container, false);
btn_forexample = view.FindViewById<Button>(Resource.Id.btn_forexample);
break;
case 2:
view = (ViewGroup)inflater.Inflate(Resource.Layout.Page3, container, false);
break;
default:
view = (ViewGroup)inflater.Inflate(Resource.Layout.DefaultPage, container, false);
break;
}
return view;
}
Hope it helps ;)

How do you do AppBar docking (to screen edge, like WinAmp) in WPF?

Is there any complete guidance on doing AppBar docking (such as locking to the screen edge) in WPF? I understand there are InterOp calls that need to be made, but I'm looking for either a proof of concept based on a simple WPF form, or a componentized version that can be consumed.
Related resources:
http://www.codeproject.com/KB/dotnet/AppBar.aspx
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/05c73c9c-e85d-4ecd-b9b6-4c714a65e72b/
Please Note: This question gathered a good amount of feedback, and some people below have made great points or fixes. Therefore, while I'll keep the code here (and possibly update it), I've also created a WpfAppBar project on github. Feel free to send pull requests.
That same project also builds to a WpfAppBar nuget package
I took the code from the first link provided in the question ( http://www.codeproject.com/KB/dotnet/AppBar.aspx ) and modified it to do two things:
Work with WPF
Be "standalone" - if you put this single file in your project, you can call AppBarFunctions.SetAppBar(...) without any further modification to the window.
This approach doesn't create a base class.
To use, just call this code from anywhere within a normal wpf window (say a button click or the initialize). Note that you can not call this until AFTER the window is initialized, if the HWND hasn't been created yet (like in the constructor), an error will occur.
Make the window an appbar:
AppBarFunctions.SetAppBar( this, ABEdge.Right );
Restore the window to a normal window:
AppBarFunctions.SetAppBar( this, ABEdge.None );
Here's the full code to the file - note you'll want to change the namespace on line 7 to something apropriate.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if( s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition =new Point( appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size( appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if( edge == ABEdge.None)
{
if( info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = 0;
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = 0;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth);
}
else
{
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = 0;
barData.rc.right = (int)SystemParameters.PrimaryScreenWidth;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = 0;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight);
}
else
{
barData.rc.bottom = (int)SystemParameters.PrimaryScreenHeight;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
There is an excellent MSDN article from 1996 which is entertainingly up to date: Extend the Windows 95 Shell with Application Desktop Toolbars. Following its guidance produces an WPF based appbar which handles a number of scenarios that the other answers on this page do not:
Allow dock to any side of the screen
Allow dock to a particular monitor
Allow resizing of the appbar (if desired)
Handle screen layout changes and monitor disconnections
Handle Win + Shift + Left and attempts to minimize or move the window
Handle co-operation with other appbars (OneNote et al.)
Handle per-monitor DPI scaling
I have both a demo app and the implementation of AppBarWindow on GitHub.
Example use:
<apb:AppBarWindow x:Class="WpfAppBarDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:apb="clr-namespace:WpfAppBar;assembly=WpfAppBar"
DataContext="{Binding RelativeSource={RelativeSource Self}}" Title="MainWindow"
DockedWidthOrHeight="200" MinHeight="100" MinWidth="100">
<Grid>
<Button x:Name="btClose" Content="Close" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="23" Margin="10,10,0,0" Click="btClose_Click"/>
<ComboBox x:Name="cbMonitor" SelectedItem="{Binding Path=Monitor, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="10,38,0,0"/>
<ComboBox x:Name="cbEdge" SelectedItem="{Binding Path=DockMode, Mode=TwoWay}" HorizontalAlignment="Left" Margin="10,65,0,0" VerticalAlignment="Top" Width="120"/>
<Thumb Width="5" HorizontalAlignment="Right" Background="Gray" x:Name="rzThumb" Cursor="SizeWE" DragCompleted="rzThumb_DragCompleted" />
</Grid>
</apb:AppBarWindow>
Codebehind:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.cbEdge.ItemsSource = new[]
{
AppBarDockMode.Left,
AppBarDockMode.Right,
AppBarDockMode.Top,
AppBarDockMode.Bottom
};
this.cbMonitor.ItemsSource = MonitorInfo.GetAllMonitors();
}
private void btClose_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void rzThumb_DragCompleted(object sender, DragCompletedEventArgs e)
{
this.DockedWidthOrHeight += (int)(e.HorizontalChange / VisualTreeHelper.GetDpi(this).PixelsPerDip);
}
}
Changing docked position:
Resizing with thumb:
Cooperation with other appbars:
Clone from GitHub if you want to use it. The library itself is only three files, and can easily be dropped in a project.
Very happy to have found this question. Above class is really useful, but doesnt quite cover all the bases of AppBar implementation.
To fully implement all the behaviour of an AppBar (cope with fullscreen apps etc) you're going to want to read this MSDN article too.
http://msdn.microsoft.com/en-us/library/bb776821.aspx
Sorry for my English... Here is the Philip Rieck's solution with some corrects. It correctly works with Taskbar position and size changes.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace wpf_appbar
{
public enum ABEdge : int
{
Left,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(Rect r)
{
Left = (int)r.Left;
Right = (int)r.Right;
Top = (int)r.Top;
Bottom = (int)r.Bottom;
}
public static bool operator ==(RECT r1, RECT r2)
{
return r1.Bottom == r2.Bottom && r1.Left == r2.Left && r1.Right == r2.Right && r1.Top == r2.Top;
}
public static bool operator !=(RECT r1, RECT r2)
{
return !(r1 == r2);
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
private enum TaskBarPosition : int
{
Left,
Top,
Right,
Bottom
}
[StructLayout(LayoutKind.Sequential)]
class TaskBar
{
public TaskBarPosition Position;
public TaskBarPosition PreviousPosition;
public RECT Rectangle;
public RECT PreviousRectangle;
public int Width;
public int PreviousWidth;
public int Height;
public int PreviousHeight;
public TaskBar()
{
Refresh();
}
public void Refresh()
{
APPBARDATA msgData = new APPBARDATA();
msgData.cbSize = Marshal.SizeOf(msgData);
SHAppBarMessage((int)ABMsg.ABM_GETTASKBARPOS, ref msgData);
PreviousPosition = Position;
PreviousRectangle = Rectangle;
PreviousHeight = Height;
PreviousWidth = Width;
Rectangle = msgData.rc;
Width = Rectangle.Right - Rectangle.Left;
Height = Rectangle.Bottom - Rectangle.Top;
int h = (int)SystemParameters.PrimaryScreenHeight;
int w = (int)SystemParameters.PrimaryScreenWidth;
if (Rectangle.Bottom == h && Rectangle.Top != 0) Position = TaskBarPosition.Bottom;
else if (Rectangle.Top == 0 && Rectangle.Bottom != h) Position = TaskBarPosition.Top;
else if (Rectangle.Right == w && Rectangle.Left != 0) Position = TaskBarPosition.Right;
else if (Rectangle.Left == 0 && Rectangle.Right != w) Position = TaskBarPosition.Left;
}
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public ABEdge PreviousEdge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
PreviousEdge = Edge;
ABSetPos(Edge, PreviousEdge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if (s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.None,
PreviousEdge = ABEdge.None,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if (edge == ABEdge.None)
{
if (info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
info.PreviousEdge = info.Edge;
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, info.PreviousEdge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
static TaskBar tb = new TaskBar();
private static void ABSetPos(ABEdge edge, ABEdge prevEdge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
RECT wa = new RECT(SystemParameters.WorkArea);
tb.Refresh();
switch (edge)
{
case ABEdge.Top:
barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Top = wa.Top - (prevEdge == ABEdge.Top ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - ((tb.Position != TaskBarPosition.Top && tb.PreviousPosition == TaskBarPosition.Top) ? tb.Height : 0) + ((tb.Position == TaskBarPosition.Top && tb.PreviousPosition != TaskBarPosition.Top) ? tb.Height : 0);
barData.rc.Bottom = barData.rc.Top + (int)Math.Round(appbarWindow.ActualHeight);
break;
case ABEdge.Bottom:
barData.rc.Left = wa.Left - (prevEdge == ABEdge.Left ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Right = wa.Right + (prevEdge == ABEdge.Right ? (int)Math.Round(appbarWindow.ActualWidth) : 0);
barData.rc.Bottom = wa.Bottom + (prevEdge == ABEdge.Bottom ? (int)Math.Round(appbarWindow.ActualHeight) : 0) - 1 + ((tb.Position != TaskBarPosition.Bottom && tb.PreviousPosition == TaskBarPosition.Bottom) ? tb.Height : 0) - ((tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition != TaskBarPosition.Bottom) ? tb.Height : 0);
barData.rc.Top = barData.rc.Bottom - (int)Math.Round(appbarWindow.ActualHeight);
break;
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
switch (barData.uEdge)
{
case (int)ABEdge.Bottom:
if (tb.Position == TaskBarPosition.Bottom && tb.PreviousPosition == tb.Position)
{
barData.rc.Top += (tb.PreviousHeight - tb.Height);
barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight;
}
break;
case (int)ABEdge.Top:
if (tb.Position == TaskBarPosition.Top && tb.PreviousPosition == tb.Position)
{
if (tb.PreviousHeight - tb.Height > 0) barData.rc.Top -= (tb.PreviousHeight - tb.Height);
barData.rc.Bottom = barData.rc.Top + (int)appbarWindow.ActualHeight;
}
break;
}
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.Left, (double)barData.rc.Top, (double)(barData.rc.Right - barData.rc.Left), (double)(barData.rc.Bottom - barData.rc.Top));
appbarWindow.Dispatcher.BeginInvoke(new ResizeDelegate(DoResize), DispatcherPriority.ApplicationIdle, appbarWindow, rect);
}
}
}
The same code you can write for the Left and Right edges.
Good job, Philip Rieck, thank you!
I modified code from Philip Rieck (btw. Thanks a lot) to work in multiple display settings. Here's my solution.
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace AppBarApplication
{
public enum ABEdge : int
{
Left = 0,
Top,
Right,
Bottom,
None
}
internal static class AppBarFunctions
{
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct APPBARDATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public int cbSize;
public RECT rcMonitor;
public RECT rcWork;
public int dwFlags;
}
private enum ABMsg : int
{
ABM_NEW = 0,
ABM_REMOVE,
ABM_QUERYPOS,
ABM_SETPOS,
ABM_GETSTATE,
ABM_GETTASKBARPOS,
ABM_ACTIVATE,
ABM_GETAUTOHIDEBAR,
ABM_SETAUTOHIDEBAR,
ABM_WINDOWPOSCHANGED,
ABM_SETSTATE
}
private enum ABNotify : int
{
ABN_STATECHANGE = 0,
ABN_POSCHANGED,
ABN_FULLSCREENAPP,
ABN_WINDOWARRANGE
}
[DllImport("SHELL32", CallingConvention = CallingConvention.StdCall)]
private static extern uint SHAppBarMessage(int dwMessage, ref APPBARDATA pData);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int RegisterWindowMessage(string msg);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO mi);
private const int MONITOR_DEFAULTTONEAREST = 0x2;
private const int MONITORINFOF_PRIMARY = 0x1;
private class RegisterInfo
{
public int CallbackId { get; set; }
public bool IsRegistered { get; set; }
public Window Window { get; set; }
public ABEdge Edge { get; set; }
public WindowStyle OriginalStyle { get; set; }
public Point OriginalPosition { get; set; }
public Size OriginalSize { get; set; }
public ResizeMode OriginalResizeMode { get; set; }
public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam,
IntPtr lParam, ref bool handled)
{
if (msg == CallbackId)
{
if (wParam.ToInt32() == (int)ABNotify.ABN_POSCHANGED)
{
ABSetPos(Edge, Window);
handled = true;
}
}
return IntPtr.Zero;
}
}
private static Dictionary<Window, RegisterInfo> s_RegisteredWindowInfo
= new Dictionary<Window, RegisterInfo>();
private static RegisterInfo GetRegisterInfo(Window appbarWindow)
{
RegisterInfo reg;
if (s_RegisteredWindowInfo.ContainsKey(appbarWindow))
{
reg = s_RegisteredWindowInfo[appbarWindow];
}
else
{
reg = new RegisterInfo()
{
CallbackId = 0,
Window = appbarWindow,
IsRegistered = false,
Edge = ABEdge.Top,
OriginalStyle = appbarWindow.WindowStyle,
OriginalPosition = new Point(appbarWindow.Left, appbarWindow.Top),
OriginalSize =
new Size(appbarWindow.ActualWidth, appbarWindow.ActualHeight),
OriginalResizeMode = appbarWindow.ResizeMode,
};
s_RegisteredWindowInfo.Add(appbarWindow, reg);
}
return reg;
}
private static void RestoreWindow(Window appbarWindow)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
appbarWindow.WindowStyle = info.OriginalStyle;
appbarWindow.ResizeMode = info.OriginalResizeMode;
appbarWindow.Topmost = false;
Rect rect = new Rect(info.OriginalPosition.X, info.OriginalPosition.Y,
info.OriginalSize.Width, info.OriginalSize.Height);
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
public static void SetAppBar(Window appbarWindow, ABEdge edge)
{
RegisterInfo info = GetRegisterInfo(appbarWindow);
info.Edge = edge;
APPBARDATA abd = new APPBARDATA();
abd.cbSize = Marshal.SizeOf(abd);
abd.hWnd = new WindowInteropHelper(appbarWindow).Handle;
if (edge == ABEdge.None)
{
if (info.IsRegistered)
{
SHAppBarMessage((int)ABMsg.ABM_REMOVE, ref abd);
info.IsRegistered = false;
}
RestoreWindow(appbarWindow);
return;
}
if (!info.IsRegistered)
{
info.IsRegistered = true;
info.CallbackId = RegisterWindowMessage("AppBarMessage");
abd.uCallbackMessage = info.CallbackId;
uint ret = SHAppBarMessage((int)ABMsg.ABM_NEW, ref abd);
HwndSource source = HwndSource.FromHwnd(abd.hWnd);
source.AddHook(new HwndSourceHook(info.WndProc));
}
appbarWindow.WindowStyle = WindowStyle.None;
appbarWindow.ResizeMode = ResizeMode.NoResize;
appbarWindow.Topmost = true;
ABSetPos(info.Edge, appbarWindow);
}
private delegate void ResizeDelegate(Window appbarWindow, Rect rect);
private static void DoResize(Window appbarWindow, Rect rect)
{
appbarWindow.Width = rect.Width;
appbarWindow.Height = rect.Height;
appbarWindow.Top = rect.Top;
appbarWindow.Left = rect.Left;
}
private static void GetActualScreenData(ABEdge edge, Window appbarWindow, ref int leftOffset, ref int topOffset, ref int actualScreenWidth, ref int actualScreenHeight)
{
IntPtr handle = new WindowInteropHelper(appbarWindow).Handle;
IntPtr monitorHandle = MonitorFromWindow(handle, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi = new MONITORINFO();
mi.cbSize = Marshal.SizeOf(mi);
if (GetMonitorInfo(monitorHandle, ref mi))
{
if (mi.dwFlags == MONITORINFOF_PRIMARY)
{
return;
}
leftOffset = mi.rcWork.left;
topOffset = mi.rcWork.top;
actualScreenWidth = mi.rcWork.right - leftOffset;
actualScreenHeight = mi.rcWork.bottom - mi.rcWork.top;
}
}
private static void ABSetPos(ABEdge edge, Window appbarWindow)
{
APPBARDATA barData = new APPBARDATA();
barData.cbSize = Marshal.SizeOf(barData);
barData.hWnd = new WindowInteropHelper(appbarWindow).Handle;
barData.uEdge = (int)edge;
int leftOffset = 0;
int topOffset = 0;
int actualScreenWidth = (int)SystemParameters.PrimaryScreenWidth;
int actualScreenHeight = (int)SystemParameters.PrimaryScreenHeight;
GetActualScreenData(edge, appbarWindow, ref leftOffset, ref topOffset, ref actualScreenWidth, ref actualScreenHeight);
if (barData.uEdge == (int)ABEdge.Left || barData.uEdge == (int)ABEdge.Right)
{
barData.rc.top = topOffset;
barData.rc.bottom = actualScreenHeight;
if (barData.uEdge == (int)ABEdge.Left)
{
barData.rc.left = leftOffset;
barData.rc.right = (int)Math.Round(appbarWindow.ActualWidth) + leftOffset;
}
else
{
barData.rc.right = actualScreenWidth + leftOffset;
barData.rc.left = barData.rc.right - (int)Math.Round(appbarWindow.ActualWidth);
}
}
else
{
barData.rc.left = leftOffset;
barData.rc.right = actualScreenWidth + leftOffset;
if (barData.uEdge == (int)ABEdge.Top)
{
barData.rc.top = topOffset;
barData.rc.bottom = (int)Math.Round(appbarWindow.ActualHeight) + topOffset;
}
else
{
barData.rc.bottom = actualScreenHeight + topOffset;
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
}
}
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
Rect rect = new Rect((double)barData.rc.left, (double)barData.rc.top,
(double)(barData.rc.right - barData.rc.left), (double)(barData.rc.bottom - barData.rc.top));
//This is done async, because WPF will send a resize after a new appbar is added.
//if we size right away, WPFs resize comes last and overrides us.
appbarWindow.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
new ResizeDelegate(DoResize), appbarWindow, rect);
}
}
}
As a commercial alternative, see the ready-to-use ShellAppBar component for WPF which supports all cases and secnarios such as taskbar docked to left,right,top,bottom edge, support for multiple monitors, drag-docking, autohide , etc etc. It may save you time and money over trying to handle all these cases yourself.
DISCLAIMER: I work for LogicNP Software, the developer of ShellAppBar.
Sorry, the last code I posted didn't work when the Taskbar is resized. The following code change seems to work better:
SHAppBarMessage((int)ABMsg.ABM_QUERYPOS, ref barData);
if (barData.uEdge == (int)ABEdge.Top)
barData.rc.bottom = barData.rc.top + (int)Math.Round(appbarWindow.ActualHeight);
else if (barData.uEdge == (int)ABEdge.Bottom)
barData.rc.top = barData.rc.bottom - (int)Math.Round(appbarWindow.ActualHeight);
SHAppBarMessage((int)ABMsg.ABM_SETPOS, ref barData);
I've spent some weeks exploring this challenge and finally created a very solid NuGet package delivering this functionality in very friendly way. Simply create a new WPF app then change the main window's class from Window to DockWindow (in the XAML) and that's it!
Get the package here and see the Git repo for a demonstration app.

Categories

Resources