How can I make my notification windows more toast like? - c#

I am making a notification system for my Package Tracker app... I need to know how can I make this toast style more efficient than what I have now...
Things I'm looking to accomplish:
when multiple notifications are shown have them docked from top to
bottom on right of screen (looking for a better solution) <<< DONE!
when 1 notification closes moves all notifications below it up... <<< DONE!
Sticky Notifications Enabled when computer is idle. <<< DONE!
if a Notification Exists for a particular item it is replaced rather than
creating a new notification. <<< DONE!
To show a notification I use:
#region Check if Notification Exists
foreach (Form f in ActiveNotifications)
{
string[] windowID = f.Text.Split('|');
if (windowID[1] == NotificationTrackingNumber)
{
NotificationFound = true;
f.Text = "Description=" + NotificationDescription;
f.Text = "Status=" + NotificationStatus;
}
}
#endregion Check if Notification Exists
if (NotificationFound)
{
NotificationFound = false;
}
else if (!NotificationFound)
{
notification = new Notification(NotificationDescription, NotificationStatus, NotificationTrackingNumber, min, sec, WindowID);
notification.Closed += new EventHandler(Notification_Closed);
notification.Show();
ActiveNotifications.Add(notification);
WindowID++;
}
To position them up and down the right side of the screen I use:
Rectangle res = Screen.PrimaryScreen.Bounds;
this.Location = new Point(res.Width - 5 - Size.Width, (this.Height + 16) * multiplier + 6);
To re-position any Active Notifications that is below the closed notification
public void Notification_Closed(object sender, EventArgs e)
{
WindowID--;
List<Form> TempNotificationList1 = new List<Form>();
List<Form> TempNotificationList2 = new List<Form>();
Point newFormPoint = NotificationSettings.notificaionLocation;
Point oldFormPoint;
int delIndex = -1;
int newWindowID = 0;
int winHandleIndex = 0;
int ClosedNotificationWindowID = NotificationSettings.WindowMultiplier;
#region Deletes Closed Notification Window from ActivateNotifications
while (winHandleIndex < ActiveNotifications.Count)
{
if (ActiveNotifications[winHandleIndex].Handle == NotificationSettings.notificationClosedID)
{
delIndex = winHandleIndex;
break;
}
winHandleIndex++;
}
if (delIndex > -1)
{
ActiveNotifications.RemoveAt(delIndex);
}
#endregion Deletes Closed Notification Window from ActivateNotifications
#region Changes the Location Active Notifications
while (ClosedNotificationWindowID < ActiveNotifications.Count)
{
string[] windowID = ActiveNotifications[ClosedNotificationWindowID].Text.Split('|');
oldFormPoint = ActiveNotifications[ClosedNotificationWindowID].Location;
ActiveNotifications[ClosedNotificationWindowID].Location = newFormPoint;
newFormPoint = oldFormPoint;
TempNotificationList1.Add(ActiveNotifications[ClosedNotificationWindowID]);
ClosedNotificationWindowID++;
}
#endregion Changes the Location Active Notifications
#region Merges ActiveNotifications & TempNotificationList1
foreach (Form f in ActiveNotifications)
{
if (!TempNotificationList1.Contains(f))
{
TempNotificationList2.Add(f);
}
}
foreach (Form f in TempNotificationList1)
{
if (!TempNotificationList2.Contains(f))
{
TempNotificationList2.Add(f);
}
}
#endregion Merges ActiveNotifications & TempNotificationList1
#region Rebuilds Active Notifications List
ActiveNotifications.Clear();
foreach (Form f in TempNotificationList2)
{
string[] windowID = f.Text.Split('|');
f.Text = newWindowID.ToString() + "|" + windowID[1];
ActiveNotifications.Add(f);
newWindowID++;
}
TempNotificationList1 = null;
#endregion Rebuilds Active Notifications List
}
Well, the code works but is there a more efficient way? Multiplier is really just window count... here is a picture of how it's working as of now...

Related

c# windows forms application Pinning start location when button clicked

