For an exam I have to start three screens at the start of the UWP application. I choose the MainPage to start everything. The main screen and second screen (TeamTotals) are displayed oké but as soon as I include the third screen I get exeptions and the third screen doesn't appear. When I start the SpelersRank in the main constructor it sometimes appears but not always so I tried a delay but nothing seems to solve the problem. Can you help me to solve this? Code here:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private async void NavigationViewLoaded(object sender, RoutedEventArgs e)
{
await TeamTotal();
await Task.Delay(1000);
//await SpelerRank(); //start the compact overlay for the spelers ranking
ContentFrame.Navigate(typeof(WedstrijdListPage));
}
private async Task TeamTotal()
{
var myview = CoreApplication.CreateNewView();
int newViewId = 0;
await myview.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame newFrame = new Frame();
newFrame.Navigate(typeof(TeamTotals), null);
Window.Current.Content = newFrame;
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
await ApplicationViewSwitcher.TryShowAsViewModeAsync(newViewId, ApplicationViewMode.CompactOverlay);
}
private async Task SpelerRank()
{
var myview = CoreApplication.CreateNewView();
int newViewId = 0;
await myview.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame newFrame = new Frame();
newFrame.Navigate(typeof(SpelerRank), null);
Window.Current.Content = newFrame;
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
await ApplicationViewSwitcher.TryShowAsViewModeAsync(newViewId, ApplicationViewMode.CompactOverlay);
}
private void NavigationViewSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
if (args.IsSettingsSelected)
{
//TODO--code voor settings hier
}
else
{
NavigationViewItem item = args.SelectedItem as NavigationViewItem;
switch (item.Tag.ToString())
{
case "home":
ContentFrame.Navigate(typeof(HomePage));
break;
case "clubs":
ContentFrame.Navigate(typeof(GolfclubListPage));
break;
case "competitie":
ContentFrame.Navigate(typeof(KlasseListPage));
break;
case "spelers":
ContentFrame.Navigate(typeof(SpelerListPage));
break;
case "teams":
ContentFrame.Navigate(typeof(TeamListPage));
break;
case "wedstrijden":
ContentFrame.Navigate(typeof(WedstrijdListPage));
break;
case "coach":
ContentFrame.Navigate(typeof(CoachListPage));
break;
}
}
}
private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
}
private void navView_BackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
if (ContentFrame.CanGoBack)
ContentFrame.GoBack();
}
}
Related
I have three buttons. The timer should have started when I pressed any of the 3 optional buttons. For example. When I press the blueHeadTopBtn button, the timer starts. If I press one or both of the remaining 2 buttons in 1 second, it gives value to the label. in this case, if a button is pressed 2 times, the label should not be valued.
When the window is loaded, it works when I click the buttons, but it does not work when I press the keyboard.
These buttons are working correctly but the codes in Window_KeyUp and Window_KeyDown methods are malfunctioning.
private async void blueHeadTopBtn_Click(object sender, RoutedEventArgs e)
{
BlueHead();
WinBluePoint();
blueHeadTopBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadTopBtn.IsEnabled = true;
}
private async void blueHeadLeftBtn_Click(object sender, RoutedEventArgs e)
{
BlueHead();
WinBluePoint();
blueHeadLeftBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadLeftBtn.IsEnabled = true;
}
private async void blueHeadRightBtn_Click(object sender, RoutedEventArgs e)
{
BlueHead();
WinBluePoint();
blueHeadRightBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadRightBtn.IsEnabled = true;
}
These codes are working incorrectly.
private async void Window_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Q)
{
BlueHead();
WinBluePoint();
blueHeadTopBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadTopBtn.IsEnabled = true;
}
if (e.Key == Key.E)
{
BlueHead();
WinBluePoint();
blueHeadLeftBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadLeftBtn.IsEnabled = true;
}
if (e.Key == Key.R)
{
BlueHead();
WinBluePoint();
blueHeadRightBtn.IsEnabled = false;
await Task.Delay(1000);
blueHeadRightBtn.IsEnabled = true;
}
}
How can I solve this issue? Did I use TASK correctly?
I guess your code is wrong. From reviewing your mysterious code snippets, it appears like you want to disable a button for 1s after it was pressed. I assume you want to achieve the same with the keyboard input: ignore a particular key for 1s after it was pressed.
In your key up handlers you only disable the buttons. This of course has no effect on the keyboard input. You must disable the key event handling.
To accomplish this, either unregister the event handler for the duration of the key lock or use a flag.
Instead of creating a flag for every key or button that should participate in the locking logic, this example uses a Dictionary which allows the implementation to scale without extra efforts. This also improves your code by eliminating the duplicate code that you had. Now, we can use a shared event handler for every button and a dedicated handler for keyboard input:
MainWindow.xaml.cs
partial class MainWindow : Window
{
private Dictionary<object, DateTime> LockedInputSourceTable { get; }
private TimeSpan UserInputLockDuration {get; }
private DispatcherTimer InputLockTimer { get; }
public MainWindow()
{
InitializeComponent();
this.UserInputLockDuration = TimeSpan.FromSeconds(1);
this.InputLockTimer = new DispatcherTimer(
TimeSpan.FromMilliseconds(500),
DispatcherPriority.Input,
RemoveInputLock,
Application.Current.Dispatcher);
this.LockedInputSourceTable = new Dictionary<object, DateTime>();
}
private void RemoveInputLock(object? sender, EventArgs e)
{
var currentTime = DateTime.Now;
foreach (var entry in this.LockedInputSourceTable)
{
DateTime timeStamp = entry.Value;
if (currentTime.Subtract(timeStamp) >= this.UserInputLockDuration)
{
object lockedObject = entry.Key;
this.LockedInputSourceTable.Remove(lockedObject);
if (lockedObject is UIElement uiElement)
{
uiElement.IsEnabled = true;
}
}
}
if (!this.LockedInputSourceTable.Any())
{
this.InputLockTimer.Stop();
}
}
protected override void OnPreviewKeyUp(KeyEventArgs e)
{
var currentTime = DateTime.Now;
base.OnPreviewKeyUp(e);
if (this.LockedInputSourceTable.ContainsKey(e.Key))
{
return;
}
switch (e.Key)
{
case Key.Q:
case Key.E:
case Key.R:
// Store timestamp and pressed key
this.LockedInputSourceTable.Add(e.Key, currentTime);
if (!this.InputLockTimer.IsEnabled)
{
this.InputLockTimer.Start();
}
BlueHead();
WinBluePoint();
break;
}
}
private void DisableElement_OnClick(object sender, RoutedEventArgs e)
{
var currentTime = DateTime.Now;
if (this.LockedInputSourceTable.ContainsKey(sender))
{
return;
}
this.LockedInputSourceTable.Add(sender, currentTime);
if (!this.InputLockTimer.IsEnabled)
{
this.InputLockTimer.Start();
}
var uiElement = sender as UIElement;
uiElement.IsEnabled = false;
BlueHead();
WinBluePoint();
}
}
MainWindow.xaml
<Window>
<StackPanel>
<Button Click="DisableElement_OnClick" />
<Button Click="DisableElement_OnClick" />
<Button Click="DisableElement_OnClick" />
</StackPanel>
</Window>
this is just a simplified version of what I am trying to do
public partial class MainWindow : Window
{
private CancellationTokenSource tokenSource = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
PrintButtonText("None");
}
private void PrintButtonText(string buttonText)
{
Console.WriteLine("Update!");
Task.Factory.StartNew(() =>
{
while (!tokenSource.Token.IsCancellationRequested)
{
Console.WriteLine("Button Pressed Text: " + buttonText);
}
}, tokenSource.Token);
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
PrintButtonText("Button1");
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
PrintButtonText("Button2");
}
}
After I do
tokenSource.Cancel();
PrintButtonText("Button1");
It's not able to start the task again and continue to print my line. I need it to work this way for my program.
I want to stop the thread and start it again with some different parameters. How can I achieve this? thanks
EDIT:
Since I didn't get a solution with my simplified version, here is full code and what I am trying to do. Basically, on wpf window camera rendering starts on start. There is a button to start saving to file, but in order to save I have to update the config and start "pipeline" again.
public partial class MainWindow : Window
{
private Pipeline pipeline = new Pipeline(); // Create and config the pipeline to sream color and depth frames.
private CancellationTokenSource tokenSource = new CancellationTokenSource();
private bool saveDataToFile = false;
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Config cfg = SetupConfig(false);
PipelineProfile pp = pipeline.Start(cfg);
StartRenderFrames(pp);
}
private void StartRenderFrames(PipelineProfile pp)
{
Colorizer colorizer = new Colorizer(); // The colorizer processing block used to visualize the depth frames.
// Allocate bitmaps for rendring. Since the sample aligns the depth frames to the color frames, both of the images will have the color resolution
using (var p = pp.GetStream(Stream.Color) as VideoStreamProfile)
{
imgColor.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
imgDepth.Source = new WriteableBitmap(p.Width, p.Height, 96d, 96d, PixelFormats.Rgb24, null);
}
Action<VideoFrame> updateColor = UpdateImage(imgColor);
Action<VideoFrame> updateDepth = UpdateImage(imgDepth);
Task.Factory.StartNew(() =>
{
while (!tokenSource.Token.IsCancellationRequested)
{
// Wait for the next available FrameSet
using (var frames = pipeline.WaitForFrames())
{
var colorFrame = frames.ColorFrame.DisposeWith(frames);
var depthFrame = frames.DepthFrame.DisposeWith(frames);
// We colorize the depth frame for visualization purposes, .
var colorizedDepth = colorizer.Process(depthFrame).DisposeWith(frames);
// Render the frames.
Dispatcher.Invoke(DispatcherPriority.Render, updateDepth, colorizedDepth);
Dispatcher.Invoke(DispatcherPriority.Render, updateColor, colorFrame);
}
}
}, tokenSource.Token);
}
private Config SetupConfig(bool saveDepthFile)
{
Config cfg = new Config();
cfg.EnableStream(Stream.Depth, 640, 480, framerate: 15);
cfg.EnableStream(Stream.Color, 640, 480, format: Format.Rgb8, framerate: 15);
if (saveDepthFile)
{
cfg.EnableRecordToFile(#"C:\temp\My_test111.bag");
}
return cfg;
}
static Action<VideoFrame> UpdateImage(Image img)
{
var wbmp = img.Source as WriteableBitmap;
return new Action<VideoFrame>(frame =>
{
using (frame)
{
var rect = new Int32Rect(0, 0, frame.Width, frame.Height);
wbmp.WritePixels(rect, frame.Data, frame.Stride * frame.Height, frame.Stride);
}
});
}
private void StartSaving_Button_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
pipeline.Stop();
// This is where I have a problem. Rendering thread does not stop before I want to start again.
Config cfg = SetupConfig(true);
PipelineProfile pp = pipeline.Start(cfg);
StartRenderFrames(pp);
}
}
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do this:
private void Form1_Load(object sender, EventArgs e)
{
IObservable<string> button1Clicks =
Observable
.FromEventPattern<EventHandler, EventArgs>(h => button1.Click += h, h => button1.Click -= h)
.Select(ep => "Button1");
IObservable<string> button2Clicks =
Observable
.FromEventPattern<EventHandler, EventArgs>(h => button2.Click += h, h => button2.Click -= h)
.Select(ep => "Button2");
IDisposable subscription =
button1Clicks
.Merge(button2Clicks)
.StartWith("None")
.Select(x => Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(500.0)).Select(n => x))
.Switch()
.ObserveOn(this)
.Subscribe(x => Console.WriteLine(x));
}
That's the entire code needed to make what you want to work.
The only thing that you need to do is move subscription out into a private field and then simply call subscription.Dispose() to shut this all down.
This is much simpler than messing with cancellation tokens.
I have created an app using Xamarin to help watching movies online. It shows the subtitles on top of all other windows. This has been done using the NSPanel, as it was the only way to make it work on MacOS Mojave.
The app works well. Now I want to improve the app by making NSPanel respond to the keyboard events, so I can control the app by using the keyboard for pausing, playing, going backward or going forward.
How do I get keyboard events in the topmost NSPanel?
I tried to use this code:
NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyDown, KeyboardEventHandler);
private static NSEvent KeyboardEventHandler(NSEvent keyEvent)
{
// handle key down events here
return (keyEvent);
}
But it only works when the app is not in the full-screen mode.
The full SubtitlesViewer-MACOS project can be found here.
Here is the part of the code that creates the panel:
public override void ViewWillAppear()
{
base.ViewWillAppear();
SetupView();
}
private void SetupView()
{
var screenRes = screenResolution();
int PANEL_HEIGHT = 200;
subtitlesPanel = new NSPanel
(
new CoreGraphics.CGRect(40, 50, screenRes.Width - 80, PANEL_HEIGHT),
NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | NSWindowStyle.DocModal,
NSBackingStore.Buffered, true
)
{
BackgroundColor = NSColor.FromCalibratedRgba(0, 0, 0, 0.0f),
ReleasedWhenClosed = true,
HidesOnDeactivate = false,
FloatingPanel = true,
StyleMask = NSWindowStyle.NonactivatingPanel,
Level = NSWindowLevel.MainMenu - 1,
IsMovable = true,
CollectionBehavior = NSWindowCollectionBehavior.CanJoinAllSpaces |
NSWindowCollectionBehavior.FullScreenAuxiliary
};
subtitlesPanel.OrderFront(null);
subtitleTextButton = new NSButton(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
{
Title = "",
WantsLayer = true
};
subtitleTextButton.Layer.BackgroundColor = NSColor.Clear.CGColor;
subtitleTextField = new NSTextField(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
{
Alignment = NSTextAlignment.Center
};
subtitleTextField.Cell.Alignment = NSTextAlignment.Center;
forwardButton = new NSButton(new CoreGraphics.CGRect(0, 0, 40, 30));
forwardButton.Title = ">>";
forwardButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.Forward();
};
backButton = new NSButton(new CoreGraphics.CGRect(0, 30, 40, 30));
backButton.Title = "<<";
backButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.Back();
};
startStopButton = new NSButton(new CoreGraphics.CGRect(0, 60, 40, 30));
startStopButton.Title = "Play";
startStopButton.Activated += (object sender, EventArgs e) => {
subtitlesProvider.StartStop(subtitlesProvider.Playing);
};
subtitlesPanel.ContentView.AddSubview(subtitleTextButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(subtitleTextField, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(forwardButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(backButton, NSWindowOrderingMode.Below, null);
subtitlesPanel.ContentView.AddSubview(startStopButton, NSWindowOrderingMode.Below, null);
SetupSubtitlesProvider();
}
Please kindly advice what else should I try to make it work.
I have found the solution here:
keyDown not being called
This is my implementation of NSPanelExt class to handle the keys.
public class NSPanelExt : NSPanel
{
public KeyPressedHandler KeyPressed;
public delegate void KeyPressedHandler(KeyCodeEventArgs e);
public NSPanelExt(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation) : base(contentRect, aStyle, bufferingType, deferCreation)
{
}
public override bool CanBecomeMainWindow => true;
public override bool CanBecomeKeyWindow => true;
public override bool AcceptsFirstResponder()
{
return true;
}
public override void KeyDown(NSEvent theEvent)
{
// this function is never called
KeyPressed?.Invoke(new KeyCodeEventArgs { Key = GetKeyCode(theEvent.KeyCode) });
}
private KeyCode GetKeyCode(ushort keyCode)
{
KeyCode result = KeyCode.Unknown;
switch (keyCode)
{
case 123:
result = KeyCode.Left;
break;
case 49:
result = KeyCode.Space;
break;
case 124:
result = KeyCode.Right;
break;
case 53:
result = KeyCode.Esc;
break;
}
return result;
}
I have also updated the ViewController to keep NSPanel always active.
public partial class ViewController : NSViewController
{
// ...
private NSButton startStopButton;
Timer _timer = new Timer();
private void SetupView()
{
// ...
subtitlesPanel.KeyPressed += SubtitlesPanel_KeyPressed;
// ...
IntializeKeepWindowFocusedTimer();
}
void SubtitlesPanel_KeyPressed(KeyCodeEventArgs e)
{
switch(e.Key)
{
case KeyCode.Left:
backButton.PerformClick(this);
break;
case KeyCode.Right:
forwardButton.PerformClick(this);
break;
case KeyCode.Space:
startStopButton.PerformClick(this);
break;
case KeyCode.Esc:
_timer.Stop();
break;
}
}
private void IntializeKeepWindowFocusedTimer()
{
_timer.Interval = 200; //in milliseconds
_timer.Elapsed += Timer_Elapsed;;
_timer.AutoReset = true;
_timer.Enabled = true;
}
void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
NSApplication.SharedApplication.BeginInvokeOnMainThread(() =>
{
subtitlesPanel.MakeKeyWindow();
if (SetSubtitleNeeded)
{
subtitlesProvider.SetSubTitle(0);
startStopButton.Title = "Stop";
SetSubtitleNeeded = false;
_timer.Interval = 5000;
}
});
}
private bool SetSubtitleNeeded = false;
partial void ClickedButton(NSObject sender)
{
_timer.Stop();
var nsUrl = subtitleFileSelector.GetFile();
if (nsUrl == null)
return;
fileName = nsUrl.Path;
subtitlesProvider.ReadFromFile(fileName);
SetSubtitleNeeded = true;
_timer.Start();
}
I am working on iOS Application in Xamarin.
timer1 = new System.Timers.Timer();
timer1.Interval = 1000;
//Play.TouchUpInside += (sender,e)=>
//{
timer1.Enabled = true;
Console.WriteLine("timer started");
timer1.Elapsed += new ElapsedEventHandler(OnTimeEvent);
//}
This is what i have written in viewdidload();
public void OnTimeEvent(object source, ElapsedEventArgs e)
{
count++;
Console.WriteLine("timer tick");
if (count == 30)
{
timer1.Enabled = false;
Console.WriteLine("timer finished");
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
StartTimer.Text = Convert.ToString(e.SignalTime.TimeOfDay); // this works!
});
})).Start();
}
else
{
//adjust the UI
new System.Threading.Thread(new System.Threading.ThreadStart(() =>
{
InvokeOnMainThread(() =>
{
StartTimer.Text = Convert.ToString(e.SignalTime.TimeOfDay); // this works!
});
})).Start();
timer1.Enabled = false;
Console.WriteLine("timer stopped");
}
}
This is the event I have called when I clicked on button play. I want this method to keep running so that time get updated on the label (starttimer.Text) in the UI. Like Runnable Interface we use in Android, what do we have to use in iOS to keep it running?
Use async - much cleaner (no marshalling to get you back on the main thread again!)
private int _duration = 0;
public async void StartTimer() {
_duration = 0;
// tick every second while game is in progress
while (_GameInProgress) {
await Task.Delay (1000);
_duration++;
string s = TimeSpan.FromSeconds(_duration).ToString(#"mm\:ss");
btnTime.SetTitle (s, UIControlState.Normal);
}
}
//before loading the view
public override void ViewWillAppear(bool animated)
{
...
StartTimer();
}
// when view is loaded
public override void ViewDidLoad()
{
base.ViewDidLoad();
....
UpdateDateTime();
}
private void UpdateDateTime()
{
var dateTime = DateTime.Now;
StartTimer.Text = dateTime.ToString("HH:mm:ss");
}
private void StartTimer()
{
var timer = new Timer(1000);
timer.Elapsed += (s, a) => InvokeOnMainThread(UpdateDateTime);
timer.Start();
}
I would have given some indication as to the actual issue in the title, but I can't figure out what it is. All I can state is that the form, when implementing the appbar, no longer responds after I attempt to assign a value to a variable, thereby causing me to have to stop debugging and restart the machine to regain the desktop working area. The location of the error is noted in the code below, and I have only listed the code to the point of the error. Is there anything blatantly obvious here that I do not see?
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public partial class Form1 : AppBar
{
public Form1() : base()
{
InitializeComponent();
}
//This is a button on the test form.
//When clicked, the form should become a desktop application bar
//docked to the top of the desktop.
private void t_Click(object sender, EventArgs e)
{
base.SET_APPBAR();
}
}
public class AppBar : Form
{
protected internal Size appbarSize;
protected internal Point appbarLocation;
protected internal bool appbarMode;
private EDGES appbarEdge;
private RECT appbarRect;
private uint ID { get; private set; }
private IntPtr HANDLE { get; private set; }
//constructor
public AppBar() : base () //no issues here
{
appbarEdge = EDGES.ABE_TOP;
appbarRect = new RECT();
ID = 0;
HANDLE = this.Handle;
appbarMode = false;
}
protected internal void SET_APPBAR()
{
if (IS_NEW())
{
QUERY_AND_SET_POSITION(ref appbarRect); //Never get to here
}
}
private bool IS_NEW()
{
if (appbarMode) //so far, so good
{
return false;
}
while (ID == 0)
{
CREATE_ID(); //ID is created, I have verified this.
}
this.FormBorderStyle = FormBorderStyle.FixedToolWindow; //BorderStyle does change
appbarSize = this.Size; //Size is correct
appbarLocation = this.Location; //still no issues
NEW(); //this is where the error begins (see code further down)
return appbarMode; //Never get here
}
private void CREATE_ID()
{
ID = Hooks.RegisterWindowMessage(Guid.NewGuid().ToString());
}
private void QUERY_AND_SET_POSITION(ref RECT appbarRect)
{
SET_APPBAR_SIZE(ref appbarRect);
QUERY_POS(ref appbarRect);
APPBAR_RESIZE(ref appbarRect);
SET_POS(ref appbarRect);
this.Location = new Point(appbarRect.left, appbarRect.top);
appbarSize = new Size(appbarRect.right - appbarRect.left, appbarRect.bottom - appbarRect.top);
this.Size = appbarSize;
}
private void SET_APPBAR_SIZE(ref RECT appbarRect)
{
switch (appbarEdge)
{
case (EDGES.ABE_BOTTOM):
appbarRect.left = 0;
appbarRect.right = SystemInformation.PrimaryMonitorSize.Width;
appbarRect.top = SystemInformation.PrimaryMonitorSize.Height - appbarSize.Height;
appbarRect.bottom = SystemInformation.PrimaryMonitorSize.Height;
break;
case (EDGES.ABE_LEFT):
appbarRect.left = 0;
appbarRect.right = appbarSize.Width;
appbarRect.top = 0;
appbarRect.bottom = SystemInformation.PrimaryMonitorSize.Height;
break;
case (EDGES.ABE_RIGHT):
appbarRect.left = SystemInformation.PrimaryMonitorSize.Width - appbarSize.Width;
appbarRect.right = SystemInformation.PrimaryMonitorSize.Width;
appbarRect.top = 0;
appbarRect.bottom = SystemInformation.PrimaryMonitorSize.Height;
break;
default:
appbarRect.left = 0;
appbarRect.right = SystemInformation.PrimaryMonitorSize.Width;
appbarRect.top = SystemInformation.WorkingArea.Top;
appbarRect.bottom = appbarSize.Height;
break;
}
}
private void APPBAR_RESIZE(ref RECT appbarRect)
{
switch (appbarEdge)
{
case (EDGES.ABE_TOP):
appbarRect.bottom = appbarRect.top + appbarSize.Height;
break;
case (EDGES.ABE_BOTTOM):
appbarRect.top = appbarRect.bottom - appbarSize.Height;
break;
case (EDGES.ABE_LEFT):
appbarRect.right = appbarRect.left + appbarSize.Width;
break;
case (EDGES.ABE_RIGHT):
appbarRect.left = appbarRect.right - appbarSize.Width;
break;
}
}
private void APPBAR_CALLBACK(ref Message apiMessage)
{
switch ((NOTIFICATIONS)(uint)apiMessage.WParam)//? on int vs uint here
{
case (NOTIFICATIONS.ABN_STATECHANGE):
STATE_CHANGE();
break;
case (NOTIFICATIONS.ABN_POSCHANGED):
QUERY_AND_SET_POSITION(ref appbarRect);
break;
case (NOTIFICATIONS.ABN_FULLSCREENAPP):
if ((int)apiMessage.LParam != 0)
{
this.SendToBack();
this.TopMost = false;
}
else
{
STATE_CHANGE();
}
break;
case (NOTIFICATIONS.ABN_WINDOWARRANGE):
//first call
if ((int)apiMessage.LParam != 0)
{
this.Visible = false;
}
//second call
else
{
this.Visible = true;
}
break;
}
}
protected override void WndProc(ref Message apiMessage)
{
if (appbarMode)
{
if (HANDLE == apiMessage.HWnd)
{
APPBAR_CALLBACK(ref apiMessage);
}
else if (apiMessage.Msg == (int)API_MESSAGES.WM_ACTIVATE)
{
ACTIVATE();
}
else if (apiMessage.Msg == (int)API_MESSAGES.WM_WINDOWPOSCHANGED)
{
WINDOW_POS_CHANGED();
}
}
base.WndProc(ref apiMessage);
}
private void NEW()
{
APPBARDATA data = new APPBARDATA(); //no apparent issue
data.cbSize = (uint)Marshal.SizeOf(data); //no apparent issue
data.hWnd = HANDLE; //no apparent issue
data.uCallbackMessage = ID; //no apparent issue
if (Hooks.SHAppBarMessage((uint)DWORD.ABM_NEW, ref data) != 0) //SHAppBar returns 1 (true)
{
//no issue if I were to place a MessageBox here
appbarMode = true; // why in the world is this causing an issue?????
//can't do anything from this point
}
}
}
public static class Hooks
{
[DllImport("shell32.dll", EntryPoint = "SHAppBarMessage")]
public static extern uint SHAppBarMessage(uint dwMessage, ref APPBARDATA pData);
[DllImport("user32.dll", EntryPoint = "RegisterWindowMessage")]
public static extern uint RegisterWindowMessage(
[MarshalAs(UnmanagedType.LPTStr)]
string lpString);
}
After whatever the issue is occurs, I can't click on any button or close the form. Desktop working area is always appropriately resized. Hopefully all of this makes sense to someone. Thanks for looking in advance.
The problem is that you're performing a long running operation in the UI thread. This is blocking the UI thread and preventing it from doing anything else (from painting changes, responding to button click or mouse move events, etc.).
You need to perform your long running operation(s) in a background thread. While you can do this manually, using a BackgroundWorker is preferable as it is designed for exactly this application. Not only will it create/configure the background thread for you, but it provides simple and easy to use mechanisms for updating the UI when your background task is completed, updating the UI with progress while the task is working, etc.
Note that while in a background thread you cannot access UI elements; they need to be accessed from the UI thread. You should move all code that gets information from the UI to before you start the background task (saving it for later in local variables or fields) and you should move all code to update the UI based on the results to the end, (in the RunWorkerCompleted event, if using a BackgroundWorker). All of the events other than DoWork for the BackgroundWorker are all executed in the UI thread, and so can access your UI Controls.