I have made a new windows forms application, and I'm trying to use a button to allow the user to save the application startup location when the button is clicked. I have tried to look the problem up, but I can't find anything about using a button to do it.
Here is what I have found so far which saves the location on the screen when the form is closed, I just don't know how to make it only save the location when a button is clicked:
public static void GeometryFromString(string thisWindowGeometry, Form formIn)
{
if (string.IsNullOrEmpty(thisWindowGeometry) == true)
{
return;
}
string[] numbers = thisWindowGeometry.Split('|');
string windowString = numbers[4];
if (windowString == "Normal")
{
Point windowPoint = new Point(int.Parse(numbers[0]),
int.Parse(numbers[1]));
Size windowSize = new Size(int.Parse(numbers[2]),
int.Parse(numbers[3]));
bool locOkay = GeometryIsBizarreLocation(windowPoint, windowSize);
bool sizeOkay = GeometryIsBizarreSize(windowSize);
if (locOkay == true && sizeOkay == true)
{
formIn.Location = windowPoint;
formIn.Size = windowSize;
formIn.StartPosition = FormStartPosition.Manual;
formIn.WindowState = FormWindowState.Normal;
}
else if (sizeOkay == true)
{
formIn.Size = windowSize;
}
}
else if (windowString == "Maximized")
{
formIn.Location = new Point(100, 100);
formIn.StartPosition = FormStartPosition.Manual;
formIn.WindowState = FormWindowState.Maximized;
}
}
private static bool GeometryIsBizarreLocation(Point loc, Size size)
{
bool locOkay;
if (loc.X < 0 || loc.Y < 0)
{
locOkay = false;
}
else if (loc.X + size.Width > Screen.PrimaryScreen.WorkingArea.Width)
{
locOkay = false;
}
else if (loc.Y + size.Height > Screen.PrimaryScreen.WorkingArea.Height)
{
locOkay = false;
}
else
{
locOkay = true;
}
return locOkay;
}
private static bool GeometryIsBizarreSize(Size size)
{
return (size.Height <= Screen.PrimaryScreen.WorkingArea.Height &&
size.Width <= Screen.PrimaryScreen.WorkingArea.Width);
}
public static string GeometryToString(Form mainForm)
{
return mainForm.Location.X.ToString() + "|" +
mainForm.Location.Y.ToString() + "|" +
mainForm.Size.Width.ToString() + "|" +
mainForm.Size.Height.ToString() + "|" +
mainForm.WindowState.ToString();
}
Here's one of many ways to implement a button to save the Location (and optionally Size).
First, Create a Settings resource if one doesn't already exist. Right-click on the Project and select Properties.
Choose the Settings tab and click the link to create the resource.
Make entries for Size and Location:
Add a Click handler for your Save button:
public MainForm()
{
InitializeComponent();
buttonSaveSizeAndPosition.Click += saveSizeAndPosition;
}
private async void saveSizeAndPosition(object sender, EventArgs e)
{
Properties.Settings.Default.Location = Location;
Properties.Settings.Default.Size = Size;
Properties.Settings.Default.Save();
var textB4 = Text;
Text = $"Location = {Location} Size = {Size}";
await Task.Delay(1000);
Text = textB4;
}
Then, when you load the main form check to see whether the Size has moved off of the default value before reloading these properties:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Properties.Settings.Default.Size.Equals(new Size()))
{
Location = Properties.Settings.Default.Location;
Size = Properties.Settings.Default.Size;
}
}

C# - show child control (Listbox) on top of parent control(textbox) in winform

Created a custom intellisense textbox (textbox with listbox a child).
As shown in below image, the listbox pops up when i enter a char which all works fine and good but when i am at the end of textbox the listbox is partially visible, is there anyway i can show the whole listbox content?
Tried this "Show control inside user control outside the boundaries of its parent
But when the popup window opens the text box looses focus and i cannot type anything further, my intellisense textbox keeps giving better results based on what they type but in this situation i am not able to type anymore.
FYI tried to add pParentControl.Focus() into show method defined in other article as shown below, missing something?
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
Here is the complete code
class TextBox_AutoComplete : TextBox
{
#region Class Members
List<string> dictionary;
ListBox listbox = new ListBox();
#endregion
private PopupHelper m_popup;
#region Extern functions
[DllImport("user32")]
private extern static int GetCaretPos(out Point p);
#endregion
#region Constructors
public TextBox_AutoComplete() : base()
{
this.Margin = new Padding(0, 0, 0, 0);
this.Multiline = true;
this.Dock = DockStyle.Fill;
this.KeyDown += Textbox_KeyDown;
this.KeyUp += Textbox_KeyUp;
listbox.Parent = this;
listbox.KeyUp += List_OnKeyUp;
listbox.Visible = false;
this.dictionary = new List<string>();
}
#endregion
#region Properties
public List<string> Dictionary
{
get { return this.dictionary; }
set { this.dictionary = value; }
}
#endregion
#region Methods
private static string GetLastString(string s)
{
Regex rgx = new Regex("[^a-zA-Z0-9_.\\[\\]]");
s = rgx.Replace(s, " ");
string[] strArray = s.Split(' ');
return strArray[strArray.Length - 1];
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
Point cp;
GetCaretPos(out cp);
List<string> lstTemp = new List<string>();
List<string> TempFilteredList = new List<string>();
string LastString = GetLastString(this.Text.Substring(0, SelectionStart));
//MessageBox.Show(LastString);
/*seperated them so that column name matches are found first*/
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().Substring(n.IndexOf(".") > 0 ? n.IndexOf(".") : 0).StartsWith(LastString.ToUpper())
).Select(r => r)
.ToList());
TempFilteredList.AddRange(dictionary.Where(n => n.Replace("[", "").ToUpper().StartsWith(LastString.ToUpper())
|| n.ToUpper().StartsWith(LastString.ToUpper()))
.Select(r => r)
.ToList());
lstTemp = TempFilteredList.Distinct().Select(r => r).ToList();
/*Getting max width*/
int maxWidth = 0, temp = 0;
foreach (var obj in lstTemp)
{
temp = TextRenderer.MeasureText(obj.ToString(), new Font("Arial", 10, FontStyle.Regular)).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
if (lstTemp.Count != 0 && LastString != "")
{
listbox.DataSource = lstTemp;
// listbox.Show();
if (m_popup == null)
m_popup = new PopupHelper(listbox);
m_popup.Show(this);
}
else if (m_popup != null)
{
//listbox.Hide();
m_popup.Hide();
}
}
protected void Textbox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Down)
{
if (listbox.Visible == true)
{
listbox.Focus();
}
e.Handled = true;
}
else if (e.KeyCode == Keys.Escape)
{
listbox.Visible = false;
e.Handled = true;
}
}
protected void Textbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space && listbox.Visible == true)
{
listbox.Focus();
List_OnKeyUp(listbox, new KeyEventArgs(Keys.Space));
e.Handled = true;
}
if (e.KeyCode == Keys.Down && listbox.Visible == true)
{
listbox.Focus();
e.Handled = true;
}
}
private void List_OnKeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)
{
int Selection_Start = this.SelectionStart;
string StrLS = GetLastString(this.Text.Substring(0, Selection_Start));
this.Select(Selection_Start - StrLS.Length, StrLS.Length);
// MessageBox.Show(this.Selection_Start.ToString() + " Last string" + StrLS);
this.SelectedText=((ListBox)sender).SelectedItem.ToString();
listbox.Hide();
this.Focus();
}
}
#endregion
}
public sealed class PopupHelper : IDisposable
{
private readonly Control m_control;
private readonly ToolStripDropDown m_tsdd;
private readonly Panel m_hostPanel; // workarround - some controls don't display correctly if they are hosted directly in ToolStripControlHost
public PopupHelper(Control pControl)
{
m_hostPanel = new Panel();
m_hostPanel.Padding = Padding.Empty;
m_hostPanel.Margin = Padding.Empty;
m_hostPanel.TabStop = false;
m_hostPanel.BorderStyle = BorderStyle.None;
m_hostPanel.BackColor = Color.Transparent;
m_tsdd = new ToolStripDropDown();
m_tsdd.CausesValidation = false;
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.Opacity = 0.9;
m_control = pControl;
m_control.CausesValidation = false;
m_control.Resize += MControlResize;
//m_hostPanel.Controls.Add(m_control);
m_tsdd.Padding = Padding.Empty;
m_tsdd.Margin = Padding.Empty;
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = pControl.Size;
m_tsdd.Items.Add(new ToolStripControlHost(m_control));
}
private void ResizeWindow()
{
m_tsdd.MinimumSize = m_tsdd.MaximumSize = m_tsdd.Size = m_control.Size;
m_hostPanel.MinimumSize = m_hostPanel.MaximumSize = m_hostPanel.Size = m_control.Size;
}
private void MControlResize(object sender, EventArgs e)
{
ResizeWindow();
}
/// <summary>
/// Display the popup and keep the focus
/// </summary>
/// <param name="pParentControl"></param>
public void Show(Control pParentControl)
{
if (pParentControl == null) return;
// position the popup window
var loc = pParentControl.PointToScreen(new Point(0, pParentControl.Height));
pParentControl.Focus();
m_tsdd.Show(loc);
}
public void Hide()
{
m_tsdd.Hide();
}
public void Close()
{
m_tsdd.Close();
}
public void Dispose()
{
m_control.Resize -= MControlResize;
m_tsdd.Dispose();
m_hostPanel.Dispose();
}
}
Firstly, I personally don't see any benefit in having a control inside another. Yes, the child control is locked inside its parent's boundaries automatically for you, but this benefit is negated by the issue that you're facing, and solving that issue requires the same work as when the two controls have no relation. In both cases, you'll have to do the calculations manually to keep the child visible inside its parent. In the second case the parent is the app's window.
Secondly, I don't recommend using hacks like the one mentioned in the comments to show the child outside its parent's boundaries. The hack creates more issues than it solves, as you found out. And what's the point of that hack anyway? If you want to show the child outside the parent, then don't make it a child control in the first place, and you don't need any hack.
The best solution is the one that you find in any well designed app, and in Windows itself. Open any app, let's say Notepad, and right-click near the upper-left corner. You'll see the context menu pulling to lower-right direction. Now right-click near the other three corners and you'll see the context menu pulling in different direction each time, so it will always be visible inside the app. Now if you resize the app window too small and right-click, the context menu will choose the best direction but some of it will be outside the app because the window is too small. That's why you need your list not to be a child, but it's up to you, and it's only about these edge cases. The solution will be similar in both cases.
You're displaying the list in this line:
listbox.SetBounds(cp.X + 20, cp.Y + 20, maxWidth, 60);
The key is cp.X and cp.Y. This is what decides where the list will appear. You need to make this point dynamic and responsive to the boundaries of the parent. You fixed the width to maxWidth and height to 60, so I will use those values in the calculation.
To make sure the list will not go beyond the bottom:
var y = this.Height < cp.Y + 60 ? this.Height - 60 : cp.Y;
To make sure the list will not go beyond the right:
var x = this.Width < cp.X + maxWidth ? this.Width - maxWidth : cp.X;
Now you can show your list at the calculated point:
listbox.SetBounds(x, y, maxWidth, 60);
Notes:
I didn't include the 20 gap that you used. I think it looks better without the gap and I haven't seen any app that has a gap. If you prefer the gap, add it to the calculation of x and y. Don't add it in the SetBounds() or that will screw up the calculation.
The calculation above doesn't take into account when the parent size is too small to show the child inside. If you want to support that edge case, you need to make the child a separate control and add some checks to the calculation.

In WPF, how to shift a window onto the screen if it is off the screen?

If I have a window, how can I ensure that the window will never be hidden off the screen?
This is important, because sometimes if the user adds or removes a monitor, the window may be permanently hidden off the screen if we have remembered the previous position.
I am using WPF + MVVM.
This answer has been tested in a large scale real world application.
Call this from any attached property to shift the window back onto the visible screen:
public static class ShiftWindowOntoScreenHelper
{
/// <summary>
/// Intent:
/// - Shift the window onto the visible screen.
/// - Shift the window away from overlapping the task bar.
/// </summary>
public static void ShiftWindowOntoScreen(Window window)
{
// Note that "window.BringIntoView()" does not work.
if (window.Top < SystemParameters.VirtualScreenTop)
{
window.Top = SystemParameters.VirtualScreenTop;
}
if (window.Left < SystemParameters.VirtualScreenLeft)
{
window.Left = SystemParameters.VirtualScreenLeft;
}
if (window.Left + window.Width > SystemParameters.VirtualScreenLeft + SystemParameters.VirtualScreenWidth)
{
window.Left = SystemParameters.VirtualScreenWidth + SystemParameters.VirtualScreenLeft - window.Width;
}
if (window.Top + window.Height > SystemParameters.VirtualScreenTop + SystemParameters.VirtualScreenHeight)
{
window.Top = SystemParameters.VirtualScreenHeight + SystemParameters.VirtualScreenTop - window.Height;
}
// Shift window away from taskbar.
{
var taskBarLocation = GetTaskBarLocationPerScreen();
// If taskbar is set to "auto-hide", then this list will be empty, and we will do nothing.
foreach (var taskBar in taskBarLocation)
{
Rectangle windowRect = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
// Keep on shifting the window out of the way.
int avoidInfiniteLoopCounter = 25;
while (windowRect.IntersectsWith(taskBar))
{
avoidInfiniteLoopCounter--;
if (avoidInfiniteLoopCounter == 0)
{
break;
}
// Our window is covering the task bar. Shift it away.
var intersection = Rectangle.Intersect(taskBar, windowRect);
if (intersection.Width < window.Width
// This next one is a rare corner case. Handles situation where taskbar is big enough to
// completely contain the status window.
|| taskBar.Contains(windowRect))
{
if (taskBar.Left == 0)
{
// Task bar is on the left. Push away to the right.
window.Left = window.Left + intersection.Width;
}
else
{
// Task bar is on the right. Push away to the left.
window.Left = window.Left - intersection.Width;
}
}
if (intersection.Height < window.Height
// This next one is a rare corner case. Handles situation where taskbar is big enough to
// completely contain the status window.
|| taskBar.Contains(windowRect))
{
if (taskBar.Top == 0)
{
// Task bar is on the top. Push down.
window.Top = window.Top + intersection.Height;
}
else
{
// Task bar is on the bottom. Push up.
window.Top = window.Top - intersection.Height;
}
}
windowRect = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
}
}
}
}
/// <summary>
/// Returned location of taskbar on a per-screen basis, as a rectangle. See:
/// https://stackoverflow.com/questions/1264406/how-do-i-get-the-taskbars-position-and-size/36285367#36285367.
/// </summary>
/// <returns>A list of taskbar locations. If this list is empty, then the taskbar is set to "Auto Hide".</returns>
private static List<Rectangle> GetTaskBarLocationPerScreen()
{
List<Rectangle> dockedRects = new List<Rectangle>();
foreach (var screen in Screen.AllScreens)
{
if (screen.Bounds.Equals(screen.WorkingArea) == true)
{
// No taskbar on this screen.
continue;
}
Rectangle rect = new Rectangle();
var leftDockedWidth = Math.Abs((Math.Abs(screen.Bounds.Left) - Math.Abs(screen.WorkingArea.Left)));
var topDockedHeight = Math.Abs((Math.Abs(screen.Bounds.Top) - Math.Abs(screen.WorkingArea.Top)));
var rightDockedWidth = ((screen.Bounds.Width - leftDockedWidth) - screen.WorkingArea.Width);
var bottomDockedHeight = ((screen.Bounds.Height - topDockedHeight) - screen.WorkingArea.Height);
if ((leftDockedWidth > 0))
{
rect.X = screen.Bounds.Left;
rect.Y = screen.Bounds.Top;
rect.Width = leftDockedWidth;
rect.Height = screen.Bounds.Height;
}
else if ((rightDockedWidth > 0))
{
rect.X = screen.WorkingArea.Right;
rect.Y = screen.Bounds.Top;
rect.Width = rightDockedWidth;
rect.Height = screen.Bounds.Height;
}
else if ((topDockedHeight > 0))
{
rect.X = screen.WorkingArea.Left;
rect.Y = screen.Bounds.Top;
rect.Width = screen.WorkingArea.Width;
rect.Height = topDockedHeight;
}
else if ((bottomDockedHeight > 0))
{
rect.X = screen.WorkingArea.Left;
rect.Y = screen.WorkingArea.Bottom;
rect.Width = screen.WorkingArea.Width;
rect.Height = bottomDockedHeight;
}
else
{
// Nothing found!
}
dockedRects.Add(rect);
}
if (dockedRects.Count == 0)
{
// Taskbar is set to "Auto-Hide".
}
return dockedRects;
}
}
As a bonus, you can implement your own drag'n'drop, and when the drag finishes, the window will be shifted back onto the screen.
It would be more intuitive from the users point of view if the window slid quickly back into the visible area rather than just skipping back into the visible area, but at least this method gets the right results.
/// <summary>
/// Intent: Add this Attached Property to any XAML element, to allow you to click and drag the entire window.
/// Essentially, it searches up the visual tree to find the first parent window, then calls ".DragMove()" on it. Once the drag finishes, it pushes
/// the window back onto the screen if part or all of it wasn't visible.
/// </summary>
public class EnableDragAttachedProperty
{
public static readonly DependencyProperty EnableDragProperty = DependencyProperty.RegisterAttached(
"EnableDrag",
typeof(bool),
typeof(EnableDragAttachedProperty),
new PropertyMetadata(default(bool), OnLoaded));
private static void OnLoaded(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
try
{
var uiElement = dependencyObject as UIElement;
if (uiElement == null || (dependencyPropertyChangedEventArgs.NewValue is bool) == false)
{
return;
}
if ((bool)dependencyPropertyChangedEventArgs.NewValue == true)
{
uiElement.MouseMove += UIElement_OnMouseMove;
}
else
{
uiElement.MouseMove -= UIElement_OnMouseMove;
}
}
catch (Exception ex)
{
// Log exception here.
}
}
/// <summary>
/// Intent: Fetches the parent window, so we can call "DragMove()"on it. Caches the results in a dictionary,
/// so we can apply this same property to multiple XAML elements.
/// </summary>
private static void UIElement_OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
try
{
var uiElement = sender as UIElement;
if (uiElement != null)
{
Window window = GetParentWindow(uiElement);
if (mouseEventArgs.LeftButton == MouseButtonState.Pressed)
{
// DragMove is a synchronous call: once it completes, the drag is finished and the left mouse
// button has been released.
window?.DragMove();
// See answer in section 'Additional Links' below in the SO answer.
//HideAndShowWindowHelper.ShiftWindowIntoForeground(window);
// When the use has finished the drag and released the mouse button, we shift the window back
// onto the screen, it it ended up partially off the screen.
ShiftWindowOntoScreenHelper.ShiftWindowOntoScreen(window);
}
}
}
catch (Exception ex)
{
_log.Warn($"Exception in {nameof(UIElement_OnMouseMove)}. " +
$"This means that we cannot shift and drag the Toast Notification window. " +
$"To fix, correct C# code.", ex);
}
}
public static void SetEnableDrag(DependencyObject element, bool value)
{
element.SetValue(EnableDragProperty, value);
}
public static bool GetEnableDrag(DependencyObject element)
{
return (bool)element.GetValue(EnableDragProperty);
}
#region GetParentWindow
private static readonly Dictionary<UIElement, Window> _parentWindow = new Dictionary<UIElement, Window>();
private static readonly object _parentWindowLock = new object();
/// <summary>
/// Intent: Given any UIElement, searches up the visual tree to find the parent Window.
/// </summary>
private static Window GetParentWindow(UIElement uiElement)
{
bool ifAlreadyFound;
lock (_parentWindowLock)
{
ifAlreadyFound = _parentWindow.ContainsKey(uiElement) == true;
}
if (ifAlreadyFound == false)
{
DependencyObject parent = uiElement;
int avoidInfiniteLoop = 0;
// Search up the visual tree to find the first parent window.
while ((parent is Window) == false)
{
parent = VisualTreeHelper.GetParent(parent);
avoidInfiniteLoop++;
if (avoidInfiniteLoop == 1000)
{
// Something is wrong - we could not find the parent window.
return null;
}
}
lock (_parentWindowLock)
{
_parentWindow[uiElement] = parent as Window;
}
}
lock(_parentWindowLock)
{
return _parentWindow[uiElement];
}
}
#endregion
}
Additional links
For tips on how to avoid having a notification window hidden by other windows, see my answer at: Bring a window to the front in WPF.
The subject is old, but if anyone is looking for another solution, I did this to display a window on a click at the cursor position and ensure the window is always visible according to screen (working area) boudaries :
public static void PositionWindowOnScreen(Window window)
{
Screen activeScreen = Screen.FromPoint(Control.MousePosition);
double xPositionToSet = Control.MousePosition.X;
double yPositionToSet = Control.MousePosition.Y;
if (xPositionToSet + window.Width > activeScreen.WorkingArea.Width + activeScreen.WorkingArea.X &&
xPositionToSet - activeScreen.WorkingArea.X - window.Width > 0)
{
xPositionToSet = xPositionToSet - window.Width;
}
if (yPositionToSet + window.Height > activeScreen.WorkingArea.Height + activeScreen.WorkingArea.Y &&
yPositionToSet - activeScreen.WorkingArea.Y - window.Height > 0)
{
yPositionToSet = yPositionToSet - window.Height;
}
window.WindowStartupLocation = WindowStartupLocation.Manual;
window.Left = xPositionToSet;
window.Top = yPositionToSet;
}

MediaElement not always playing video in WPF application

we created a test application were users interact with it using gestures. Each user gets to interact with all gestures in a sequential manner. When the users first is introduced to a new gesture, a small instructional movie is played for the user showing him how to perform that gesture. The movie should first play on the main screen, and then after playing once should loop on the second monitor until the next gesture starts, when it should then close the secondary video screen and start the process over. This is where we are running into problems.
The movies all get loaded up fine, and actually most of the time play correctly. The problem is that sometimes, the movie does not actually start in the secondary monitor. They load up fine but never actually start. This is not something we can reliably reproduce, this happens sometimes at random.
This is the code that we have for the VideoPlayer window at the moment:
public partial class VideoWindow : Window {
static VideoWindow currentVideoWindow;
public VideoWindow(GestureDirection direction, GestureType type, bool reopen = false)
{
if(currentVideoWindow != null) {
currentVideoWindow.CloseWindow();
currentVideoWindow = this;
}
else {
currentVideoWindow = this;
}
GestureParser.Pause(!reopen);
InitializeComponent();
this.Title = type + " " + direction;
this.Show();
videoMediaElement.LoadedBehavior = MediaState.Manual;
videoMediaElement.UnloadedBehavior = MediaState.Manual;
string videoDirectory = #"techniques/";
string video = direction.ToString() + "_" + type.ToString() + ".mp4";
if (Screen.AllScreens.Length > 1) {
int secScreen = Screen.AllScreens.Length == 2 ? 0 : 2;
int mainScreen = Screen.AllScreens.Length == 2 ? 1 : 0;
Screen s = reopen ? Screen.AllScreens[secScreen] : Screen.AllScreens[mainScreen];
System.Drawing.Rectangle r = s.WorkingArea;
this.Top = r.Top;
this.Left = r.Left;
this.Topmost = true;
this.Show();
this.WindowStyle = WindowStyle.None;
this.WindowState = WindowState.Maximized;
}
else {
Screen s = Screen.AllScreens[0];
System.Drawing.Rectangle r = s.WorkingArea;
this.Top = r.Top;
this.Left = r.Left;
this.Show();
}
String videoPath = CreateAbsolutePathTo(videoDirectory + video);
if (File.Exists(videoPath)) {
Uri videoUri = new Uri(videoPath, UriKind.Relative);
videoMediaElement.Source = videoUri;
}
if (reopen) {
this.Activate();
canvasWindow.Activate();
videoMediaElement.MediaEnded += (sender, args) => {
videoMediaElement.Stop();
videoMediaElement.Position = TimeSpan.Zero;
};
} else {
videoMediaElement.MediaEnded += (sender, args) => {
this.CloseWindow();
var t = new VideoWindow(direction, type, true);
};
}
videoMediaElement.Play();
Task task = Task.Factory.StartNew(() =>
{
while(GetMediaState(videoMediaElement) != MediaState.Play)
{
videoMediaElement.Play();
}
});
}
private MediaState GetMediaState(MediaElement myMedia)
{
FieldInfo hlp = typeof(MediaElement).GetField("_helper", BindingFlags.NonPublic | BindingFlags.Instance);
object helperObject = hlp.GetValue(myMedia);
FieldInfo stateField = helperObject.GetType().GetField("_currentState", BindingFlags.NonPublic | BindingFlags.Instance);
MediaState state = (MediaState)stateField.GetValue(helperObject);
return state;
}
static CanvasWindow canvasWindow;
public static void SetCanvasWindow(CanvasWindow window) {
canvasWindow = window;
}
private static string CreateAbsolutePathTo(string mediaFile) {
return Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, mediaFile);
}
private void CloseWindow() {
videoMediaElement.Stop();
videoMediaElement.Close();
this.Close();
}
}
As you can see, we have tried creating a task that constantly checks to see if the video is playing, and if not it should play it. The thing is, the media state is always playing, even if the movie is stopped, and as such it does nothing.
We really don't know what is wrong, and would really appreciate some help.

How to smoothly navigate to a different panorama item

In a panorama application, I have added a couple of situations where the user is navigated back to a certain panorama item. However this is done in one sudden movement. Is there a way I can do this action more smoothly, with some form of transition? Or something of the like?
This code worked for me
SlideTransition slideTransition = new SlideTransition();
slideTransition.Mode = SlideTransitionMode.SlideRightFadeIn;
ITransition transition = slideTransition.GetTransition(panorama_main);
transition.Completed += delegate
{
transition.Stop();
};
PanoramaItem pItem = (PanoramaItem)panorama_main.Items[3];
panorama_main.DefaultItem = pItem;
transition.Begin();
From Inder Kumar Rathore's answer and answers in this page I've come to do this:
#region navigation
public enum MainPanoramaItem
{
None = -1, Mag, Scan, Account, Lists, More, Help, MainPanoramaItemCount
}
public void PanoramaNavigateTo(MainPanoramaItemitem)
{
int count = (int)MainPanoramaItem.MainPanoramaItemCount;
int toPosition = (int)item;
int nowPosition = panorama.SelectedIndex;
bool left = false;
if(nowPosition > toPosition){
int rightDelta = count - nowPosition + toPosition;
int leftDelta = nowPosition - toPosition;
left = rightDelta > leftDelta;
} else {
int leftDelta = count - nowPosition + toPosition;
int rightDelta = nowPosition - toPosition;
left = rightDelta > leftDelta;
}
SlideTransition slideTransition = new SlideTransition();
slideTransition.Mode = left ? SlideTransitionMode.SlideLeftFadeOut : SlideTransitionMode.SlideRightFadeOut;
ITransition transition = slideTransition.GetTransition(panorama);
transition.Completed += delegate
{
transition.Stop();
SlideTransition slideTransitionIn = new SlideTransition();
slideTransitionIn.Mode = left ? SlideTransitionMode.SlideLeftFadeIn : SlideTransitionMode.SlideRightFadeIn;
ITransition transitionIn = slideTransitionIn.GetTransition(panorama);
transitionIn.Completed += delegate { transitionIn.Stop(); };
panorama.SetValue(Panorama.SelectedItemProperty, panorama.Items[(int)item]);
Panorama temp = panorama;
LayoutRoot.Children.Remove(panorama);
LayoutRoot.Children.Add(temp);
LayoutRoot.UpdateLayout();
transitionIn.Begin();
};
transition.Begin();
}
protected override void OnBackKeyPress(CancelEventArgs e)
{
if (panorama.DefaultItem != panorama.Items[(int)PanoramaItem.Mag])
{
PanoramaNavigateTo(PanoramaItem.Mag);
e.Cancel = true;
}
}
#endregion
in MainPage.xaml.cs
It uses fade out and then fade in AND keeps the title of the panorama at it's original place.
I'm not sure yet how it reacts with a real application since mine is quite empty.

Categories

Resources